Erfolgreiche Planung für GPU-Cluster: Warteschlangenentwurf, Ausschließung und Gerechtigkeit bei lokalen Modelllasten
Erfolgreiche Planung von GPU-Clustern konzentriert sich darauf, die richtigen Arbeitslasten zur richtigen Zeit auszuführen. Basierend auf einem Beitrag von AllenAI auf Hugging Face untersucht dieser Artikel das Queue-Design, die Gruppenplanung, die Vorbehaltsfunktion sowie die Gerechtigkeit bei der lokalen Modelltrainierung und -inferenz und zeigt auf, warum allein Nutzungsmetriken selten echte Leistungssteigerungen erfassen.
Kurze Zusammenfassung
Erfolgreiche Planung von GPU-Clustern konzentriert sich darauf, die richtigen Arbeitslasten zur richtigen Zeit auszuführen. Basierend auf einem Beitrag von AllenAI auf Hugging Face untersucht dieser Artikel das Queue-Design, die Gruppenplanung, die Vorbehaltsfunktion sowie die Gerechtigkeit bei der lokalen Modelltrainierung und -inferenz und zeigt auf, warum allein Nutzungsmetriken selten echte Leistungssteigerungen erfassen.
Effektives Scheduling für GPU-Cluster: Queue-Design, Preemption und Fairness bei lokalen Modellarbeiten
Die Durchsatzleistung eines GPU-Clusters wird zum Zeitpunkt der Aufnahme entschieden, nicht zum Zeitpunkt des Kernels. Ein perfekt optimierter Attention-Kernel bringt ein paar Prozent Vorteil; eine Queue, die den falschen Job zum falschen Zeitpunkt aufnimmt, kostet einen ganzen Node acht Stunden lang. Dennoch ist der Scheduler in den meisten selbst gehosteten KI-Umgebungen das am wenigsten ausgereifte Element: eine Standardqueue, eine First-Come-First-Served-Strategie und die Hoffnung, dass die Nachfrage unter der Kapazität bleibt.
Dieser Artikel ist eine praktische Zusammenfassung des Gestaltungsspielraums bezüglich Queue-Design, Preemption und Fairness für lokale Modellarbeiten – Fine-Tuning-Vorgänge, Batch-Inferenz, Bewertungssequenzen sowie interaktive Notebooks, die dieselben physischen GPUs nutzen. Der Beitrag des Allen Institute for AI auf dem Hugging Face-Blog, Impactful scheduling for GPU clusters (Link), betrachtet das Scheduling als Hebel, um mehr nützliche Arbeit aus der bereits vorhandenen Kapazität zu gewinnen. Dieser Ansatz dient hier als Ausgangspunkt; die konkreten Muster darunter sind allgemeine Betriebspraktiken, keine Aussagen aus diesem Beitrag.
Überprüfter Kontext und Interpretation
Bevor es um die Mechanismen geht, eine Hinweis zu den Grenzen. Der überprüfte Hintergrund für diesen Artikel stammt aus einer einzigen Primärquelle: dem oben verlinkten Blogbeitrag von Hugging Face. Es handelt sich dabei um eine allgemeine Darstellung des Schedulings bei GPU-Clustern statt um eine Spezifikation, weshalb nichts davon als Funktionsliste, Benchmark oder Konfigurationsreferenz aufgefasst werden sollte. Wenn ich Kubernetes-Objekte, SLURM-Parameter oder Python-Muster beschreibe, handelt es sich dabei um weit verbreitete, eigenständig dokumentierte Tools. Betrachten Sie die Befehle als beispielhafte Strukturen, die an Ihre eigene Umgebung sowie an die Dokumentation der installierten Version angepasst werden müssen.
Warum lokale Modellarbeiten naive Queues zerstören
Allgemeine Batch-Schedulers wurden unter der einfachen Annahme entwickelt, dass Aufgaben grob austauschbar sind und ihre Ausführung in Reihenfolge des Eintreffens annähernd optimal ist. Lokale Modellarbeiten verletzen diese Annahme auf drei Arten.
Arbeiten weisen unterschiedliche Strukturen auf. Eine Feinabstimmung mit 7B LoRA könnte eine GPU für vierzig Minuten benötigen. Eine vollständige Feinabstimmung eines größeren Modells kann acht GPUs auf einem Knoten für einen Tag erfordern, wobei alle acht gleichzeitig starten oder gar nicht starten. Eine Batch-Inferenz kann über alle frei verfügbaren GPUs für drei Minuten verteilt werden. Eine einzige FIFO-Warteschlange, die alle drei Arten bedient, führt in der Regel zu einem Head-of-Line-Blockieren.
Arbeiten weisen unterschiedliche Elastizität auf. Die Inferenzleistung skaliert angemessen mit der Anzahl der GPUs; das verteilte Training hingegen nicht. Die Ausführung halb eines Trainingsjobs ist schlimmer als gar keine Ausführung, da teilweise eingesetzte Worker Speicher und Netzwerkbandbreite belegen, ohne Fortschritte zu erzielen.
Die Nachfrage ist unregelmäßig und korreliert. Forschungsgruppen neigen dazu, in Wellen Anfragen einzureichen – ein Fristdatum, ein Paper, eine Bewertungskampagne. Ein Cluster, der auf eine durchschnittliche Last ausgelegt ist, bleibt die Hälfte der Zeit sichtbar untätig und in der anderen Hälfte unbrauchbar. Die Warteschlangentheorie löst das nicht; eine Zulassungspolitik schon.
Anforderungen
Bevor Sie das Scheduling-Verhalten ändern, überprüfen Sie die Grundlage. Die untenstehende Liste ist weitgehend allgemein genug, um sowohl Kubernetes-basierte als auch SLURM-basierte Cluster abzudecken.
- GPU-Node mit Sichtbarkeit des laufenden Geräts. Auf Kubernetes muss das NVIDIA-Device-Plugin oder der GPU Operator laufen und
nvidia.com/gpu(oder ein äquivalenter MIG- oder zeitgestufter Ressourcennamen) anzeigen. Auf SLURM mussGresTypes=gpukonfiguriert sein und--gres=gpu:Nmuss richtig zuweisen.
- Eine Warteschlangen- oder Zulassungsschicht. Auf Kubernetes sind dies in der Regel Kueue, Volcano oder ein benutzerdefinierter Admission-Webhook. Auf SLURM bestehen diese aus Partitionen sowie QOS statt einer separaten Komponente.
- Toxisches Checkpoint-Speicherungssystem. Ein Ausschalten ohne Checkpointing verwandelt eine Scheduling-Strategie in eine Strategie mit Datenverlust. NFS, ein CSI-basiertes Volume oder ein in den Trainingscontainer eingebundenes Objektspeichersystem funktionieren alle; die Voraussetzung ist, dass Schreibvorgänge atomar sind und schnell genug erfolgen, um die Schrittzeit nicht zu dominieren.
- Metriken. Warteschlangentiefe, Zulassungsverzögerung, Anzahl der Ausschaltungen sowie die pro Namespace verbrauchten GPU-Sekunden. Ohne diese ist Fairness eine Frage des Ermessens.
- Code für fortsetzbare Arbeitslasten. Ihr Trainingsskript muss
SIGTERMhandhaben und den Zustand persistieren. Dies ist eine Codeanforderung, keine Infrastrukturanforderung, und es handelt sich um den am häufigsten übersprungenen Schritt.
Design der Warteschlange
Das Design der Warteschlange beantwortet eine einzige Frage: Wenn eine Aufgabe eintrifft und die Kapazität knapp ist, wer darf ausgeführt werden und in welcher Reihenfolge?
Die Hierarchie der Warteschlange dem Organigramm anpassen
Eine flache Warteschlange zwingt dazu, jede Entscheidung allein anhand der Prioritätsnummer zu treffen, was sich nicht feinabstimmen lässt. Eine zweistufige Hierarchie – ein Ressourcenpool auf Cluster-Ebene und Warteschlangen auf Namespace-Ebene – ist leichter nachvollziehbar und leichter zu überprüfen. Jedes Team erhält eine Warteschlange mit einer nominellen Quote; die Cluster-Warteschlange aggregiert diese Quoten und legt fest, was passiert, wenn ein Team seine Quote überschreitet.
Die Zulassungseinheit vor dem Prioritätsschema festlegen
Wenn Ihre Warteschlange Pods zulässt, kann eine verteilter Trainingsjob teilweise geschedult werden. Wenn sie ganze Jobs zulässt, ist das nicht möglich. Eine gruppenbasierte Zulassung – Ein-oder-alles-Platzierung – ist für synchrones Training zwingend erforderlich und wird stark empfohlen, wenn kollektive Kommunikation genutzt wird. Der Nachteil besteht darin, dass ein solcher Job auf den größten zusammenhängenden Block an GPUs wartet statt auf die Summe der verfügbaren, verstreuten GPUs; das ist in der Regel der richtige Kompromiss.
Trennen Sie die nominelle Quote von dem Leihen
Ein nützliches Muster: Jede Warteschlange verfügt über einen garantierten Anteil, den sie stets beanspruchen kann, sowie über eine Leihgrenze, die es ihr ermöglicht, freie Kapazitäten aus anderen Warteschlangen zu nutzen. Das Leihen lässt das Cluster beschäftigt erscheinen, ohne ihn dauerhaft umzuverteilen. Die entscheidende Designfrage ist, ob die geliehene Kapazität rückgewonnen werden kann – ob die leihgebende Warteschlange den Leihnehmer zurückfordern kann, wenn sie ihre eigene Quote wieder benötigt. Wenn ja, müssen die Leihnehmer checkpointfähig sein. Wenn nein, ist „leihen“ ein höflicher Ausdruck für „weggeben“.
Achten Sie auf das Anti-Muster des Blockierens durch den Anfang der Warteschlange
Eine einzige Warteschlange mit strenger Prioritätsreihenfolge und ohne Leihen führt zu einer vorhersehbaren Störung: Eine große Aufgabe mit niedriger Priorität steht an erster Stelle, alle kleinen Aufgaben mit hoher Priorität warten dahinter, und die Auslastung sinkt, obwohl das Cluster voll erscheint. Die Lösung ist strukturell – entweder eine prioritätsbewusste Zulassung, die es Aufgaben niedrigerer Priorität ermöglicht, zu starten, sobald Platz vorhanden ist, oder ein kapazitätssparendes Leihen mit Rückgewinnung.
Vorbehaltsrecht
Das Vorbehaltsrecht ist der Weg, auf dem ein Prioritätsschema Wirklichkeit wird. Ohne es sind Prioritäten nur beratende Bezeichnungen, die nichts beeinflussen.
Drei Ebenen des Vorbehaltsrechts
- Ausschließung auf Pod-Ebene. Der Scheduler löscht einzelne Pods, um einen Knoten freizumachen. Dies ist günstig umzusetzen, doch gefährlich für Gruppenaufgaben, denn das Ausschließen eines Arbeiters einer synchronen Aufgabe lässt die übrigen Arbeiter untätig bleiben, bis die Aufgabe neu geplant wird.
- Vorbehaltsrecht auf Aufgabenebene. Die gesamte Arbeitslast wird ausgesetzt und erneut in die Warteschlange eingefügt. Dies ist geeignet für das Training, etwas verschwenderisch für die Inferenz.
- Vorbehaltsrecht basierend auf Checkpoints. Die vorbehaltenen Arbeitslasten erhalten ein Beendigungssignal, schreiben ihren Zustand ab und beenden sauber. Dies ist die einzige Variante, die nicht implizit verlorene Arbeit akzeptiert.
Das Vorbehaltsrecht hat seinen Preis – modellieren Sie ihn
Der Kostenfaktor des Vorbehaltsrechts liegt nicht im Ausschließen selbst, sondern in der Neuberechnung seit dem letzten Checkpoint. Wenn Sie alle dreißig Minuten einen Checkpoint erstellen und eine Aufgabe vorbehalten, die bereits neunundzwanzig Minuten lang läuft, haben Sie neunundzwanzig Minuten an GPU-Sekunden zerstört, um den Knoten zurückzugewinnen. Zwei Faktoren bestimmen dies:
- Frist zur Überprüfung. Geben Sie der Arbeitslast genügend Zeit für eine Überprüfung. Eine Frist von zwanzig Sekunden in Kombination mit einer Überprüfung, die neunzig Sekunden dauert, führt zu einer beschädigten Überprüfung, die unweigerlich auftritt.
- Mindestlaufzeit vor Enteignung. Einige Schedulierer erlauben es, zu verlangen, dass eine Aufgabe mindestens N Minuten lang läuft, bevor sie als Kandidat für die Enteignung in Frage kommt. Dadurch wird eine geringe Reaktionsfähigkeit beim Schedulieren gegen eine erhebliche Reduzierung von verschwendeter Arbeit eingetauscht.
Die Enteignung zu einem erstklassigen Vertrag mit der Arbeitslast machen
Der Schedulierer kann die Überprüfung nicht für Sie durchführen. Die Arbeitslast muss dies selbst umsetzen. Das bedeutet, SIGTERM zu handhaben, atomar zu schreiben und ehrlich darüber zu sein, wie lange die Speicherung dauert.
Gerechtigkeit
Gerechtigkeit ist der am meisten missverstandene der drei Säulen – hauptsächlich, weil sie meist sofort bewertet wird.
Gerechtigkeit ist zeitbasiert durchschnittlich, nicht sofortig
Eine Warteschlange mit null GPUs zum gegenwärtigen Zeitpunkt wird nicht ungerecht behandelt, wenn sie gestern 60% des Clusters verbraucht hat. Fair-share-Schedulierer erfassen eine abnehmende Nutzung: Jüngster Verbrauch hat großen Einfluss, alter Verbrauch sinkt auf Null. Daraus ergeben sich zwei Folgen. Erstens hat ein Team, das seit einer Woche inaktiv ist, heute zu Recht eine höhere effektive Priorität. Zweitens zeigt ein Dashboard mit sofortigen Messwerten ständig Alarme an, und die Betreuer werden ständig versuchen, ein funktionierendes System „zu reparieren“.
Gewichtete Anteile statt gleicher Anteile
Gleiche Anteile gehen davon aus, dass die Arbeit jedes Teams gleich wertvoll ist – was selten der Fall ist. Gewichtete Anteile ermöglichen es, Aussagen wie „die Forschungsgruppe erhält den doppelten Anteil an der Plattform-Gruppe“ als Zahl statt als Argument auszudrücken. Der Schedulierer verteilt dann proportional zum Gewicht, wenn die Nachfrage die Angebotsmenge übersteigt.
Verhungern und Altersfaktor
Eine strenge Prioritätsreihenfolge ohne Altersfaktor lässt Aufgaben niedriger Priorität endlos verhungern. Ein Altersfaktor – bei dem die effektive Priorität mit der Wartezeit steigt – begrenzt die maximale Wartezeit. Wo ein Altersfaktor nicht verfügbar ist, sollten Sie ein Maximalwartezeit-Ziel pro Warteschlange festlegen und Verstöße als Fehlerrisiko untersuchen, das es zu klären gilt – nicht als akzeptablen Betriebskosten.
Gerechtigkeit über alle Ressourcen hinweg, nicht nur über GPUs
Eine Aufgabe kann GPU-arm und CPU-intensiv sein oder GPU-intensiv und speicherintensiv. Die Gerechtigkeitsregel für dominante Ressourcen gilt dabei natürlicherweise weiter: Der Anteil jeder Aufgabe wird als ihr maximaler Verhältniswert über alle begrenzten Ressourcen berechnet, nicht nur über die GPUs. In einem Cluster, in dem das Vorausverarbeiten die CPUs ausschöpft, führt eine Gerechtigkeitsberechnung, die nur GPU-Ressourcen berücksichtigt, zu überraschenden Engpässen.
Schritt-für-Schritt-Installation
Die folgende Abfolge setzt einen Kubernetes-Cluster mit GPU-Node voraus; eine SLURM-Alternative wird angegeben. Passen Sie Namen und Versionen an Ihre Umgebung an.
1. Überprüfen Sie, ob die GPUs als planbare Ressourcen sichtbar sind.
kubectl get nodes -L nvidia.com/gpuDadurch werden Nodes aufgelistet, wobei eine nvidia.com/gpu-Spalte die Anzahl der zuweisbaren GPUs anzeigt. Ist diese Spalte leer, müssen Sie die Sichtbarkeit der Geräte vor einer Bearbeitung der Planung korrigieren.
2. Installieren Sie die Warteschlangenschicht. Im Beispiel wird Kueue verwendet, ein Kubernetes-SIG-Projekt für die Warteschlangenverwaltung von Aufgaben mit Quoten, Leihen und Vorbehaltsrechten.
kubectl apply --server-side -k "github.com/kubernetes-sigs/kueue/config/default?ref=main"Dadurch werden der Controller sowie seine CRDs installiert. In der Produktion sollte ref auf ein veröffentlichtes Tag festgelegt werden, anstatt main zu verfolgen.
3. Überprüfen Sie, ob der Controller läuft.
kubectl get pods -n kueue-systemDer Pod des Controller-Managers sollte auf Running zugreifen können. Prüfen Sie seine Logs, falls die Admission-Webhooks sich nicht registrieren.
4. Definieren Sie eine Cluster-Warteschlange mit nomineller Quote und zurückholbaren Leihen. Ersetzen Sie die Namen und Mengen der Ressourcen durch Ihre eigenen.
apiVersion: kueue.x-k8s.io/v1beta1
kind: ClusterQueue
metadata:
name: cluster-queue
spec:
namespaceSelector: {}
resourceGroups:
- coveredResources: ["cpu", "memory", "nvidia.com/gpu"]
flavors:
- name: default
resources:
- name: "cpu"
nominalQuota: 200
- name: "memory"
nominalQuota: 800Gi
- name: "nvidia.com/gpu"
nominalQuota: 8Die nominalQuota-Werte stellen den garantierten Mindestwert dar. Gruppen von Cluster-Warteschlangen können in eine Kohorte gelegt werden, sodass die inaktive Quote zwischen ihnen ausgeliehen werden kann; prüfen Sie die in Schritt 2 installierte CRD-Version, da sich die Feldnamen zwischen den Versionen unterscheiden.
5. Erstellen Sie eine namensgebundene Warteschlange, die auf die Cluster-Warteschlange verweist.
apiVersion: kueue.x-k8s.io/v1beta1
kind: LocalQueue
metadata:
name: team-a
namespace: team-a
spec:
clusterQueue: cluster-queueTeams reichen Aufgaben an ihre lokale Warteschlange ein; die Cluster-Warteschlange setzt die globale Richtlinie durch.
6. Markieren Sie Aufgaben als von der Warteschlange verwaltet und ausgesetzt. Eine Batch-Aufgabe mit dem Warteschlangen-Label wird vom Controller bis zur Zulassung zurückgehalten.
apiVersion: batch/v1
kind: Job
metadata:
name: finetune-001
namespace: team-a
labels:
kueue.x-k8s.io/queue-name: team-a
spec:
suspend: true
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: trainer
image: your-registry/trainer:latest
resources:
limits:
nvidia.com/gpu: 1Der Feld suspend: true ermöglicht es dem Warteschlangencontroller, zu entscheiden, wann die Pod erstellt wird. Die Einstellung von backoffLimit: 0 verhindert, dass Kubernetes mit seinem eigenen Wiederholungszyklus gegen den Scheduler antreten kann.
SLURM-Alternative. Auf einem SLURM-Cluster werden Prioritätsstufen auf Partitionsebene definiert und QOS zur Vorbeugung der Unterbrechung genutzt.
# slurm.conf fragment — partition priority drives preemption across tiers
# PreemptType=preempt/partition_prio
# PartitionName=interactive PriorityTier=1000 PreemptMode=CANCEL MaxTime=04:00:00 Nodes=ALL
# PartitionName=batch PriorityTier=1 PreemptMode=REQUEUE MaxTime=7-00:00:00 Nodes=ALLPreemptMode=REQUEUE gibt die unterbrochene Aufgabe anstelle dessen zurück in die Warteschlange, anstatt sie zu beenden – was die richtige Standardeinstellung für Trainingslasten ist, die nicht schnell Checkpoints erstellen können. Überprüfen Sie die genauen Parameterbezeichnungen anhand Ihrer SLURM-Version, bevor Sie dies einsetzen.
Verwendungsbeispiele
Beispiel 1: Einreichen und Beobachten der Warteschlangenzulassung
kubectl -n team-a apply -f finetune-001.yaml
kubectl -n team-a get workloads
kubectl -n team-a describe workload finetune-001Der erste Befehl reicht die Aufgabe ein; der zweite zeigt an, ob sie zugelassen wurde oder noch aussteht; der dritte erklärt warum – unzureichende Quoten, nicht planbare Art oder Wartezeit auf eine Gruppenbeschränkung.
Beispiel 2: Checkpointing unter Berücksichtigung von Unterbrechungen
Dieses Muster handhabt SIGTERM, schreibt atomar über eine temporäre Datei und beendet sauber, damit die Aufgabe erneut in die Warteschlange gegeben werden kann.
import os
import signal
import torch
CHECKPOINT_PATH = os.environ.get("CHECKPOINT_PATH", "/checkpoints/latest.pt")
_stop = False
def _handle_sigterm(signum, frame):
global _stop
_stop = True
signal.signal(signal.SIGTERM, _handle_sigterm)
def save(state: dict) -> None:
"""Write atomically so a crash mid-write never corrupts the checkpoint."""
tmp = f"{CHECKPOINT_PATH}.tmp"
torch.save(state, tmp)
os.replace(tmp, CHECKPOINT_PATH)
def maybe_stop(model, optimizer, step: int) -> bool:
if _stop:
save({"step": step, "model": model.state_dict(),
"optimizer": optimizer.state_dict()})
return True
return FalseRufen Sie maybe_stop am Ende jedes Schritts auf. Die Gnadenfrist des Schedulers muss länger sein als die Zeit, die save benötigt; messen Sie sie statt zu raten.
Beispiel 3: Überprüfung einer gewichteten fairen-Share-Strategie
Bevor Sie dem Fairnessverhalten eines Schedulers vertrauen, überprüfen Sie, ob Ihre Gewichte die erwartete Reihenfolge erzeugen. Dieses kleine Skript dient als Lehrmodell, nicht als Scheduler.
from dataclasses import dataclass
@dataclass
class Queue:
name: str
weight: float
gpu_seconds_recent: float
waited_seconds: float = 0.0
def admission_order(queues, aging_rate: float = 0.01):
"""Lower score = admitted first. Aging rewards long waits."""
def score(q: Queue) -> float:
share = q.gpu_seconds_recent / max(q.weight, 1e-9)
return share - aging_rate * q.waited_seconds
return sorted(queues, key=score)
queues = [
Queue("team-a", weight=1.0, gpu_seconds_recent=3600),
Queue("team-b", weight=2.0, gpu_seconds_recent=3600, waited_seconds=600),
Queue("team-c", weight=1.0, gpu_seconds_recent=0, waited_seconds=120),
]
for q in admission_order(queues):
print(q.name)Führen Sie es mit unterschiedlichen Gewichten und Verfallsraten aus, um zu sehen, wie empfindlich die Reihenfolge ist. Eine Strategie, die bei einer 0.001-Änderung in aging_rate die Reihenfolge ändert, ist keine Strategie, die Sie Ihren Nutzern erklären können.
Was messen
Vier Anzeichen zeigen an, ob das Design funktioniert: p95 Wartezeit in der Queue pro Queue (Reaktionsfähigkeit), zulassene GPU-Sekunden pro verfügbare GPU-Sekunde (Auslastung), Vorbehalte pro Stunde und Checkpoint-Verlust pro Vorbehalt (der Kostenfaktor Ihres Prioritätskonzepts) sowie maximale Wartezeit für die am wenigsten bediente Queue (Verhungern). Wenn sich der vierte Wert nach oben bewegt, während die ersten drei in Ordnung sind, ist Ihre Gerechtigkeitspolitik heimlich zu einer Prioritätspolitik geworden.
Fazit
Die Scheduling-Strategie ist das wirksamste Instrument in einem lokalen GPU-Cluster, da sie bereits vor dem Ausführen jeder Arbeitslast wirkt. Die drei Mechanismen – Queue-Design, Vorbehalte und Gerechtigkeit – sind keine unabhängigen Einstellungen. Queues definieren die Einheiten, die vorbehalten werden können; Vorbehalte bestimmen, ob die Garantien einer Queue glaubwürdig sind; Gerechtigkeit entscheidet darüber, ob das Queuing-Konzept über Monate statt nur Tage unter echten Lasten Bestand hat.
Beginnen Sie mit der kleinsten Änderung, die ein gemessenes Problem behebt: Gruppenzulassung für eine synchrone Trainingsaufgabe oder eine Gnadenfrist, die lang genug ist, um einen Checkpoint abzuspeichern. Modellieren Sie anschließend die Kosten von Vorbehalten, bevor Sie sie aktivieren, messen Sie die Gerechtigkeit über einen sich verändernden Zeitraum statt sofort und betrachten Sie die Fähigkeit der Arbeitslast, Checkpoints zu erstellen, als Voraussetzung und nicht als nachträglichen Aspekt. Der Quellbeitrag bietet einen nützlichen Ausgangspunkt für diese Diskussion; die konkrete Politik müssen Sie selbst entwerfen.



