Here are two examples from Blink. For converting a v8 string to a C++
string, we use this method:
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/core/v8/V8StringResource.cpp?rcl=1466579912&l=101
- it checks whether the v8 string already has an external string resource
(i.e. it's actually already a C++ string), and if not, it externalizes it.

For the other direction, we use a cache from C++ strings to v8 strings with
external string resources:
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/bindings/core/v8/V8ValueCache.h?rcl=1466579912&l=94

Having said that, we also observe certain patterns where string conversion
shows up high in the profile, and we're working on improving this over time.

hth
-jochen

On Fri, Jun 17, 2016, 4:46 PM <[email protected]> wrote:

> Many thanks for the suggestions, Jochen.
>
> However, I'm not sure that this resolves the issue at the Node.js/V8
> interface.  I did read up on External String resources a while back.  The
> problem, as I understand it, is that strings passed into a C++ method via
> JavaScript will be bona fide JavaScript strings over which I have no
> control.  Besides, I'm not sure how you would create an External String
> resource in the JavaScript environment anyway.  The reverse is true on the
> output side: the JavaScript environment will be expecting the C++ method to
> create a bona fide JavaScript string to pass out as the return value.
>
> Of course feel free to correct my analysis (preferably with an example or
> two!) if there is a way to utilize some other string handling methodology
> which doesn't merely push the same performance issue into a different part
> of the system.
>
> *** I'm still hopeful that the V8 Development team might take a look at
> this as the performance of the standard string handling functions at the
> JS/C++ interface is absolutely dreadful (as my benchmarks show) and will
> negatively impact on anyone creating C++ based add-ons to what is, an
> otherwise, brilliant JS engine. ***
>
> Thanks again,
>
> Chris.
>
>
>
> On Friday, 10 June 2016 09:54:45 UTC+1, Jochen Eisinger wrote:
>
>> Thanks for the detailed analysis, very interesting. Some comments inline:
>>
>> On Fri, Jun 10, 2016 at 10:45 AM <[email protected]> wrote:
>>
>>> Hello,
>>>
>>> * This post is mainly for the attention of any V8 Developers who might
>>> be monitoring this group.  However, all thoughts and suggestions are
>>> welcome!
>>>
>>> First some background.  I'm development an interface to a database
>>> specifically for the Node.js environment.  The database in question can
>>> operate as an embedded entity, so I am using the published Node.js/V8 API
>>> to communicate with it via its own C API.  I acknowledge that this is a
>>> slightly unusual approach in that the interface between JavaScript and
>>> other databases is often created over TCP infrastructure. However, with the
>>> embedded architecture there is, quite understandably, an expectation of
>>> high performance - certainly much higher than what could otherwise be
>>> achieved over TCP.
>>>
>>> The requirement for high performance has led me to spend some time
>>> analyzing the throughput of various aspects of the V8 API.  As a result, I
>>> have found that there is a particular problem/bottleneck in marshaling
>>> string based data between the JavaScript environment and C/C++ - which of
>>> course is an essential part of establishing close-coupled lines of
>>> communication at this level.
>>>
>>> The following simple benchmark illustrates this performance issue.
>>> Basically, I use the following simple Node.js/JavaScript code to call the
>>> 'db.benchmark()' method 1000000000 times and record the time taken.
>>> Although the code implies that a connection to the database is made, no
>>> database is used, or even loaded, in these tests.  The source code to the
>>> various incarnations of the 'db.benchmark()' method are included together
>>> with the timing results obtained.
>>>
>>> JavaScript Benchmark Code:
>>>
>>> var my_database = require('db_api');
>>> var db = new my_database.db_api();
>>> var max = 1000000000;
>>> var d1 = new Date();
>>> var d1_ms = d1.getTime()
>>> console.log("d1: " + d1.toISOString());
>>> for (n = 0; n < max; n ++) {
>>>    db.benchmark("Input String");
>>> }
>>> var d2 = new Date();
>>> var d2_ms = d2.getTime()
>>> var diff = Math.abs(d1_ms - d2_ms)
>>> console.log("\nd2: " + d2.toISOString());
>>> console.log("diff: " + diff + " secs: " + (diff / 1000));
>>>
>>>
>>> First, let’s create a baseline by removing the benchmark from the
>>> JavaScript code …
>>>
>>>    //db.benchmark("Input String");
>>>
>>> Results:
>>> d1: 2016-05-16T09:55:01.919Z
>>> d2: 2016-05-16T09:55:06.589Z
>>> diff: 4670 secs: 4.67
>>>
>>>
>>> Now let’s create a second baseline by adding a benchmark call that does
>>> absolutely nothing.  The JavaScript call 'db.benchmark("Input String")' is
>>> reinstated but the C++ code of the benchmark method does absolutely nothing
>>> ...
>>>
>>>    static void benchmark(const FunctionCallbackInfo<Value>& args)
>>>    {
>>>
>>>       // interact with a database via its C API
>>>
>>>       return;
>>>    }
>>>
>>> Results:
>>> d1: 2016-05-16T09:59:18.915Z
>>> d2: 2016-05-16T09:59:38.933Z
>>> diff: 20018 secs: 20.018
>>>
>>> This tells us that calling a C/C++ method/function that does absolutely
>>> nothing (no inputs or outputs to process) is moderately expensive on its
>>> own.
>>>
>>>
>>> Next, let’s accept a single string argument and copy it to a C character
>>> buffer for use at the database API …
>>>
>>>    static void Benchmark(const FunctionCallbackInfo<Value>& args)
>>>    {
>>>       char c_input[256];
>>>       Local<String> input = args[0]->ToString();
>>>       input->WriteUtf8(c_input);
>>>
>>>       // interact with a database via its C API
>>>
>>>       return;
>>>    }
>>>
>>>
>>> Results:
>>> d1: 2016-05-16T10:02:54.355Z
>>> d2: 2016-05-16T10:04:26.832Z
>>> diff: 92477 secs: 92.477
>>>
>>> We see a significant performance hit in simply marshaling a simple JS
>>> string into a C buffer.
>>>
>>>
>>> Next, let’s generate an output for JavaScript derived from a string held
>>> in a C buffer …
>>>
>>>    static void benchmark(const FunctionCallbackInfo<Value>& args)
>>>    {
>>>       char c_output[256];
>>>       Isolate* isolate = args.GetIsolate();
>>>       HandleScope scope(isolate);
>>>
>>>       // interact with a database via its C API
>>>
>>>       strcpy(c_output, "Output String");
>>>       Local<String> output = String::NewFromUtf8(isolate, c_output);
>>>       args.GetReturnValue().Set(output);
>>>       return;
>>>    }
>>>
>>> Results:
>>> d1: 2016-05-16T10:14:30.399Z
>>> d2: 2016-05-16T10:17:16.905Z
>>> diff: 166506 secs: 166.506
>>>
>>> Creating a JavaScript string resource from a C buffer is also expensive.
>>>
>>>
>>> Finally, let's put it all together by accepting a string argument and
>>> returning a string output …
>>>
>>>    static void benchmark(const FunctionCallbackInfo<Value>& args)
>>>    {
>>>       char c_input[256], c_output[256];
>>>       Isolate* isolate = args.GetIsolate();
>>>       HandleScope scope(isolate);
>>>       Local<String> input = args[0]->ToString();
>>>       input->WriteUtf8(c_input);
>>>
>>>       // interact with a database via its C API
>>>
>>>       strcpy(c_output, "Output String");
>>>       Local<String> output = String::NewFromUtf8(isolate, c_output);
>>>       args.GetReturnValue().Set(output);
>>>       return;
>>>    }
>>>
>>> Results:
>>> d1: 2016-05-16T10:28:47.164Z
>>> d2: 2016-05-16T10:32:58.709Z
>>> diff: 251545 secs: 251.545
>>>
>>> This last experiment is fairly representative of the 'real world':
>>> string based data sent to the database (update operations) and string based
>>> data returned (retrieval operations).
>>>
>>>
>>> As you can see, there is a significant cost in marshaling data between C
>>> string buffers and internal V8 string data types/constructs (and vice
>>> versa).  Is there an alternative way of doing this?  On the input side,
>>> simply getting a pointer to the raw input data would probably work fine for
>>> the purpose of interacting with an outgoing C API.  Likewise, on the output
>>> side, is there a faster way to generate V8 strings from C character buffers?
>>>
>>
>> You can get a pointer to the underlying string using v8::String::Value.
>> Note that the string might have the be flattened first, and it's not safe
>> to store the pointer across calls to v8.
>>
>> In the other direction, you can implement a
>> v8::String::ExternalStringResource to expose a string to v8 without copying
>> it.
>>
>>
>>
>
>>> Alternatively, are there plans to improve the performance of the
>>> existing functionality?
>>>
>>> When developing this software I was expecting calls to the database
>>> (particularly update operations) to be the rate limiting part.  However,
>>> differential benchmarks have demonstrated that operations on the database
>>> are, on their own, several orders of magnitude faster than the mechanics of
>>> marshaling data between the V8/JavaScript and C/C++ environment.  This was
>>> a bit of a surprise to be honest.  Finally, for the curious, what's the
>>> database?  It's InterSystems Caché.
>>>
>>> Many thanks for reading this and thanks in advance for any thoughts or
>>> suggestions!
>>>
>>> Chris.
>>>
>>> Director
>>> M/Gateway Developments Ltd
>>> http://www.mgateway.com
>>>
>>> --
>>> --
>>> v8-dev mailing list
>>>
>> [email protected]
>>
>>
>>> http://groups.google.com/group/v8-dev
>>> ---
>>> You received this message because you are subscribed to the Google
>>> Groups "v8-dev" group.
>>>
>> To unsubscribe from this group and stop receiving emails from it, send an
>>> email to [email protected].
>>
>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>> --
> --
> v8-dev mailing list
> [email protected]
> http://groups.google.com/group/v8-dev
> ---
> You received this message because you are subscribed to the Google Groups
> "v8-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
-- 
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- 
You received this message because you are subscribed to the Google Groups 
"v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to