-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
src,test: ensure that V8 fast APIs are called #54317
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#include "node_debug.h" | ||
|
||
#ifdef DEBUG | ||
#include "node_binding.h" | ||
|
||
#include "env-inl.h" | ||
#include "util.h" | ||
#include "v8-fast-api-calls.h" | ||
#include "v8.h" | ||
|
||
#include <string_view> | ||
#include <unordered_map> | ||
#endif // DEBUG | ||
|
||
namespace node { | ||
namespace debug { | ||
|
||
#ifdef DEBUG | ||
using v8::Context; | ||
using v8::FastApiCallbackOptions; | ||
using v8::FunctionCallbackInfo; | ||
using v8::Local; | ||
using v8::Number; | ||
using v8::Object; | ||
using v8::String; | ||
using v8::Value; | ||
|
||
thread_local std::unordered_map<std::string_view, int> v8_fast_api_call_counts; | ||
|
||
void TrackV8FastApiCall(std::string_view key) { | ||
v8_fast_api_call_counts[key]++; | ||
} | ||
|
||
int GetV8FastApiCallCount(std::string_view key) { | ||
return v8_fast_api_call_counts[key]; | ||
} | ||
|
||
void GetV8FastApiCallCount(const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
if (!args[0]->IsString()) { | ||
env->ThrowError("getV8FastApiCallCount must be called with a string"); | ||
return; | ||
} | ||
Local<String> key = args[0].As<String>(); | ||
Utf8Value utf8_key(env->isolate(), key); | ||
targos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
args.GetReturnValue().Set(GetV8FastApiCallCount(utf8_key.ToString())); | ||
} | ||
|
||
void SlowIsEven(const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
if (!args[0]->IsNumber()) { | ||
env->ThrowError("isEven must be called with a number"); | ||
return; | ||
} | ||
int64_t value = args[0].As<Number>()->Value(); | ||
args.GetReturnValue().Set(value % 2 == 0); | ||
} | ||
|
||
bool FastIsEven(Local<Value> receiver, | ||
const int64_t value, | ||
// NOLINTNEXTLINE(runtime/references) | ||
FastApiCallbackOptions& options) { | ||
TRACK_V8_FAST_API_CALL("debug.isEven"); | ||
return value % 2 == 0; | ||
} | ||
|
||
void SlowIsOdd(const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
if (!args[0]->IsNumber()) { | ||
env->ThrowError("isOdd must be called with a number"); | ||
return; | ||
} | ||
int64_t value = args[0].As<Number>()->Value(); | ||
args.GetReturnValue().Set(value % 2 != 0); | ||
} | ||
|
||
bool FastIsOdd(Local<Value> receiver, | ||
const int64_t value, | ||
// NOLINTNEXTLINE(runtime/references) | ||
FastApiCallbackOptions& options) { | ||
TRACK_V8_FAST_API_CALL("debug.isOdd"); | ||
return value % 2 != 0; | ||
} | ||
|
||
static v8::CFunction fast_is_even(v8::CFunction::Make(FastIsEven)); | ||
static v8::CFunction fast_is_odd(v8::CFunction::Make(FastIsOdd)); | ||
|
||
void Initialize(Local<Object> target, | ||
Local<Value> unused, | ||
Local<Context> context, | ||
void* priv) { | ||
SetMethod(context, target, "getV8FastApiCallCount", GetV8FastApiCallCount); | ||
SetFastMethod(context, target, "isEven", SlowIsEven, &fast_is_even); | ||
SetFastMethod(context, target, "isOdd", SlowIsOdd, &fast_is_odd); | ||
} | ||
#endif // DEBUG | ||
|
||
} // namespace debug | ||
} // namespace node | ||
|
||
#ifdef DEBUG | ||
targos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(debug, node::debug::Initialize) | ||
#endif // DEBUG |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#pragma once | ||
|
||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#ifdef DEBUG | ||
#include <string_view> | ||
#endif // DEBUG | ||
|
||
namespace node { | ||
namespace debug { | ||
|
||
#ifdef DEBUG | ||
void TrackV8FastApiCall(std::string_view key); | ||
int GetV8FastApiCallCount(std::string_view key); | ||
|
||
#define TRACK_V8_FAST_API_CALL(key) node::debug::TrackV8FastApiCall(key) | ||
#else // !DEBUG | ||
#define TRACK_V8_FAST_API_CALL(key) | ||
#endif // DEBUG | ||
|
||
} // namespace debug | ||
} // namespace node | ||
|
||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Flags: --expose-internals --no-warnings --allow-natives-syntax | ||
'use strict'; | ||
const common = require('../common'); | ||
|
||
const assert = require('assert'); | ||
const { internalBinding } = require('internal/test/binding'); | ||
|
||
if (!common.isDebug) { | ||
targos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert.throws(() => internalBinding('debug'), { | ||
message: 'No such binding: debug' | ||
}); | ||
return; | ||
} | ||
|
||
const { | ||
getV8FastApiCallCount, | ||
isEven, | ||
isOdd, | ||
} = internalBinding('debug'); | ||
|
||
assert.throws(() => getV8FastApiCallCount(), { | ||
message: 'getV8FastApiCallCount must be called with a string', | ||
}); | ||
|
||
function testIsEven() { | ||
for (let i = 0; i < 10; i++) { | ||
assert.strictEqual(isEven(i), i % 2 === 0); | ||
} | ||
} | ||
|
||
function testIsOdd() { | ||
for (let i = 0; i < 20; i++) { | ||
assert.strictEqual(isOdd(i), i % 2 !== 0); | ||
} | ||
} | ||
|
||
// Should return 0 by default for any string. | ||
assert.strictEqual(getV8FastApiCallCount(''), 0); | ||
assert.strictEqual(getV8FastApiCallCount('foo'), 0); | ||
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 0); | ||
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 0); | ||
|
||
eval('%PrepareFunctionForOptimization(testIsEven)'); | ||
testIsEven(); | ||
eval('%PrepareFunctionForOptimization(testIsOdd)'); | ||
testIsOdd(); | ||
|
||
// Functions should not be optimized yet. | ||
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 0); | ||
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 0); | ||
|
||
eval('%OptimizeFunctionOnNextCall(testIsEven)'); | ||
testIsEven(); | ||
eval('%OptimizeFunctionOnNextCall(testIsOdd)'); | ||
testIsOdd(); | ||
|
||
// Functions should have been optimized and fast path taken. | ||
assert.strictEqual(getV8FastApiCallCount('debug.isEven'), 10); | ||
assert.strictEqual(getV8FastApiCallCount('debug.isOdd'), 20); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the purpose of the tests, I think using a bool should be enough - an int would overflow if the process runs for long enough.
Also if we require the counter/toggle to be pre-declared in a macro list, we can just track them in thread_local bool instead of doing a runtime map lookup, and the overhead of simply setting a variable to true feels negligible enough to be enabled at runtime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that but I don't think it matters. As an internal debug-only API, it is not meant to be useful outside of our unit tests and those would never run for long enough.
I thought a counter could be useful for complex cases where we want to call the same API multiple times (or it's called in a loop internally).
I'd say feel free to refactor / change in a future PR (or block this one). I spent enough energy on it myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if it's called multiple times, only V8 can really reliably check exactly how many times the fast API is run, as that's subject to the optimizing strategy - unless we use the intrinsics that are not supported for embedders' use cases. As an embedder a true/false is the best we can count on.
Sorry if this is untimely - feel free to land it, just trying to address the previous comments about release/debug differences.