Signatur
Beschreibung
Die Methode writeTimestamp() des GPUCommandEncoder nimmt einen Befehl in den Befehlspuffer auf. Dieser Befehl schreibt einen Zeitstempel in einen bestimmten Eintrag eines GPUQuerySet, sobald alle vorher kodierten Befehle desselben GPUCommandBuffer auf der GPU abgeschlossen sind. Wenn man zwei solche Zeitstempel rund um eine Folge von Befehlen setzt, kann man messen, wie lange die GPU dafür gebraucht hat. Das hilft bei der Leistungsanalyse von Render- und Compute-Durchläufen.
Die Zeitstempel sind 64-Bit-Ganzzahlen in Nanosekunden. Um sie auszulesen, überträgt man sie mit resolveQuerySet() in einen GPUBuffer (Nutzung QUERY_RESOLVE). Danach kopiert man sie in einen Puffer, den die CPU mappen kann, und liest sie dort über ein BigUint64Array aus.
Wichtig: writeTimestamp() wurde aus der WebGPU-Spezifikation entfernt. Der Standardweg ist heute die Eigenschaft timestampWrites in den Deskriptoren von beginRenderPass() und beginComputePass(). Sie misst Zeitstempel am Anfang und am Ende eines Durchlaufs. Neuen Code solltest du nur noch mit timestampWrites schreiben. writeTimestamp() ist höchstens als Rückfallebene für ältere, experimentelle Implementierungen gedacht.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $querySet Pflicht | GPUQuerySet | Das Query-Set, in das geschrieben wird. Es muss mit type: "timestamp" erzeugt worden sein. |
|
| $queryIndex Pflicht | number | Index des Eintrags im querySet, in den der Zeitstempel geschrieben wird. Er muss kleiner als querySet.count sein. |
Rückgabewert
queue.submit() übermittelt wurde.Beispiele
Gerät mit Timestamp-Feature anfordern und Query-Set erzeugen
const adapter = await navigator.gpu.requestAdapter();
if (!adapter.features.has('timestamp-query')) {
throw new Error('timestamp-query wird nicht unterstützt');
}
const device = await adapter.requestDevice({
requiredFeatures: ['timestamp-query'],
});
// Zwei Einträge: Start und Ende
const querySet = device.createQuerySet({ type: 'timestamp', count: 2 });
console.log(querySet.type, querySet.count);
GPU-Zeit eines Compute-Durchlaufs messen (Legacy-Weg)
// Annahme: device, querySet (2 Einträge), pipeline, bindGroup existieren
const resolveBuffer = device.createBuffer({
size: 16, // 2 × 8 Byte
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
});
const readBuffer = device.createBuffer({
size: 16,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
});
const encoder = device.createCommandEncoder();
encoder.writeTimestamp(querySet, 0); // Start
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(64);
pass.end();
encoder.writeTimestamp(querySet, 1); // Ende
encoder.resolveQuerySet(querySet, 0, 2, resolveBuffer, 0);
encoder.copyBufferToBuffer(resolveBuffer, 0, readBuffer, 0, 16);
device.queue.submit([encoder.finish()]);
await readBuffer.mapAsync(GPUMapMode.READ);
const [start, end] = new BigUint64Array(readBuffer.getMappedRange());
console.log(`GPU-Zeit: ${Number(end - start) / 1e6} ms`);
readBuffer.unmap();
Moderne Alternative: timestampWrites im Pass-Deskriptor
const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass({
timestampWrites: {
querySet,
beginningOfPassWriteIndex: 0,
endOfPassWriteIndex: 1,
},
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(64);
pass.end();
encoder.resolveQuerySet(querySet, 0, 2, resolveBuffer, 0);
device.queue.submit([encoder.finish()]);
// Wichtig · Fallstricke
- Veraltet: Die Methode steht nicht mehr in der WebGPU-Spezifikation. Nimm stattdessen
timestampWritesinbeginRenderPass()oderbeginComputePass(). Prüfe vor dem Aufruf mittypeof encoder.writeTimestamp === 'function', ob die Methode vorhanden ist. - Das Gerät muss mit dem Feature
"timestamp-query"angefordert werden. Sonst entsteht ein Validierungsfehler, und der Encoder wird ungültig. - Wenn
querySet.typenicht"timestamp"ist oderqueryIndex≥querySet.countist, führt das zu einem Validierungsfehler. - Browser runden oder quantisieren die Zeitstempel aus Sicherheitsgründen, zum Schutz vor Timing-Angriffen. Einzelne Messwerte sind daher ungenau. Miss lieber mehrere Durchläufe und bilde den Mittelwert.
- Die Werte sind
BigInt. Bilde Differenzen mitBigInt-Arithmetik und wandle erst danach mitNumber()um. In seltenen Fällen kann der Endwert kleiner als der Startwert sein, etwa bei einem Taktwechsel der GPU. Solche Messungen solltest du verwerfen. - Der Offset im Zielpuffer bei
resolveQuerySet()muss ein Vielfaches von 256 sein.