From ff0babb8a78d3d180b73df537a18816f907c096d Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Mon, 31 May 2021 05:31:41 +0530 Subject: [PATCH] readme updated with preformance benchmarks --- README.md | 33 ++++++++++++-- src/core.c | 42 ++++++++++++++++- src/var.c | 18 ++++++++ test/benchmark/factors/factors.pk | 2 - test/benchmark/fib/fib.pk | 3 +- test/benchmark/fib/fib.py | 2 +- test/benchmark/fib/fib.rb | 2 +- test/benchmark/fib/fib.wren | 5 +-- test/benchmark/list_reverse/list_reverse.pk | 4 +- test/benchmark/list_reverse/list_reverse.py | 2 +- test/benchmark/loop/loop.pk | 13 ++---- test/benchmark/loop/loop.wren | 2 +- test/benchmark/primes/primes.pk | 3 +- test/benchmark/primes/primes.py | 2 +- test/benchmark/primes/primes.rb | 2 +- test/benchmark/primes/primes.wren | 4 +- test/examples/fib.pk | 18 ++++---- test/examples/fizzbuzz.pk | 9 ++++ test/examples/helloworld.pk | 1 + test/examples/palette.pk | 19 ++++++++ test/examples/pi.pk | 17 +++++++ test/examples/prime.pk | 12 ++--- test/run.py | 50 ++++++++++++++++++--- 23 files changed, 210 insertions(+), 55 deletions(-) create mode 100644 test/examples/fizzbuzz.pk create mode 100644 test/examples/helloworld.pk create mode 100644 test/examples/palette.pk create mode 100644 test/examples/pi.pk diff --git a/README.md b/README.md index 9db0731..291e8ee 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ -# PocketLang

