// Copyright 2024 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "include/v8-context.h" #include "include/v8-fast-api-calls.h" #include "include/v8-internal.h" #include "include/v8-local-handle.h" #include "include/v8-persistent-handle.h" #include "include/v8-template.h" #include "src/base/macros.h" #include "test/benchmarks/cpp/benchmark-utils.h" #include "third_party/google_benchmark_chrome/src/include/benchmark/benchmark.h" namespace { v8::Local v8_str(const char* x) { return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x).ToLocalChecked(); } class FastApiBenchmark : public v8::benchmarking::BenchmarkWithIsolate { public: static void RegularCallback(const v8::FunctionCallbackInfo& info) { } static void FastCallback(v8::Local receiver, v8::FastApiCallbackOptions& options) {} static uint32_t FastStringValueView(v8::Local receiver, v8::Local value, v8::FastApiCallbackOptions& options) { v8::Local str = value.As(); v8::String::ValueView view(options.isolate, str); return view.length(); } static uint32_t FastOneByteString(v8::Local receiver, const v8::FastOneByteString& str, v8::FastApiCallbackOptions& options) { return str.length; } static void RegularString(const v8::FunctionCallbackInfo& info) { v8::Local str = info[0].As(); v8::String::ValueView view(info.GetIsolate(), str); info.GetReturnValue().Set(view.length()); } void SetUp(::benchmark::State& state) override { auto* isolate = v8_isolate(); v8::HandleScope handle_scope(isolate); auto proxy_template_function = v8::FunctionTemplate::New(isolate); auto object_template = proxy_template_function->InstanceTemplate(); { v8::CFunction fast_callback = v8::CFunction::Make(FastApiBenchmark::FastCallback); object_template->Set( isolate, "fastApiCall", v8::FunctionTemplate::New( isolate, FastApiBenchmark::RegularCallback, v8::Local(), v8::Local(), 1, v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect, &fast_callback)); } { object_template->Set(isolate, "regularApiCall", v8::FunctionTemplate::New( isolate, FastApiBenchmark::RegularCallback)); } { v8::CFunction fast_callback = v8::CFunction::Make(FastApiBenchmark::FastOneByteString); object_template->Set( isolate, "fastOneByteString", v8::FunctionTemplate::New( isolate, FastApiBenchmark::RegularString, v8::Local(), v8::Local(), 1, v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect, &fast_callback)); } { v8::CFunction fast_callback = v8::CFunction::Make(FastApiBenchmark::FastStringValueView); object_template->Set( isolate, "fastStringValueView", v8::FunctionTemplate::New( isolate, FastApiBenchmark::RegularString, v8::Local(), v8::Local(), 1, v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect, &fast_callback)); } { object_template->Set( isolate, "regularString", v8::FunctionTemplate::New(isolate, FastApiBenchmark::RegularString)); } v8::Local context = v8::Context::New(isolate, nullptr, object_template); context_.Reset(isolate, context); context->Enter(); } void TearDown(::benchmark::State& state) override { auto* isolate = v8_isolate(); v8::HandleScope handle_scope(isolate); auto context = context_.Get(isolate); context->Exit(); context_.Reset(); } v8::Local CompileBenchmarkScript(const char* source) { v8::EscapableHandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local v8_source = v8_str(source); v8::Local script = v8::Script::Compile(context, v8_source).ToLocalChecked(); return handle_scope.Escape(script); } protected: v8::Local v8_context() { return context_.Get(v8_isolate()); } v8::Global context_; }; } // namespace BENCHMARK_F(FastApiBenchmark, FastCallToEmptyCallback)(benchmark::State& st) { const char* kScript = "function invoke() { globalThis.fastApiCall(); }" "\%PrepareFunctionForOptimization(invoke);" "invoke();" "\%OptimizeFunctionOnNextCall(invoke);" "for (var i =0; i < 1_000_000; i++) invoke();"; v8::HandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local script = CompileBenchmarkScript(kScript); v8::HandleScope benchmark_handle_scope(v8_isolate()); for (auto _ : st) { USE(_); v8::Local result = script->Run(context).ToLocalChecked(); benchmark::DoNotOptimize(result); } } BENCHMARK_F(FastApiBenchmark, RegularCallToEmptyCallback) (benchmark::State& st) { const char* kScript = "function invoke() { globalThis.regularApiCall(); }" "\%PrepareFunctionForOptimization(invoke);" "invoke();" "\%OptimizeFunctionOnNextCall(invoke);" "for (var i =0; i < 1_000_000; i++) invoke();"; v8::HandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local script = CompileBenchmarkScript(kScript); v8::HandleScope benchmark_handle_scope(v8_isolate()); for (auto _ : st) { USE(_); v8::Local result = script->Run(context).ToLocalChecked(); benchmark::DoNotOptimize(result); } } BENCHMARK_F(FastApiBenchmark, FastOneByteString)(benchmark::State& st) { const char* kScript = "function invoke() { globalThis.fastOneByteString('Hello World'); }" "\%PrepareFunctionForOptimization(invoke);" "invoke();" "\%OptimizeFunctionOnNextCall(invoke);" "for (var i =0; i < 1_000_000; i++) invoke();"; v8::HandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local script = CompileBenchmarkScript(kScript); v8::HandleScope benchmark_handle_scope(v8_isolate()); for (auto _ : st) { USE(_); v8::Local result = script->Run(context).ToLocalChecked(); benchmark::DoNotOptimize(result); } } BENCHMARK_F(FastApiBenchmark, FastStringValueView)(benchmark::State& st) { const char* kScript = "function invoke() { globalThis.fastStringValueView('Hello World'); }" "\%PrepareFunctionForOptimization(invoke);" "invoke();" "\%OptimizeFunctionOnNextCall(invoke);" "for (var i =0; i < 1_000_000; i++) invoke();"; v8::HandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local script = CompileBenchmarkScript(kScript); v8::HandleScope benchmark_handle_scope(v8_isolate()); for (auto _ : st) { USE(_); v8::Local result = script->Run(context).ToLocalChecked(); benchmark::DoNotOptimize(result); } } BENCHMARK_F(FastApiBenchmark, RegularString)(benchmark::State& st) { const char* kScript = "function invoke() { globalThis.regularString('Hello World'); }" "\%PrepareFunctionForOptimization(invoke);" "invoke();" "\%OptimizeFunctionOnNextCall(invoke);" "for (var i =0; i < 1_000_000; i++) invoke();"; v8::HandleScope handle_scope(v8_isolate()); v8::Local context = v8_context(); v8::Local script = CompileBenchmarkScript(kScript); v8::HandleScope benchmark_handle_scope(v8_isolate()); for (auto _ : st) { USE(_); v8::Local result = script->Run(context).ToLocalChecked(); benchmark::DoNotOptimize(result); } }