Plugin performance references
Understanding how plugins add latency helps you choose the right plugin type and language for AI Gateway. Built-in plugins add microsecond-level overhead, while custom plugins written in WebAssembly (Wasm) incur additional memory-copy costs that vary by language.
Built-in plugin performance
Built-in plugins add microsecond-level latency per request. For example, key-auth adds approximately 20 microseconds and basic-auth adds approximately 30 microseconds.
Custom plugin performance
Custom plugins run inside a WebAssembly (Wasm) VM. Each time a plugin accesses request data — such as reading or writing request headers — the runtime copies data across the Wasm VM boundary. This memory-copy overhead is the primary source of latency for custom plugins.
The following benchmarks measure this overhead across four languages. Each plugin runs the same logic: 20 loop iterations of request header setting, retrieval, and removal.
-
C++ implementation:
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) { std::string fake_header_key_prefix = "fake_key_"; std::string fake_header_value_prefix = "fake_value_"; // Inject 20 synthetic headers for (size_t i = 0; i < 20; i++) { addRequestHeader(fake_header_key_prefix + std::to_string(i), fake_header_value_prefix + std::to_string(i)); } // Validate header existence 20 times for (size_t i = 0; i < 20; i++) { getRequestHeader(fake_header_key_prefix + std::to_string(i)); } // Cleanup injected headers for (size_t i = 0; i < 20; i++) { removeRequestHeader(fake_header_key_prefix + std::to_string(i)); } return FilterHeadersStatus::Continue; } -
Go implementation:
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action { fake_header_key_prefix := "fake_key_" fake_header_value_prefix := "fake_value_" for i := 0; i < 20; i++ { proxywasm.AddHttpRequestHeader(fake_header_key_prefix+strconv.Itoa(i), fake_header_value_prefix+strconv.Itoa(i)) } for i := 0; i < 20; i++ { proxywasm.GetHttpRequestHeader(fake_header_key_prefix + strconv.Itoa(i)) } for i := 0; i < 20; i++ { proxywasm.RemoveHttpRequestHeader(fake_header_key_prefix + strconv.Itoa(i)) } return types.ActionContinue } -
Rust implementation:
fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { for i in 0..20 { let key = "fake_key_".to_string() + &i.to_string(); let value = "fake_value_".to_string() + &i.to_string(); self.set_http_request_header(&key, Some(&value)); } for i in 0..20 { let key = "fake_key_".to_string() + &i.to_string(); self.get_http_request_header(&key); } for i in 0..20 { let key = "fake_key_".to_string() + &i.to_string(); self.set_http_request_header(&key, None); } Action::Continue } -
AssemblyScript implementation:
function onRequestHeaders(a: u32, end_of_stream: bool): FilterHeadersStatusValues { let fake_header_key_prefix: string = "fake_key_"; let fake_header_value_prefix: string = "fake_value_"; for (let i = 0; i < 20; i++) { stream_context.headers.request.add(fake_header_key_prefix + i.toString(), fake_header_value_prefix + i.toString()) } for (let i = 0; i < 20; i++) { stream_context.headers.request.get(fake_header_key_prefix + i.toString()) } for (let i = 0; i < 20; i++) { stream_context.headers.request.remove(fake_header_key_prefix + i.toString()) } return FilterHeadersStatusValues.Continue; }
Language performance comparison
|
Language |
Additional latency (ms) |
|
C++ |
0.19 |
|
Go |
0.20 |
|
Rust |
0.21 |
|
AssemblyScript |
0.21 |
The latency differences across languages are negligible. Because the Wasm VM boundary overhead dominates, the compiled language's runtime characteristics have minimal impact on the result.
Choose a language based on your team's existing expertise, the maturity of its Wasm toolchain, and the availability of debugging and observability tools.