-**PocketLang** is a small (~3000 semicollons) and fast programming language written in C. It's syntactically similar to Ruby and it can be learned in less than an hour. Including the compiler, bytecode VM and runtime, it's a standalone executable with zero external dependecies just as it's self descriptive name. The pocketlang VM can be embedded in another hosting program very easily, as static/shared library or a generated single header version of the source, which makes it even effortless. +**Pocketlang** is a small (~3000 semicollons) and fast functional programming language written in C. It's syntactically similar to Ruby and it can be learned in less than an hour. Including the compiler, bytecode VM and runtime, it's a standalone executable with zero external dependecies just as it's self descriptive name. The pocketlang VM can be embedded in another hosting program very easily. The language is written using [Wren Language](https://wren.io/) and their wonderful book [craftinginterpreters](http://www.craftinginterpreters.com/) as a reference. -### What PocketLang looks like +### What pocketlang looks like ```ruby # Python like import statement. @@ -26,3 +25,31 @@ for i in 0..10 end ``` +## Performance! + +The source files used to run benchmarks could be found at `test/benchmarks/` directory. They were ran using a small python script in the test directory. +- PC: ASUS N552VX, Intel Core i7-6700HQ 2.6GHz, 12GB SODIMM Ram +- Lnaguages: pocketlang (pre-alpha), wren v0.3.0, python v3.7.4, ruby v2.7.2, + +![preformance2](https://user-images.githubusercontent.com/41085900/120123257-6f043280-c1cb-11eb-8c20-a42153268a0f.png) + + +When it comes to loops pocketlang much faster (even faster than [wren](https://wren.io/), the language I'm using as a reference) because we have dedicated opcode instructions for iterations and calling builtin functions. And it's is a functional language, which makes is possible to avoid object orianted/ class instance creation overhead. + + +## Building From Source + +It can be build from source easily without any depencency, requirenments or even a build system, just a c99 compatible compiler is enough. It can be compiled with the following command. + +#### gcc/mingw +``` +gcc -o pocket cli/*.c src/*.c -Isrc/include -lm -Wno-int-to-pointer-cast +``` + +#### msvc +``` +cl /Fepocket cli/*.c src/*.c /Isrc/include && rm *.obj +``` + +For other compilers it's the same (If you weren't able to compile it, please report by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new)). In addition you can use some of our build scripts (Makefile, batch script for MSVC, SCons) in the `build/` directory. For more see [build from source docs](https://thakeenathees.github.io/pocketlang/Getting%20Started/build%20from%20source.html). + diff --git a/src/core.c b/src/core.c index 8e585a6..9b045cc 100644 --- a/src/core.c +++ b/src/core.c @@ -557,6 +557,37 @@ void stdMathCeil(PKVM* vm) { RET(VAR_NUM(ceil(num))); } +void stdMathPow(PKVM* vm) { + double num, ex; + if (!validateNumeric(vm, ARG1, &num, "Parameter 1")) return; + if (!validateNumeric(vm, ARG2, &ex, "Parameter 2")) return; + + RET(VAR_NUM(pow(num, ex))); +} + +void stdMathSqrt(PKVM* vm) { + double num; + if (!validateNumeric(vm, ARG1, &num, "Parameter 1")) return; + + RET(VAR_NUM(sqrt(num))); +} + +void stdMathAbs(PKVM* vm) { + double num; + if (!validateNumeric(vm, ARG1, &num, "Parameter 1")) return; + if (num < 0) num = -num; + RET(VAR_NUM(num)); +} + +void stdMathSign(PKVM* vm) { + double num; + if (!validateNumeric(vm, ARG1, &num, "Parameter 1")) return; + if (num < 0) num = -1; + else if (num > 0) num = +1; + else num = 0; + RET(VAR_NUM(num)); +} + PK_DOC(stdMathHash, "hash(value:var) -> num\n" "Return the hash value of the variable, if it's not hashable it'll " @@ -623,6 +654,10 @@ void initializeCore(PKVM* vm) { Script* math = newModuleInternal(vm, "math"); moduleAddFunctionInternal(vm, math, "floor", stdMathFloor, 1); moduleAddFunctionInternal(vm, math, "ceil", stdMathCeil, 1); + moduleAddFunctionInternal(vm, math, "pow", stdMathPow, 2); + moduleAddFunctionInternal(vm, math, "sqrt", stdMathSqrt, 1); + moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1); + moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1); moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1); } @@ -656,7 +691,12 @@ Var varAdd(PKVM* vm, Var v1, Var v2) { } break; case OBJ_LIST: - TODO; + { + if (o2->type == OBJ_LIST) { + TODO; + } + } + TODO; case OBJ_MAP: case OBJ_RANGE: diff --git a/src/var.c b/src/var.c index 695e9cf..c29f700 100644 --- a/src/var.c +++ b/src/var.c @@ -759,6 +759,24 @@ bool isValuesEqual(Var v1, Var v2) { memcmp(s1->data, s2->data, s1->length) == 0; } + case OBJ_LIST: { + /* + * l1 = []; list_append(l1, l1) # [[...]] + * l2 = []; list_append(l2, l2) # [[...]] + * l1 == l2 ## This will cause a stack overflow but not handling that + * ## (in python too). + */ + List *l1 = (List*)o1, *l2 = (List*)o2; + if (l1->elements.count != l2->elements.count) return false; + Var* v1 = l1->elements.data; + Var* v2 = l2->elements.data; + for (uint32_t i = 0; i < l1->elements.count; i++) { + if (!isValuesEqual(*v1, *v2)) return false; + v1++, v2++; + } + return true; + } + default: return false; } diff --git a/test/benchmark/factors/factors.pk b/test/benchmark/factors/factors.pk index df66ef2..3e0782b 100644 --- a/test/benchmark/factors/factors.pk +++ b/test/benchmark/factors/factors.pk @@ -1,12 +1,10 @@ from lang import clock start = clock() - N = 50000000; factors = [] for i in 1..N+1 if N % i == 0 list_append(factors, i) end end - print("elapsed:", clock() - start, 's') diff --git a/test/benchmark/fib/fib.pk b/test/benchmark/fib/fib.pk index 64c0853..76417b4 100644 --- a/test/benchmark/fib/fib.pk +++ b/test/benchmark/fib/fib.pk @@ -8,8 +8,7 @@ end start = clock() for i in 0..10 - print(fib(32)) + print(fib(30)) end - print('elapsed:', clock() - start, 's') diff --git a/test/benchmark/fib/fib.py b/test/benchmark/fib/fib.py index 7869ad0..0f5e79d 100644 --- a/test/benchmark/fib/fib.py +++ b/test/benchmark/fib/fib.py @@ -6,5 +6,5 @@ def fib(n): start = clock() for i in range(0, 10): - print(fib(32)) + print(fib(30)) print("elapsed: ", clock() - start, 's') diff --git a/test/benchmark/fib/fib.rb b/test/benchmark/fib/fib.rb index 20d8303..5582f70 100644 --- a/test/benchmark/fib/fib.rb +++ b/test/benchmark/fib/fib.rb @@ -9,6 +9,6 @@ end start = Time.now for i in 0...10 - puts fib(32) + puts fib(30) end puts "elapsed: " + (Time.now - start).to_s + ' s' diff --git a/test/benchmark/fib/fib.wren b/test/benchmark/fib/fib.wren index e4cae63..e16efcb 100644 --- a/test/benchmark/fib/fib.wren +++ b/test/benchmark/fib/fib.wren @@ -1,5 +1,4 @@ - class Fib { static get(n) { if (n < 2) return n @@ -9,6 +8,6 @@ class Fib { var start = System.clock for (i in 0...10) { - System.print(Fib.get(32)) + System.print(Fib.get(30)) } -System.print("elapsed: %(System.clock - start)") \ No newline at end of file +System.print("elapsed: %(System.clock - start) s") diff --git a/test/benchmark/list_reverse/list_reverse.pk b/test/benchmark/list_reverse/list_reverse.pk index 1036804..3fb510d 100644 --- a/test/benchmark/list_reverse/list_reverse.pk +++ b/test/benchmark/list_reverse/list_reverse.pk @@ -13,10 +13,8 @@ def reverse_list(list) end start = clock() - N = 20000000 l = (0..N).as_list reverse_list(l) - -print(clock() - start, 's') +print('elapsed: ', clock() - start, 's') diff --git a/test/benchmark/list_reverse/list_reverse.py b/test/benchmark/list_reverse/list_reverse.py index 9559fe0..5b8ebb8 100644 --- a/test/benchmark/list_reverse/list_reverse.py +++ b/test/benchmark/list_reverse/list_reverse.py @@ -12,4 +12,4 @@ start = clock() N = 20000000 l = list(range(N)) reverse_list(l) -print(clock() - start, 's') +print('elapsed: ', clock() - start, 's') diff --git a/test/benchmark/loop/loop.pk b/test/benchmark/loop/loop.pk index 078bf66..46909ce 100644 --- a/test/benchmark/loop/loop.pk +++ b/test/benchmark/loop/loop.pk @@ -1,15 +1,10 @@ from lang import clock - start = clock() -list = [] -for i in 0..10000000 - list_append(list, i) -end +l = [] +for i in 0..10000000 do list_append(l, i) end sum = 0 -for i in list - sum += i -end +for i in l do sum += i end print(sum) -print("elapsed:", clock() - start) +print("elapsed:", clock() - start, 's') diff --git a/test/benchmark/loop/loop.wren b/test/benchmark/loop/loop.wren index 1ce8bd5..790af58 100644 --- a/test/benchmark/loop/loop.wren +++ b/test/benchmark/loop/loop.wren @@ -7,4 +7,4 @@ var sum = 0 for (i in list) sum = sum + i System.print(sum) -System.print("elapsed: %(System.clock - start)") \ No newline at end of file +System.print("elapsed: %(System.clock - start) s") \ No newline at end of file diff --git a/test/benchmark/primes/primes.pk b/test/benchmark/primes/primes.pk index 748e0e6..f2b75fd 100644 --- a/test/benchmark/primes/primes.pk +++ b/test/benchmark/primes/primes.pk @@ -9,8 +9,7 @@ def is_prime(n) end start = clock() - -N = 60000; primes = [] +N = 30000; primes = [] for i in 0..N if is_prime(i) list_append(primes, i) diff --git a/test/benchmark/primes/primes.py b/test/benchmark/primes/primes.py index b8abd52..4e70979 100644 --- a/test/benchmark/primes/primes.py +++ b/test/benchmark/primes/primes.py @@ -8,7 +8,7 @@ def is_prime(n): start = clock() -N = 60000; primes = [] +N = 30000; primes = [] for i in range(N): if is_prime(i): primes.append(i) diff --git a/test/benchmark/primes/primes.rb b/test/benchmark/primes/primes.rb index c15c9bf..420d7a3 100644 --- a/test/benchmark/primes/primes.rb +++ b/test/benchmark/primes/primes.rb @@ -8,7 +8,7 @@ def is_prime(n) end start = Time.now -N = 60000; primes = [] +N = 30000; primes = [] for i in 0...N if is_prime(i) primes.append(i) diff --git a/test/benchmark/primes/primes.wren b/test/benchmark/primes/primes.wren index 601afc8..09418de 100644 --- a/test/benchmark/primes/primes.wren +++ b/test/benchmark/primes/primes.wren @@ -9,12 +9,12 @@ var is_prime = Fn.new {|n| var start = System.clock -var N = 60000 +var N = 30000 var primes = [] for (i in 0...N) { if (is_prime.call(i)) { primes.add(i) } } -System.print("elapsed: %(System.clock - start)") +System.print("elapsed: %(System.clock - start) s") diff --git a/test/examples/fib.pk b/test/examples/fib.pk index 4212988..f605c7d 100644 --- a/test/examples/fib.pk +++ b/test/examples/fib.pk @@ -1,18 +1,16 @@ -## Fib test. - -res = '' +## Iterative fibonacci function. def fib(n) + res = [] a = 0; b = 1 for _ in 0..n - res += to_string(a) + " " - temp = a; - a = b; - b += temp; + list_append(res, a) + temp = a; a = b; b += temp; end + return res end -fib(10) -print(res) -assert(res == '0 1 1 2 3 5 8 13 21 34 ') \ No newline at end of file +fibs = fib(10) +print('fibs =', fibs) +assert(fibs == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]) diff --git a/test/examples/fizzbuzz.pk b/test/examples/fizzbuzz.pk new file mode 100644 index 0000000..56bfcc4 --- /dev/null +++ b/test/examples/fizzbuzz.pk @@ -0,0 +1,9 @@ + + +for i in 1..100 + if i%3 == 0 and i%5 == 0 then print('fizzbuzz') + elif i%3 == 0 then print('fizz') + elif i%5 == 0 then print('buzz') + else print(i) + end +end diff --git a/test/examples/helloworld.pk b/test/examples/helloworld.pk new file mode 100644 index 0000000..60f08aa --- /dev/null +++ b/test/examples/helloworld.pk @@ -0,0 +1 @@ +print('Hello world!') diff --git a/test/examples/palette.pk b/test/examples/palette.pk new file mode 100644 index 0000000..87be16b --- /dev/null +++ b/test/examples/palette.pk @@ -0,0 +1,19 @@ + +## Run pocket palette.pk > palette.ppm +## Open palatte.ppm with any image viwer which support ppm image +## (ex: photoshop, gimp, libre office draw, ...) + +blue = 200 + +print("P3") +print(255, 255) +print(255) + +from lang import write +for r in 0..255 + for g in 0..255 + write(r, ' ', g, ' ', blue, ' ') + end + write('\n') +end + diff --git a/test/examples/pi.pk b/test/examples/pi.pk new file mode 100644 index 0000000..2d762e8 --- /dev/null +++ b/test/examples/pi.pk @@ -0,0 +1,17 @@ + +## PI approximation using Leibniz formula. +## +## PI/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - ... + +from math import abs + +pi_by_4 = 0; sign = -1 +for i in 1..10000 + sign *= -1 + pi_by_4 += sign * 1/(2*i - 1) +end + +PI = 4 * pi_by_4 +assert(abs(PI - 3.141593) < 0.0002) + +print('PI =', PI) diff --git a/test/examples/prime.pk b/test/examples/prime.pk index 2e4a256..ae4465f 100644 --- a/test/examples/prime.pk +++ b/test/examples/prime.pk @@ -1,8 +1,6 @@ ## Prime numbers. -res = '' - def is_prime(n) if n < 2 then return false end for i in 2..n @@ -12,12 +10,16 @@ def is_prime(n) end def get_all_primes(n) + res = [] for i in 0..n if is_prime(i) - res += to_string(i) + ' ' + list_append(res, i) end end + return res end -get_all_primes(20) -assert(res == '2 3 5 7 11 13 17 19 ') \ No newline at end of file +primes = get_all_primes(20) +assert(primes == [2, 3, 5, 7, 11, 13, 17, 19]) + +print('primes =', primes) diff --git a/test/run.py b/test/run.py index c5d03fa..65bebcb 100644 --- a/test/run.py +++ b/test/run.py @@ -1,7 +1,11 @@ -import subprocess -import json, os +import subprocess, os, sys +import json, re +from os.path import join -files = [ +FMT_PATH = "%-25s" +INDENTATION = ' | ' + +test_files = [ "lang/basics.pk", "lang/functions.pk", "lang/if.pk", @@ -9,12 +13,44 @@ files = [ "examples/prime.pk", ] -FMT_PATH = "%-25s" -INDENTATION = ' | ' +benchmarks = { + "factors" : ['.pk', '.py', '.rb', '.wren'], + "fib" : ['.pk', '.py', '.rb', '.wren'], + "list_reverse" : ['.pk', '.py', '.rb'], + "loop" : ['.pk', '.py', '.rb', ".wren"], + "primes" : ['.pk', '.py', '.rb', ".wren"], +} +def run_all_benchmarks(): + print("--------------------------------") + print(" BENCHMARKS ") + print("--------------------------------") + + def get_interpreter(file): + if file.endswith('.pk' ) : return 'pocket' + if file.endswith('.py' ) : return 'python' + if file.endswith('.rb' ) : return 'ruby' + if file.endswith('.wren') : return 'wren' + assert False + + for bm_name in benchmarks: + print(bm_name + ":") + for ext in benchmarks[bm_name]: + file = join('benchmark', bm_name, bm_name + ext) + interpreter = get_interpreter(file) + print(' %10s: '%interpreter, end=''); sys.stdout.flush() + result = run_command([interpreter, file]) + time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s', + result.stdout.decode('utf8'), + re.MULTILINE) + assert len(time) == 1, r'elapsed:\s*([0-9\.]+)\s*s --> no mach found.' + print('%10ss'%time[0]) def run_all_tests(): - for path in files: + print("--------------------------------") + print(" TESTS ") + print("--------------------------------") + for path in test_files: run_file(path) def run_file(path): @@ -37,4 +73,4 @@ def run_command(command): run_all_tests() - +run_all_benchmarks()