// V8xLua_benchmark.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string.h>
#include <iostream>
// for SetThreadAffinityMask
#include <windows.h>
// for __rdtsc intrinsic
#include <intrin.h>
#pragma warning(disable : 4996) /* strcpy deprecation... argh*/
char username[1024];
int x;
#define DISABLE_PRINTING
#define HOW_MANY_TIMES 100000
static const char *progname = "V8xLua_benchmark";
/*
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
Lua stuff begin here
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
*/
extern "C"{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
static int print_lua(lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tostring(L, -1); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to "
LUA_QL("print"));
#ifndef DISABLE_PRINTING
//if (i>1) fputs("\t", stdout);
fputs(s, stdout);
#endif
lua_pop(L, 1); /* pop result */
}
#ifndef DISABLE_PRINTING
fputs("\n", stdout);
#endif
return 0;
}
static int Plus(lua_State *L) {
int a = luaL_checkint(L,1);
int b = luaL_checkint(L,2);
lua_pushinteger(L,a+b);
return 1;
}
static int SetUsername (lua_State *L) {
size_t l;
char *s = (char*)luaL_checklstring(L, 1, &l);
strcpy((char*)&username,s);
return 1;
}
static int GetUsername(lua_State *L) {
luaL_Buffer b;
const char *e=(char*)&username;
if(!e){
//variable doesnt exists!
lua_pushnil(L);
return 0;
}
luaL_buffinit(L, &b);
luaL_addstring(&b,e);
luaL_pushresult(&b);
return 1;
}
static int SetX(lua_State *L) {
x = luaL_checkint(L,1);
return 1;
}
static int GetX(lua_State *L) {
lua_pushinteger(L,x);
return 1;
}
static void l_message (const char *pname, const char *msg) {
if (pname)
fprintf(stderr, "%s: ", pname);
fprintf(stderr, "%s\n", msg);
fflush(stderr);
}
static int report (lua_State *L, int status) {
if (status && !lua_isnil(L, -1)) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL)
msg = "(error object is not a string)";
l_message(progname, msg);
lua_pop(L, 1);
}
return status;
}
static int traceback (lua_State *L) {
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1);
lua_pushinteger(L, 2);
lua_call(L, 2, 1);
return 1;
}
static int docall (lua_State *L, int narg, int clear) {
int status;
int base = lua_gettop(L) - narg; /* function index */
lua_pushcfunction(L, traceback); /* push traceback function */
lua_insert(L, base); /* put it under chunk and args */
status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
lua_remove(L, base); /* remove traceback function */
/* force a complete garbage collection in case of errors */
if (status != 0)
lua_gc(L, LUA_GCCOLLECT, 0);
return status;
}
static int execute_script (lua_State *L,const char *script_name, int n) {
int status;
status = luaL_loadfile(L,script_name);
if (status == 0)
status = docall(L, 0, 0);
else
lua_pop(L, 0);
return report(L, status);
}
void Run_Lua()
{
lua_State *l;
l = lua_open();
luaL_openlibs(l);
//new functions
lua_register(l, "print_lua", print_lua);
lua_register(l, "plus", Plus);
//Get/Set for string
lua_register(l, "SetUsername", SetUsername);
lua_register(l, "GetUsername", GetUsername);
//Get/Set for integer
lua_register(l, "SetX", SetX);
lua_register(l, "GetX", GetX);
//set x
x = 0;
//set username
strcpy((char*)&username,"John Doe");
#ifndef DISABLE_PRINTING
printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif
unsigned __int64 loadTime = 0, runTime = 0;
unsigned __int64 start, end;
int status;
//execute script
#ifdef DISABLE_PRINTING
for(int i=0;i<HOW_MANY_TIMES;i++)
#endif
{
start = __rdtsc();
status = luaL_loadfile(l,"script.lua");
end = __rdtsc();
if (status != 0)
{
lua_pop(l, 0);
printf ("Error loading file\n");
break;
}
loadTime += end - start;
start = __rdtsc();
status = docall(l, 0, 0);
end = __rdtsc();
runTime += end - start;
report(l, status);
}
std::cout << "Lua load time avg: " << loadTime / HOW_MANY_TIMES << std::endl;
std::cout << "Lua run time avg: " << runTime / HOW_MANY_TIMES << std::endl;
std::cout << "Lua total time avg: " << (loadTime + runTime) / HOW_MANY_TIMES << std::endl << std::endl;
#ifndef DISABLE_PRINTING
//check if my username changed...
if(strcmp((char*)&username,"John Doe")!=0)
{
printf("################### AFTER SCRIPT EXECUTION ###################\n");
printf("Script changed my username to %s\n",(char*)&username);
printf("x value after script [%d]\n",x);
}
#endif
//Remember to destroy the Lua State
lua_close(l);
}
/*
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
V8 stuff begin here
###################################################################################################
###################################################################################################
###################################################################################################
###################################################################################################
*/
#include <v8.h>
v8::Handle<v8::ObjectTemplate> global;
v8::Handle<v8::Context> context;
//get the value of x variable inside javascript
static v8::Handle<v8::Value> XGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) {
return v8::Number::New(x);
}
//set the value of x variable inside javascript
static void XSetter(v8::Local<v8::String> name,v8::Local<v8::Value> value,const v8::AccessorInfo& info) {
x = value->Int32Value();
}
//get the value of username variable inside javascript
v8::Handle<v8::Value> userGetter(v8::Local<v8::String> name,const v8::AccessorInfo& info) {
return v8::String::New((char*)&username,strlen((char*)&username));
}
//set the value of username variable inside javascript
void userSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value,const v8::AccessorInfo& info) {
v8::Local<v8::String> s = value->ToString();
s->WriteAscii((char*)&username);
}
//convert unsigned int to value
static v8::Local<v8::Value> v8_uint32(unsigned int x) {
return v8::Uint32::New(x);
}
// Add two numbers
v8::Handle<v8::Value> Plus(const v8::Arguments& args)
{
unsigned int A = args[0]->Uint32Value();
unsigned int B = args[1]->Uint32Value();
return v8_uint32(A + B);
}
// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called. Prints its arguments on stdout separated by
// spaces and ending with a newline.
v8::Handle<v8::Value> Print(const v8::Arguments& args) {
bool first = true;
for (int i = 0; i < args.Length(); i++)
{
v8::HandleScope handle_scope;
if (first)
{
first = false;
}
else
{
#ifndef DISABLE_PRINTING
printf(" ");
#endif
}
//convert the args[i] type to normal char* string
v8::String::AsciiValue str(args[i]);
#ifndef DISABLE_PRINTING
printf("%s", *str);
#endif
}
#ifndef DISABLE_PRINTING
printf("\n");
#endif
//returning Undefined is the same as returning void...
return v8::Undefined();
}
// Executes a string within the current v8 context.
bool ExecuteString(v8::Handle<v8::String> source,v8::Handle<v8::String> name) {
unsigned __int64 loadTime = 0, runTime = 0;
unsigned __int64 start, end;
v8::HandleScope handle_scope;
//access global context within this scope
v8::Context::Scope context_scope(context);
v8::Handle<v8::Value> result;
//exception handler
v8::TryCatch try_catch;
#ifdef DISABLE_PRINTING
for(int i=0;i<HOW_MANY_TIMES;i++)
{
#endif
start = __rdtsc();
//compile script to binary code - JIT
v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
end = __rdtsc();
loadTime += end - start;
//check if we got problems on compilation
if (script.IsEmpty()) {
// Print errors that happened during compilation.
v8::String::AsciiValue error(try_catch.Exception());
printf("%s\n", *error);
return false;
}
else
{
start = __rdtsc();
//no errors , let's continue
result = script->Run();
end = __rdtsc();
runTime += end - start;
}
#ifdef DISABLE_PRINTING
}
#endif
std::cout << "V8 load time avg: " << loadTime / HOW_MANY_TIMES << std::endl;
std::cout << "V8 run time avg: " << runTime / HOW_MANY_TIMES << std::endl;
std::cout << "V8 total time avg: " << (loadTime + runTime) / HOW_MANY_TIMES << std::endl << std::endl;
//check if execution ended with errors
if(result.IsEmpty())
{
// Print errors that happened during execution.
v8::String::AsciiValue error(try_catch.Exception());
printf("%s\n", *error);
return false;
}
else
{
if (!result->IsUndefined())
{
// If all went well and the result wasn't undefined then print
// the returned value.
v8::String::AsciiValue str(result);
printf("script result [%s]\n", *str);
}
return true;
}
}
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
char *ReadFile(const char *script_name)
{
char *str_buff;
struct _stat stat_buffer;
size_t file_size,numread;
FILE *input;
if(_stat(script_name,&stat_buffer)!=0)
{
return NULL;
}
input = fopen(script_name, "r");
file_size=stat_buffer.st_size;
str_buff=(char*)malloc(file_size+1);
numread = fread((char*)str_buff,sizeof(char),file_size,input);
str_buff[numread]='\0';
if(numread<=0)
{
free(str_buff);
return NULL;
}
fclose(input);
return str_buff;
}
void Run_V8()
{
v8::HandleScope handle_scope;
char *script_name = "script.js";
char *script = ReadFile(script_name);
if(!script){
printf("Unable to execute script.js");
return;
}
//convert the string with the script to a v8 string
v8::Handle<v8::String> source = v8::String::New(script, strlen(script));
//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
v8::Handle<v8::String> name = v8::String::New(script_name,strlen(script_name));
// Create a template for the global object.
global = v8::ObjectTemplate::New();
//associates "plus" on script to the Plus function
global->Set(v8::String::New("plus"), v8::FunctionTemplate::New(Plus));
//associates "print" on script to the Print function
global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
//create accessor for string username
global->SetAccessor(v8::String::New("user"),userGetter,userSetter);
//create accessor for integer x
global->SetAccessor(v8::String::New("x"), XGetter, XSetter);
//create context for the script
context = v8::Context::New(NULL, global);
//fill username with something
strcpy((char*)&username,"John Doe");
//x initialization
x=0;
#ifndef DISABLE_PRINTING
printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif
ExecuteString(source,name);
//check if my username changed...
if(strcmp((char*)&username,"John Doe")!=0)
{
#ifndef DISABLE_PRINTING
printf("################### AFTER SCRIPT EXECUTION ###################\n");
printf("Script changed my username to %s\n",(char*)&username);
printf("x value after script [%d]\n",x);
#endif
}
free(script);
}
int plus(int a,int b)
{
return a+b;
}
void execute_c()
{
int local;
#ifndef DISABLE_PRINTING
printf("begin script");
printf("script executed by %s\n",(char*)&username);
#endif
if ( strcmp((char*)&username,"John Doe")==0){
#ifndef DISABLE_PRINTING
printf("\tuser name is invalid. Changing name to Chuck Norris\n");
#endif
strcpy((char*)&username,"Chuck Norris");
}
local = plus(123,27);
#ifndef DISABLE_PRINTING
printf("123 plus 27 = %d\n",local );
#endif
x = plus(3456789,6543211);
#ifndef DISABLE_PRINTING
printf("end script\n");
#endif
}
//same script functionality done in c... just to have a base time
void Run_C()
{
strcpy((char*)&username,"John Doe");
#ifndef DISABLE_PRINTING
printf("################### BEFORE SCRIPT EXECUTION ###################\n");
#endif
unsigned __int64 runTime = 0;
unsigned __int64 start, end;
//simple javascritp to test
#ifdef DISABLE_PRINTING
for(int i=0;i<HOW_MANY_TIMES;i++)
#endif
{
start = __rdtsc();
execute_c();
end = __rdtsc();
runTime += end - start;
}
std::cout << "C run time avg: " << runTime / HOW_MANY_TIMES << std::endl;
if(strcmp((char*)&username,"John Doe")!=0)
{
#ifndef DISABLE_PRINTING
printf("################### AFTER SCRIPT EXECUTION ###################\n");
printf("Script changed my username to %s\n",(char*)&username);
printf("x value after script [%d]\n",x);
#endif
}
}
/*
How to use:
define DISABLE_PRINTING to enable benchmark and
undefine DISABLE_PRINTING to enable script with 1 normal execution to check output
*/
int _tmain(int argc, _TCHAR* argv[])
{
// though on my dual core system I see no difference
// let's force thread to run on one CPU to be confident in rdtsc results
DWORD_PTR mask = 1;
DWORD result = SetThreadAffinityMask(GetCurrentThread(), mask);
if (!result)
{
std::cout << "Unable to set affinity mask" << std::endl;
}
Run_Lua();
Run_V8();
Run_C();
return 0;
}