fiber stored as register variable

and the benchmark script refactored, and now it will generate an
html report
This commit is contained in:
Thakee Nathees 2022-04-14 07:09:34 +05:30
parent 9d6d37fa09
commit b285336895
10 changed files with 305 additions and 181 deletions

4
.gitignore vendored
View File

@ -6,6 +6,10 @@
.DS_Store .DS_Store
build/ build/
tests/benchmarks/report.html
docs/build/
docs/static/wasm/
# Prerequisites # Prerequisites
*.d *.d

3
docs/.gitignore vendored
View File

@ -1,3 +0,0 @@
build/
static/wasm/

View File

@ -677,14 +677,14 @@ static void reportError(PKVM* vm) {
* RUNTIME * * RUNTIME *
*****************************************************************************/ *****************************************************************************/
static PkResult runFiber(PKVM* vm, Fiber* fiber) { static PkResult runFiber(PKVM* vm, Fiber* fiber_) {
// Set the fiber as the vm's current fiber (another root object) to prevent // Set the fiber as the vm's current fiber (another root object) to prevent
// it from garbage collection and get the reference from native functions. // it from garbage collection and get the reference from native functions.
vm->fiber = fiber; vm->fiber = fiber_;
ASSERT(fiber->state == FIBER_NEW || fiber->state == FIBER_YIELDED, OOPS); ASSERT(fiber_->state == FIBER_NEW || fiber_->state == FIBER_YIELDED, OOPS);
fiber->state = FIBER_RUNNING; fiber_->state = FIBER_RUNNING;
// The instruction pointer. // The instruction pointer.
register const uint8_t* ip; register const uint8_t* ip;
@ -692,21 +692,22 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
register Var* rbp; //< Stack base pointer register. register Var* rbp; //< Stack base pointer register.
register CallFrame* frame; //< Current call frame. register CallFrame* frame; //< Current call frame.
register Module* module; //< Currently executing module. register Module* module; //< Currently executing module.
register Fiber* fiber = fiber_;
#if DEBUG #if DEBUG
#define PUSH(value) \ #define PUSH(value) \
do { \ do { \
ASSERT(vm->fiber->sp < (vm->fiber->stack + (vm->fiber->stack_size - 1)), \ ASSERT(fiber->sp < (fiber->stack + (fiber->stack_size - 1)), \
OOPS); \ OOPS); \
(*vm->fiber->sp++ = (value)); \ (*fiber->sp++ = (value)); \
} while (false) } while (false)
#else #else
#define PUSH(value) (*vm->fiber->sp++ = (value)) #define PUSH(value) (*fiber->sp++ = (value))
#endif #endif
#define POP() (*(--vm->fiber->sp)) #define POP() (*(--fiber->sp))
#define DROP() (--vm->fiber->sp) #define DROP() (--fiber->sp)
#define PEEK(off) (*(vm->fiber->sp + (off))) #define PEEK(off) (*(fiber->sp + (off)))
#define READ_BYTE() (*ip++) #define READ_BYTE() (*ip++)
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1])) #define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
@ -714,11 +715,12 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// done with the fiber or aborting it for runtime errors. // done with the fiber or aborting it for runtime errors.
#define FIBER_SWITCH_BACK() \ #define FIBER_SWITCH_BACK() \
do { \ do { \
Fiber* caller = vm->fiber->caller; \ Fiber* caller = fiber->caller; \
ASSERT(caller == NULL || caller->state == FIBER_RUNNING, OOPS); \ ASSERT(caller == NULL || caller->state == FIBER_RUNNING, OOPS); \
vm->fiber->state = FIBER_DONE; \ fiber->state = FIBER_DONE; \
vm->fiber->caller = NULL; \ fiber->caller = NULL; \
vm->fiber = caller; \ fiber = caller; \
vm->fiber = fiber; \
} while (false) } while (false)
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR. // Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
@ -746,7 +748,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// function. // function.
#define LOAD_FRAME() \ #define LOAD_FRAME() \
do { \ do { \
frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \ frame = &fiber->frames[fiber->frame_count-1]; \
ip = frame->ip; \ ip = frame->ip; \
rbp = frame->rbp; \ rbp = frame->rbp; \
module = frame->closure->fn->owner; \ module = frame->closure->fn->owner; \
@ -807,9 +809,9 @@ L_vm_main_loop:
OPCODE(SWAP): OPCODE(SWAP):
{ {
Var tmp = *(vm->fiber->sp - 1); Var tmp = *(fiber->sp - 1);
*(vm->fiber->sp - 1) = *(vm->fiber->sp - 2); *(fiber->sp - 1) = *(fiber->sp - 2);
*(vm->fiber->sp - 2) = tmp; *(fiber->sp - 2) = tmp;
DISPATCH(); DISPATCH();
} }
@ -979,8 +981,7 @@ L_vm_main_loop:
if (is_immediate) { if (is_immediate) {
// rbp[0] is the return value, rbp + 1 is the first local and so on. // rbp[0] is the return value, rbp + 1 is the first local and so on.
closure->upvalues[i] = captureUpvalue(vm, vm->fiber, closure->upvalues[i] = captureUpvalue(vm, fiber, (rbp + 1 + index));
(rbp + 1 + index));
} else { } else {
// The upvalue is already captured by the current function, reuse it. // The upvalue is already captured by the current function, reuse it.
closure->upvalues[i] = frame->closure->upvalues[index]; closure->upvalues[i] = frame->closure->upvalues[index];
@ -993,7 +994,7 @@ L_vm_main_loop:
OPCODE(CLOSE_UPVALUE): OPCODE(CLOSE_UPVALUE):
{ {
closeUpvalues(vm->fiber, vm->fiber->sp - 1); closeUpvalues(fiber, fiber->sp - 1);
DROP(); DROP();
DISPATCH(); DISPATCH();
} }
@ -1026,7 +1027,7 @@ L_vm_main_loop:
// body of the module, so the main function will return what's at the // body of the module, so the main function will return what's at the
// rbp without modifying it. So at the end of the main function the // rbp without modifying it. So at the end of the main function the
// stack top would be the module itself. // stack top would be the module itself.
Var* module_ret = vm->fiber->sp - 1; Var* module_ret = fiber->sp - 1;
UPDATE_FRAME(); //< Update the current frame's ip. UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, imported->body, module_ret); pushCallFrame(vm, imported->body, module_ret);
@ -1040,11 +1041,7 @@ L_vm_main_loop:
OPCODE(TAIL_CALL): OPCODE(TAIL_CALL):
{ {
const uint8_t argc = READ_BYTE(); const uint8_t argc = READ_BYTE();
Var* callable = fiber->sp - argc - 1;
// The call might change the vm->fiber so we need the reference to the
// fiber that actually called the function.
Fiber* call_fiber = vm->fiber;
Var* callable = call_fiber->sp - argc - 1;
const Closure* closure = NULL; const Closure* closure = NULL;
@ -1076,8 +1073,8 @@ L_vm_main_loop:
} }
// Next call frame starts here. (including return value). // Next call frame starts here. (including return value).
call_fiber->ret = callable; fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null. *(fiber->ret) = VAR_NULL; //< Set the return value to null.
if (closure->fn->is_native) { if (closure->fn->is_native) {
@ -1095,13 +1092,18 @@ L_vm_main_loop:
// would be null if we're not running the function with a fiber. // would be null if we're not running the function with a fiber.
if (vm->fiber == NULL) return PK_RESULT_SUCCESS; if (vm->fiber == NULL) return PK_RESULT_SUCCESS;
// Load the top frame to vm's execution variables.
if (vm->fiber != call_fiber) LOAD_FRAME();
// Pop function arguments except for the return value. // Pop function arguments except for the return value.
// Don't use 'vm->fiber' because calling fiber_new() and yield() // Note that calling fiber_new() and yield() would change the
// would change the fiber. // vm->fiber so we're using fiber.
call_fiber->sp = call_fiber->ret + 1; fiber->sp = fiber->ret + 1;
// If the fiber has changed, Load the top frame to vm's execution
// variables.
if (vm->fiber != fiber) {
fiber = vm->fiber;
LOAD_FRAME();
}
CHECK_ERROR(); CHECK_ERROR();
} else { } else {
@ -1145,8 +1147,8 @@ L_vm_main_loop:
// TODO: move this to a function in pk_core.c. // TODO: move this to a function in pk_core.c.
OPCODE(ITER): OPCODE(ITER):
{ {
Var* value = (vm->fiber->sp - 1); Var* value = (fiber->sp - 1);
Var* iterator = (vm->fiber->sp - 2); Var* iterator = (fiber->sp - 2);
Var seq = PEEK(-3); Var seq = PEEK(-3);
uint16_t jump_offset = READ_SHORT(); uint16_t jump_offset = READ_SHORT();
@ -1295,32 +1297,32 @@ L_vm_main_loop:
{ {
// Close all the locals of the current frame. // Close all the locals of the current frame.
closeUpvalues(vm->fiber, rbp + 1); closeUpvalues(fiber, rbp + 1);
// Set the return value. // Set the return value.
Var ret_value = POP(); Var ret_value = POP();
// Pop the last frame, and if no more call frames, we're done with the // Pop the last frame, and if no more call frames, we're done with the
// current fiber. // current fiber.
if (--vm->fiber->frame_count == 0) { if (--fiber->frame_count == 0) {
// TODO: if we're evaluating an expression we need to set it's // TODO: if we're evaluating an expression we need to set it's
// value on the stack. // value on the stack.
//vm->fiber->sp = vm->fiber->stack; ?? //fiber->sp = fiber->stack; ??
FIBER_SWITCH_BACK(); FIBER_SWITCH_BACK();
if (vm->fiber == NULL) { if (fiber == NULL) {
return PK_RESULT_SUCCESS; return PK_RESULT_SUCCESS;
} else { } else {
*vm->fiber->ret = ret_value; *fiber->ret = ret_value;
} }
} else { } else {
*rbp = ret_value; *rbp = ret_value;
// Pop the params (locals should have popped at this point) and update // Pop the params (locals should have popped at this point) and update
// stack pointer. // stack pointer.
vm->fiber->sp = rbp + 1; // +1: rbp is returned value. fiber->sp = rbp + 1; // +1: rbp is returned value.
} }
LOAD_FRAME(); LOAD_FRAME();

View File

@ -1,22 +1,17 @@
#!python #!python
## Copyright (c) 2020-2021 Thakee Nathees ## Copyright (c) 2020-2021 Thakee Nathees
## Copyright (c) 2021-2022 Pocketlang Contributors
## Distributed Under The MIT License ## Distributed Under The MIT License
import subprocess, re, os, sys, platform import re, os, sys
from os.path import join, abspath, dirname, relpath import subprocess, platform
from shutil import which from shutil import which
from os.path import join, abspath, dirname, relpath, exists
## The absolute path of this file, when run as a script. ## The absolute path of this file, when run as a script.
## This file is not intended to be included in other files at the moment. ## This file is not intended to be included in other files at the moment.
THIS_PATH = abspath(dirname(__file__)) THIS_PATH = abspath(dirname(__file__))
## Map from systems to the relative pocket binary path.
SYSTEM_TO_BINARY_PATH = {
"Windows": "..\\..\\build\\release\\bin\\pocket.exe",
"Linux" : "../../build/release/pocket",
"Darwin" : "../../build/release/pocket",
}
## A list of benchmark directories, relative to THIS_PATH ## A list of benchmark directories, relative to THIS_PATH
BENCHMARKS = ( BENCHMARKS = (
"factors", "factors",
@ -26,140 +21,246 @@ BENCHMARKS = (
"primes", "primes",
) )
## Map from file extension to it's interpreter, Will be updated. ## Map the files extension with it's interpreter. (executable, extension).
INTERPRETERS = {} INTERPRETERS = {
'pocketlang' : ('pocket', '.pk'),
'python' : ('python3', '.py'),
'wren' : ('wren', '.wren'),
'ruby' : ('ruby', '.rb'),
'lua' : ('lua', '.lua'),
## Javascript on Node is using V8 that compiles the function before calling it
## which makes it way faster than every other language in this list, we're
## only comparing byte-code interpreted VM languages. Node is the odd one out.
#'javascript' : ('node', '.js'),
}
## Map from systems to the relative pocket binary path.
SYSTEM_TO_POCKET_PATH = {
"current" : {
"Windows": "..\\..\\build\\release\\bin\\pocket.exe",
"Linux" : "../../build/release/pocket",
"Darwin" : "../../build/release/pocket",
},
## This maps the older version of pocket in the system path, to compare
## pocketlang with it's older version.
"older" : {
"Windows": "..\\..\\build\\release\\bin\\pocket_older.exe",
"Linux" : "../../build/release/pocket_older",
"Darwin" : "../../build/release/pocket_older",
}
}
## The html template to display the report.
HTML_TEMPLATE = "template.html"
def main(): def main():
results = dict()
update_interpreters() update_interpreters()
run_all_benchmarsk()
## ----------------------------------------------------------------------------
## RUN ALL BENCHMARKS
## ----------------------------------------------------------------------------
def run_all_benchmarsk():
for benchmark in BENCHMARKS: for benchmark in BENCHMARKS:
print_title(benchmark.title()) print_title(benchmark.title())
dir = join(THIS_PATH, benchmark) for lang in INTERPRETERS:
for file in _source_files(os.listdir(dir)): interp, ext = INTERPRETERS[lang]
file = abspath(join(dir, file)) source = abspath(join(THIS_PATH, benchmark, benchmark + ext))
if not exists(source): continue
ext = get_ext(file) ## File extension.
lang, interp, val = INTERPRETERS[ext]
if not interp: continue
print(" %-10s : "%lang, end=''); sys.stdout.flush() print(" %-10s : "%lang, end=''); sys.stdout.flush()
result = _run_command([interp, file]) result = subprocess.run([interp, source],
time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s',
result.stdout.decode('utf8'),
re.MULTILINE)
if len(time) != 1:
print() # Skip the line.
error_exit(r'elapsed:\s*([0-9\.]+)\s*s --> no mach found.')
print('%.6fs'%float(time[0]))
pass
def _run_command(command):
return subprocess.run(command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
time = get_time(result.stdout.decode('utf8'))
print('%.6fs' % float(time))
## Returns a list of valid source files to run benchmarks. if benchmark not in results:
def _source_files(files): results[benchmark] = []
global INTERPRETERS results[benchmark].append([lang, time])
ret = []
for file in files:
ext = get_ext(file)
if ext not in INTERPRETERS: continue
ret.append(file)
ret.sort(key=lambda f : INTERPRETERS[get_ext(f)][2]) display_results(results)
return ret
## ----------------------------------------------------------------------------
## UPDATE INTERPRETERS
## ----------------------------------------------------------------------------
## Set the pocketlang path for the current system to the compiled output.
def update_interpreters(): def update_interpreters():
pocket = _get_pocket_binary()
python = 'python' if platform.system() == 'Windows' else 'python3'
print_title("CHECKING FOR INTERPRETERS")
global INTERPRETERS
order = 0
INTERPRETERS['.pk'] = _find_interp('pocketlang', pocket, order); order+=1
INTERPRETERS['.wren'] = _find_interp('wren', 'wren', order); order+=1
INTERPRETERS['.py'] = _find_interp('python', python, order); order+=1
INTERPRETERS['.rb'] = _find_interp('ruby', 'ruby', order); order+=1
INTERPRETERS['.lua'] = _find_interp('lua', 'lua', order); order+=1
INTERPRETERS['.js'] = _find_interp('javascript', 'node', order); order+=1
## This will return the path of the pocket binary (on different platforms).
## The debug version of it for enabling the assertions.
def _get_pocket_binary():
system = platform.system() system = platform.system()
if system not in SYSTEM_TO_BINARY_PATH: if system not in SYSTEM_TO_POCKET_PATH['current']:
error_exit("Unsupported platform %s" % system) print("Unsupported platform %s" % system)
sys.exit(1)
pocket = abspath(join(THIS_PATH, SYSTEM_TO_BINARY_PATH[system])) global INTERPRETERS
if not os.path.exists(pocket): pocket = abspath(join(THIS_PATH, SYSTEM_TO_POCKET_PATH['current'][system]))
error_exit("Pocket interpreter not found at: '%s'" % pocket) pocket_older = abspath(join(THIS_PATH, SYSTEM_TO_POCKET_PATH['older'][system]))
if not exists(pocket):
print(f"{colmsg('Error', COL_RED)}: " +
"Pocket interpreter not found at: '%s'" % pocket)
sys.exit(1)
INTERPRETERS['pocketlang'] = (pocket, '.pk')
return pocket ## Add if older version of pocketlang if exists.
if exists(pocket_older):
INTERPRETERS['pk-older'] = (pocket_older, '.pk')
## Find and return the interpreter from the path. if 'python' in INTERPRETERS and system == "Windows":
## as (lang, interp, val) tuple, where the val is the additional. INTERPRETERS['python'] = ('python', '.py')
## data related to the interpreter.
def _find_interp(lang, interpreter, val): missing = []
for lang in INTERPRETERS:
interpreter = INTERPRETERS[lang][0]
print('%-27s' % (' Searching for %s ' % lang), end='') print('%-27s' % (' Searching for %s ' % lang), end='')
sys.stdout.flush()
if which(interpreter): if which(interpreter):
print_success('-- found') print(f"-- {colmsg('found', COL_GREEN)}")
return (lang, interpreter, val) else:
print_warning('-- not found (skipped)') print(f"-- {colmsg('missing', COL_YELLOW)}")
return (lang, None, val) missing.append(lang)
for miss in missing:
INTERPRETERS.pop(miss)
## Return the extension from the file name. ## Match 'elapsed: <time>s' from the output of the process and return it.
def get_ext(file_name): def get_time(result_string):
period = file_name.rfind('.'); assert period > 0 time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s', result_string, re.MULTILINE)
return file_name[period:] if len(time) != 1:
print(f'\n\'elapsed: <time>s\' {colmsg("No match found", COL_RED)}.')
sys.exit(1)
return float(time[0])
## Generate a report at 'report.html'.
def display_results(results):
for benchmark in results:
results[benchmark].sort(key=lambda x : x[1])
max_time = 0
for lang, time in results[benchmark]:
max_time = max(max_time, time)
## Add the width for the performance bar.
for entry in results[benchmark]:
time = entry[1]
entry.append(time/max_time * 100)
disp = ""
for benchmark in results:
disp += f'<h1 class="benchmark-title">{benchmark.title()}</h1>\n'
disp += '<table>\n'
disp += '<tbody>\n'
for lang, time, width in results[benchmark]:
class_ = "performance-bar"
if lang == 'pocketlang':
class_ += " pocket-bar"
lang = 'pocket' ## Shorten the name.
disp += '<tr>\n'
disp += f' <th>{lang}</th>\n'
disp += f' <td><div class="{class_}" style="width:{width}%">'
disp += f'{"%.2f"%time}s</div></td>\n'
disp += '</tr>\n'
disp += '</tbody>\n'
disp += '</table>\n'
disp += '\n'
global HTML
html = HTML.replace('{{ REPORT }}', disp)
report_path = abspath(join(THIS_PATH, 'report.html'))
with open(report_path, 'w') as out:
out.write(html)
print()
print(colmsg('Report Generated:', COL_GREEN), report_path)
## ----------------------------------------------------------------------------
## HTML REPORT
## ----------------------------------------------------------------------------
HTML = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-family: sans-serif;
}
#main {
max-width: 600px;
margin: 50px auto;
}
.benchmark-title {
text-align: center;
margin: 40px auto 20px auto;
}
table {
width: 100%;
}
th {
font-weight: normal;
text-align: right;
width: 100px;
}
.performance-bar {
background-color: #1471c8;
margin: 1px 10px;
color:white;
padding: 2px;
}
.pocket-bar {
background-color: #02509B;
}
</style>
</head>
<body>
<div id="main">
{{ REPORT }}
</div>
</body>
</html>
'''
## ---------------------------------------------------------------------------- ## ----------------------------------------------------------------------------
## PRINT FUNCTIONS ## PRINT FUNCTIONS
## ---------------------------------------------------------------------------- ## ----------------------------------------------------------------------------
def _print_sep():
print("-------------------------------------")
def print_title(title): def print_title(title):
print("-----------------------------------") _print_sep()
print(" %s " % title) print(" %s " % title)
print("-----------------------------------") _print_sep()
## ANSI color codes to print messages. ## Simple color logger --------------------------------------------------------
COLORS = { ## https://stackoverflow.com/a/70599663/10846399
'GREEN' : '\u001b[32m',
'YELLOW' : '\033[93m',
'RED' : '\u001b[31m',
'UNDERLINE' : '\033[4m' ,
'END' : '\033[0m' ,
}
## Prints a warning message to stdout. def RGB(red=None, green=None, blue=None,bg=False):
def print_warning(msg): if(bg==False and red!=None and green!=None and blue!=None):
os.system('') ## This will enable ANSI codes in windows terminal. return f'\u001b[38;2;{red};{green};{blue}m'
for line in msg.splitlines(): elif(bg==True and red!=None and green!=None and blue!=None):
print(COLORS['YELLOW'] + line + COLORS['END']) return f'\u001b[48;2;{red};{green};{blue}m'
elif(red==None and green==None and blue==None):
return '\u001b[0m'
COL_NONE = RGB()
COL_RED = RGB(220, 100, 100)
COL_GREEN = RGB(15, 160, 100)
COL_BLUE = RGB(60, 140, 230)
COL_YELLOW = RGB(220, 180, 20)
COL_WHITE = RGB(255, 255, 255)
## print success message to stdout. def colmsg(msg, color):
def print_success(msg): return f"{color}{msg}{COL_NONE}"
os.system('') ## This will enable ANSI codes in windows terminal.
for line in msg.splitlines():
print(COLORS['GREEN'] + line + COLORS['END'])
## prints an error message to stderr and exit if __name__ == "__main__":
## immediately. os.system('')
def error_exit(msg):
os.system('') ## This will enable ANSI codes in windows terminal.
print(COLORS['RED'] + 'Error: ' + msg + COLORS['END'], end='')
sys.exit(1)
if __name__ == '__main__':
main() main()

View File

@ -8,15 +8,13 @@ local function reverse(arr)
end end
end end
local start = os.clock()
local N = 20000000 local N = 20000000
local list = {} local list = {}
for i=1, N do for i=1, N do
list[i] = i list[i] = i
end end
local start = os.clock()
reverse(list) reverse(list)
local seconds = os.clock() - start local seconds = os.clock() - start
print('elapsed: ' .. seconds .. 's') print('elapsed: ' .. seconds .. 's')

View File

@ -12,9 +12,10 @@ def reverse_list(list)
return list return list
end end
start = clock()
N = 20000000 N = 20000000
l = (0..N).as_list l = (0..N).as_list
start = clock()
reverse_list(l) reverse_list(l)
print('elapsed: ', clock() - start, 's') print('elapsed: ', clock() - start, 's')

View File

@ -8,8 +8,9 @@ def reverse_list(list):
list[i], list[last_index] = list[last_index], list[i] list[i], list[last_index] = list[last_index], list[i]
return list return list
start = clock()
N = 20000000 N = 20000000
l = list(range(N)) l = list(range(N))
start = clock()
reverse_list(l) reverse_list(l)
print('elapsed: ', clock() - start, 's') print('elapsed: ', clock() - start, 's')

View File

@ -6,10 +6,9 @@ def reverse_list(list)
end end
end end
start = Time.now
N = 20000000 N = 20000000
list = (0...N).to_a list = (0...N).to_a
reverse_list(list)
start = Time.now
reverse_list(list)
puts "elapsed: " + (Time.now - start).to_s + ' s' puts "elapsed: " + (Time.now - start).to_s + ' s'

View File

@ -0,0 +1,21 @@
var reverse_list = Fn.new { |list|
var count = (list.count / 2).floor
for (i in 0...count) {
var last_index = list.count - i - 1
var last = list[last_index]
list[last_index] = list[i]
list[i] = last
}
return list
}
var N = 20000000
var list = []
for (i in 0...N) {
list.add(i)
}
var start = System.clock
reverse_list.call(list)
System.print("elapsed: %(System.clock - start) s")

View File

@ -4,7 +4,7 @@
from lang import clock from lang import clock
N = 50000 N = 20000
def woo(n, acc) def woo(n, acc)
if n == 0 then return acc end if n == 0 then return acc end