1515#include < executorch/backends/webgpu/runtime/WebGPUCompat.h>
1616#include < executorch/backends/webgpu/runtime/WebGPUDevice.h>
1717
18+ #include < cstdlib>
1819#include < cstring>
1920#include < stdexcept>
2021
@@ -496,18 +497,57 @@ void WebGPUGraph::copy_inputs(
496497 }
497498}
498499
500+ namespace {
501+ // Bench gate: compiled out unless WGPU_BACKEND_ENABLE_PROFILING; then the
502+ // WEBGPU_TIMESTAMP_QUERY env var enables per-pass GPU timestamp queries.
503+ bool should_timestamp_query () {
504+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
505+ static const bool enabled = std::getenv (" WEBGPU_TIMESTAMP_QUERY" ) != nullptr ;
506+ return enabled;
507+ #else
508+ return false ;
509+ #endif
510+ }
511+ } // namespace
512+
499513void WebGPUGraph::execute () {
500514 const size_t n = dispatches_.size ();
501515 const size_t chunk = execute_config_.chunk_size ;
502516
503517 if (chunk == 0 || n <= chunk) {
518+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
519+ // Bench: timestamp-query pool, null unless env-gated + feature present.
520+ WebGPUQueryPool* qp = nullptr ;
521+ if (should_timestamp_query () && n > 0 ) {
522+ if (auto * ctx = get_default_webgpu_context ()) {
523+ if (ctx->timestamp_supported ) {
524+ if (!ctx->querypool || ctx->querypool ->capacity () < n) {
525+ ctx->querypool = std::make_unique<WebGPUQueryPool>();
526+ ctx->querypool ->initialize (device_, static_cast <uint32_t >(n));
527+ }
528+ qp = ctx->querypool .get ();
529+ qp->reset (static_cast <uint32_t >(n));
530+ }
531+ }
532+ }
533+ #endif // WGPU_BACKEND_ENABLE_PROFILING
534+
504535 WGPUCommandEncoderDescriptor enc_desc = {};
505536 WGPUCommandEncoder encoder =
506537 wgpuDeviceCreateCommandEncoder (device_, &enc_desc);
507538
508539 // One pass per dispatch: enforces storage RAW ordering across deps.
509- for (const auto & dispatch : dispatches_) {
540+ for (size_t i = 0 ; i < n; i++) {
541+ const auto & dispatch = dispatches_[i];
510542 WGPUComputePassDescriptor pass_desc = {};
543+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
544+ // tw must outlive BeginComputePass (the descriptor points at it).
545+ WGPUPassTimestampWrites tw = {};
546+ if (qp) {
547+ tw = qp->writes_for (static_cast <uint32_t >(i));
548+ pass_desc.timestampWrites = &tw;
549+ }
550+ #endif // WGPU_BACKEND_ENABLE_PROFILING
511551 WGPUComputePassEncoder pass =
512552 wgpuCommandEncoderBeginComputePass (encoder, &pass_desc);
513553 wgpuComputePassEncoderSetPipeline (pass, dispatch.pipeline );
@@ -517,22 +557,51 @@ void WebGPUGraph::execute() {
517557 pass, dispatch.workgroup_count_x , 1 , 1 );
518558 wgpuComputePassEncoderEnd (pass);
519559 wgpuComputePassEncoderRelease (pass);
560+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
561+ if (qp) {
562+ qp->record (
563+ static_cast <uint32_t >(i),
564+ dispatch.kernel_name ,
565+ {dispatch.workgroup_count_x , 1 , 1 },
566+ {1 , 1 , 1 });
567+ }
568+ #endif // WGPU_BACKEND_ENABLE_PROFILING
520569 }
521570
522571 for (const auto & copy : output_copies_) {
523572 wgpuCommandEncoderCopyBufferToBuffer (
524573 encoder, copy.src_buffer , 0 , copy.staging_buffer , 0 , copy.nbytes );
525574 }
526575
576+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
577+ if (qp) {
578+ qp->resolve (encoder);
579+ }
580+ #endif // WGPU_BACKEND_ENABLE_PROFILING
581+
527582 WGPUCommandBufferDescriptor cmd_desc = {};
528583 WGPUCommandBuffer cmd = wgpuCommandEncoderFinish (encoder, &cmd_desc);
529584 wgpuQueueSubmit (queue_, 1 , &cmd);
530585
531586 wgpuCommandBufferRelease (cmd);
532587 wgpuCommandEncoderRelease (encoder);
588+
589+ #ifdef WGPU_BACKEND_ENABLE_PROFILING
590+ if (qp) {
591+ qp->extract_results (instance_);
592+ qp->print_results ();
593+ }
594+ #endif // WGPU_BACKEND_ENABLE_PROFILING
533595 return ;
534596 }
535597
598+ // GPU timestamp queries assume one submit; chunked execute is multi-submit.
599+ if (should_timestamp_query ()) {
600+ throw std::runtime_error (
601+ " WebGPU: WEBGPU_TIMESTAMP_QUERY is incompatible with chunked execute "
602+ " (multi-submit); disable chunking to use GPU timestamp queries" );
603+ }
604+
536605 const size_t first_chunk = execute_config_.initial_chunk_size > 0
537606 ? execute_config_.initial_chunk_size
538607 : chunk;
0 commit comments