calling Javascript function from C++,

406 views
Skip to first unread message

Ben N

unread,
Oct 29, 2015, 6:07:34 AM10/29/15
to v8-users
Hi

I am just starting out with V8 and I'm slowly finding my way round it but I have run into a problem. I'm sorry if this is a common question or if the answer is obvious, but I have spent some time trying to Google it and I haven't found a satisfactory explanation. 

The problem I have concerns the speed of calling a JavaScript function from C++  which is very very slow. Compared to calling the same function from JavaScript I"m finding it roughly 400 times slower. I've created a simple function which highlights this speed difference and the code is below.

Any advice on how fix this speed issue  would be very much appreciated.  I'm developing on a Mac and Xcode by the way. 

thank you and apologies if I'm asking or doing anything dumb!

b e n


This is what I have on the JavaScript side:

var raycast_count ;
var raytracer = new kraytracer() ;

raytracer
.oncastray = function( x, y )
{
   
++ raycast_count ;
 
return true ;
}

// this loop takes about 0.03 seconds to run
raycast_count
= 0 ;
for ( var j = 0 ; j < 1980 ; ++j )
   
for ( var i = 0 ; i < 1440 ; ++ i )
     raytracer
.oncastray( i, j ) ;


// raytracer.begin() is C++ function that performs the same loop above.
// It takes 13 seconds though!
raycast_count
= 0 ;
raytracer
.begin() ;



and on the C++ side I have a callback for raytracer.begin() which looks roughly like this,

void v8raytracer::begin( const FunctionCallbackInfo<Value> & info )
{
 
...
 
...

Local<Value> oncastray = info.This()->Get( String::NewFromUtf8( isolate, "oncastray" ) ) ;
 Handle<Value> parameters[ 2 ] ;
 Local<Value> rcv = info.This() ;
               
 if ( oncastray->IsFunction() )
 {
   Local<Function> oncastray_fn = Handle<Function>::Cast( oncastray ) ;
 
   
for ( int j = 0 ; j < 1980 ; ++j )
     
for ( int i = 0 ; i < 1440 ; ++i )
     
{
          parameters[ 0 ] = Integer::New( isolate, x ) ;
          parameters[ 1 ] = Integer::New( isolate, y ) ;
                                                                                         
           oncastray_fn->Call( rcv, 2, parameters ) ;
      }
  }
 ...
 
...
}

Ben N

unread,
Oct 29, 2015, 7:43:46 AM10/29/15
to v8-users
Oops, I meant to type getTime() instead of getMilliseconds().


b e n

Ben Noordhuis

unread,
Oct 29, 2015, 7:44:29 AM10/29/15
to v8-u...@googlegroups.com
The short answer is to keep the number of C++ -> JS transitions to a
minimum. If you look at the call stack in a debugger, you'll see why
it's so much slower: V8 has to insert a couple of adapter frames that
convert between the JS and C++ calling conventions. (Also, it can't
inline the function body like it does for hot JS functions.)

In most cases, the easiest solution is to batch arguments: instead of
calling a native function ten times with one argument, call it one
time with ten arguments. Look into using typed arrays for passing
data, they're cheaper to inspect in C++ land than regular JS arrays;
v8::Array::Get() is quite slow.

Ben N

unread,
Oct 29, 2015, 8:25:40 AM10/29/15
to v8-users
Hi Ben

I ran some more experiments and I found a memory leak problem which I think was contributing to the slowness. Fixed by adding a HandleScope.


{
 
HandleScope scope( isolate ) ;

 parameters
[ 0 ] = Integer::New( isolate, x ) ;
 parameters
[ 1 ] = Integer::New( isolate, y ) ;

 
return oncastray_fn->Call( rcv, 2, parameters )->ToBoolean()->Value() ;
}



The call is still slow but it is much better now. I will take your advice and learn how to use typed Arrays. Many thanks for this tip.


b e n




Reply all
Reply to author
Forward
0 new messages