Squeezing v8 startup time 2

This is a second part to this other post.

The process of loading/streaming, parse, compile and run is complicated enough to handle optimally. Worse enough, when you run the same scripts over and over, it will turn quite inefficient too, since this process has to be performed every time for the same scripts.

There is something we can do for this situation though. We can obtain the parsed javascript bytecode contents and reuse it over and over again. The default script execution process, non streamed, would be something like:

ScriptOrigin origin(String::NewFromUtf8(
    isolate, constCharPtrToString));

v8::MaybeLocal<v8::Script> maybescript = v8::Script::Compile(
    v8Context, 
    v8StringSource,   // Local<String> object with source contents.
    &origin);

if (!maybescript.IsEmpty()) {
    Local<Script> script = maybescript.ToLocalChecked();
    if (script.IsEmpty()) {
        // report exception here
    } else {
        Local<Value> result = script->Run(v8Context).ToLocalChecked();
        ...
    }    
}

But, on the first script execution, we can change this run process to something like:

// source is a Local<String>
ScriptCompiler::Source source(source, ...);   

// create unbounded script (see v8.h)
auto unboundedScript = ScriptCompiler::CompileUnboundScript(
    isolate,
    &source,
    ScriptCompiler::CompileOptions::kNoCompileOptions).ToLocalChecked();

// obtain parsed bytecode for resusability. 
// Store for reusability and profit
auto bytecode = new BytecodeCacheEntry(
    ScriptCompiler::CreateCodeCache(unbounded_script));

// if you want to run this script, you must:
unboundedScript->BindToCurrentContext();

// and just
auto result = script->Run(context).ToLocalChecked();

// check for result, etc.

Successive executions of this script could benefit from the bytecode like:

// build a BytecodeCacheData from a uint8_t* contents and size
auto cache_entry = new BytecodeCacheEntry(contents, size)

// Once we have the BytecodeCacheEntry contents:
ScriptCompiler::Source sc_source(source, origin, cache_entry);

// Create compiler
script = ScriptCompiler::Compile( context, &sc_source,
    ScriptCompiler::CompileOptions::kConsumeCodeCache)
    .ToLocalChecked();

// check is script is valid, etc, and run:
auto result = script->Run(context).ToLocalChecked();

Saving the loading andparsing stages that could be quite time costly. The profit is around 30-50% smaller first frame times.

Published by ibon

Chocolate engineer, software eater. Data visualisation at Workday. Past: Platochat, SdkBox, Chukong, Ludei.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: