├── Benchmarks.md ├── Benchmarks.ods ├── Benchmarks.png ├── LICENSE ├── README.md ├── life.cc ├── life.js ├── life.lua ├── life.mnl ├── life.php ├── life.pl ├── life.py ├── life.py3 ├── life.rb ├── life.scm └── life.tcl /Benchmarks.md: -------------------------------------------------------------------------------- 1 | # Benchmarks # 2 | 3 | NB: Minimum run times out of a series of measurements are reported. 4 | 5 | 6 | Testbed A 7 | -------------------------------------------------------------------------------------------------------------- 8 | 9 | _CPU_: Intel Xeon L5640 @2.26 GHz (2.80 GHz) — Westmere-EP 10 | _Kernel_: 2.6.32-042stab126.1 (CentOS 6 + OpenVZ) 11 | _Distro_: CentOS release 6.9 (Final) + vzkernel-2.6.32-042stab126.1 + CentOS release 6.10 (Final) 12 | 13 | Language + variant (translator) | Time (s) | G | Slowdown | Translator + backend version-release 14 | ------------------------------- | --------:| -----:| --------: | -------------------------------------------- 15 | C++ (g++) | 1.037 | 66000 | **1.000** | 8.3.1-3.2.el6 16 | C++ (clang++) | 1.021 | 66000 | 0.985 | 3.4.2-4.el6 + 4.9.2-6.2.el6 (g++) 17 | Python 2 | 3.204 | 1000 | 203.919 | 2.6.6-68.el6_10 18 | Python 3 | 5.203 | 1000 | 331.146 | 3.4.10-4.el6 19 | PHP | 3.560 | 1000 | 226.577 | 5.3.3-50.el6_10 20 | Perl | 5.640 | 1000 | 358.959 | 5.10.1-144.el6 21 | Ruby | 14.122 | 1000 | 898.797 | 1.8.7.374-5.el6 22 | JavaScript/ECMAScript | 5.887 | 66000 | 5.677 | 0.10.48-3.el6 (node) 23 | Tcl | 6.724 | 100 | 4279.499 | 8.5.7-6.el6 24 | Lua (lua) | 141.703 | 66000 | 136.647 | 5.1.4-4.1.el6 25 | Lua (luajit) | 4.319 | 66000 | 4.165 | 2.0.4-3.el6 26 | Scheme (guile) | 6.176 | 1000 | 393.072 | 1.8.7-5.el6 27 | Scheme (csc) | 0.671 | 1000 | 42.706 | 4.12.0-3.el6 + 8.3.1-3.2.el6 (gcc) 28 | MANOOL + AllocOpt=True | 2.502 | 1000 | 159.240 | 0.5.0 (built with g++ 8.3.1-3.2.el6) 29 | MANOOL + AllocOpt=False | 2.593 | 1000 | 165.032 | 0.5.0 (ditto) 30 | 31 | 32 | Testbed B 33 | -------------------------------------------------------------------------------------------------------------- 34 | 35 | _CPU_: Intel Celeron N3060 @1.60 GHz (2.48 GHz) — Braswell 36 | _Kernel_: 4.4.0-17134-Microsoft (Windows 10 + WSL) 37 | _Distro_: Windows 10 Home version 1803 build 17134.1130 + Ubuntu 18.04.4 LTS 38 | 39 | Language + variant (translator) | Time (s) | G | Slowdown | Translator + backend version-release 40 | ------------------------------- | --------:| -----:| --------: | -------------------------------------------- 41 | C++ (g++) | 1.946 | 66000 | **1.000** | 7.5.0-3ubuntu1~18.04 42 | C++ (clang++) | 2.217 | 66000 | 1.139 | 1:6.0-1ubuntu2 + 7.5.0-3ubuntu1~18.04 (g++) 43 | Python 2 | 3.733 | 1000 | 126.607 | 2.7.17-1~18.04ubuntu1 44 | Python 3 | 5.309 | 1000 | 180.059 | 3.6.7-1~18.04 45 | PHP | 2.852 | 1000 | 96.728 | 7.2.24-0ubuntu0.18.04.6 46 | Perl | 6.768 | 1000 | 229.542 | 5.26.1-6ubuntu0.3 47 | Ruby | 4.425 | 1000 | 150.077 | 2.5.1-1ubuntu1.6 48 | JavaScript/ECMAScript | 8.522 | 66000 | 4.379 | 8.10.0~dfsg-2ubuntu0.4 (node) 49 | Tcl | 10.571 | 100 | 3585.231 | 8.6.8+dfsg-3 50 | Lua (lua) | 153.583 | 66000 | 78.922 | 5.3.3-1ubuntu0.18.04.1 51 | Lua (luajit) | 6.274 | 66000 | 3.224 | 2.1.0~beta3+dfsg-5.1 52 | Scheme (guile) | 1.233 | 1000 | 41.818 | 2.2.3+1-3ubuntu0.1 53 | Scheme (csc) | 1.691 | 1000 | 57.351 | 4.12.0-0.3 + 7.5.0-3ubuntu1~18.04 (gcc) 54 | MANOOL + AllocOpt=True | 3.882 | 1000 | 131.661 | 0.5.0 (built with g++ 7.5.0-3ubuntu1~18.04) 55 | MANOOL + AllocOpt=False | 3.943 | 1000 | 133.730 | 0.5.0 (ditto) 56 | 57 | -------------------------------------------------------------------------------- /Benchmarks.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rusini/life10-benchmarks/c46fdf0567f37a25f50a081742deba19a69570df/Benchmarks.ods -------------------------------------------------------------------------------- /Benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rusini/life10-benchmarks/c46fdf0567f37a25f50a081742deba19a69570df/Benchmarks.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Conway's Game of Life in 10 Programming Languages 2 | ================================================= 3 | 4 | This is a straightforward implementation of the [Game of Life][0] rules in 10 programming languages: 5 | * [C++][1] (version 2011 and later) — [life.cc](life.cc) 6 | * [Python][2] (version 2 and 3) — [life.py](life.py), [life.py3](life.py3) 7 | * [PHP][3] — [life.php](life.php) 8 | * [Perl][4] (version 5) — [life.pl](life.pl) 9 | * [Ruby][5] — [life.rb](life.rb) 10 | * [JavaScript/ECMAScript][6] (via Node.js) — [life.js](life.js) 11 | * [Tcl][7] — [life.tcl](life.tcl) 12 | * [Lua][8] — [life.lua](life.lua) 13 | * [Scheme][9] (at least R5RS and later) — [life.scm](life.scm) 14 | * and my own programming language called [MANOOL](https://manool.org) — [life.mnl](life.mnl) 15 | 16 | The program simply displays an initial configuration and then the configuration after _G_ generations and exits. The grid (board) dimensions are given by 17 | parameters _N_ and _M_, and the initial configuration is hard-coded but can be modified at your will. The universe is wrapped. 18 | 19 | The MANOOL version has an additional parameter, _AllocOpt_, which determines whether the code is explicitly memory allocation-aware or completely relies on 20 | copy-on-write aspects of the language semantics. 21 | 22 | The main purpose of this repository is to have a benchmark (which is both computationally intensive and fun) to compare performance of the MANOOL translator 23 | with that of other language implementations and to compare MANOOL with other languages. However, while coding the same task in so many of them, I suddenly 24 | realized how *bizarre* are the similarities (and differences) between programming languages. 25 | 26 | The choice of languages is made due to the following: 27 | * These are all dynamically typed programming languages (except C++, which is included for reference purposes). 28 | * The implementations have relatively lightweight installation packages for Unix-like systems (again, except for C++ ;-). 29 | 30 | For a performance comparison report, refer to [Benchmarks.md](Benchmarks.md). The graph is in [Benchmarks.png](Benchmarks.png): 31 | 32 | ![Benchmarks](https://raw.githubusercontent.com/rusini/life10/master/Benchmarks.png) 33 | 34 | An analysis of MANOOL syntax and semantics with respect to these programming languages is to follow at some point in the future. 35 | 36 | The examples are written in the most *idiomatic* but most *similar* way from the performance comparison and stylistic point of view. I did not have any previous 37 | working experience with Python, Ruby, Tcl, Lua, and Scheme (had never written code of similar complexity), so you are welcome to suggest improvements if there 38 | are even more idiomatic ways to use them. 39 | 40 | Construction of the implementation in MANOOL is discussed in detail in the Reddit post [“A primer on the programming language MANOOL: 41 | Conway's Game of Life”](https://www.reddit.com/r/ProgrammingLanguages/comments/h9ulvk/a_primer_on_the_programming_language_manool/). 42 | 43 | Examples of command line to run: 44 | 45 | * C++: 46 | 47 | g++ -std=c++11 -O3 life.cc && time ./a.out; rm -f a.out 48 | 49 | or 50 | 51 | clang++ -std=c++11 -O3 life.cc && time ./a.out; rm -f a.out 52 | 53 | * Python: 54 | 55 | time python life.py 56 | 57 | or 58 | 59 | time python3 life.py3 60 | 61 | * PHP: 62 | 63 | time php life.php 64 | 65 | * Perl: 66 | 67 | time perl life.pl 68 | 69 | * Ruby: 70 | 71 | time ruby life.rb 72 | 73 | * JavaScript/ECMAScript: 74 | 75 | time node life.js 76 | 77 | * Tcl: 78 | 79 | time tclsh life.tcl 80 | 81 | * Lua: 82 | 83 | time lua life.lua 84 | 85 | or 86 | 87 | time luajit life.lua 88 | 89 | * Scheme: 90 | 91 | time guile life.scm 92 | 93 | or 94 | 95 | csc -C -O3 life.scm && time ./life; rm -f life 96 | 97 | * MANOOL: 98 | 99 | time MNL_PATH=~/manool/build/lib ~/manool/build/mnlexec life.mnl 100 | 101 | ----- 102 | 103 | To clone this repo: 104 | 105 | git clone https://github.com/rusini/life10 106 | 107 | Have fun! — *[rusini](https://github.com/rusini)* — info@manool.org 108 | 109 | [0]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life 110 | [1]: https://en.wikipedia.org/wiki/C%2B%2B 111 | [2]: https://en.wikipedia.org/wiki/Python_(programming_language) 112 | [3]: https://en.wikipedia.org/wiki/PHP 113 | [4]: https://en.wikipedia.org/wiki/Perl 114 | [5]: https://en.wikipedia.org/wiki/Ruby_(programming_language) 115 | [6]: https://en.wikipedia.org/wiki/JavaScript 116 | [7]: https://en.wikipedia.org/wiki/Tcl 117 | [8]: https://en.wikipedia.org/wiki/Lua_(programming_language) 118 | [9]: https://en.wikipedia.org/wiki/Scheme_(programming_language) 119 | -------------------------------------------------------------------------------- /life.cc: -------------------------------------------------------------------------------- 1 | // life.cc -- Conway's Game of Life in C++ (version 2011 and later) 2 | 3 | # include 4 | # include 5 | 6 | constexpr int n = 40, m = 80; 7 | constexpr int g = 66000; 8 | 9 | static void display(signed char b[n][m]) noexcept { 10 | for (int i = 0; i < n; ++i) { 11 | for (int j = 0; j < m; ++j) std::cout << (b[i][j] ? '*' : ' '); 12 | std::cout << '\n'; 13 | } 14 | } 15 | 16 | int main() { 17 | signed char b[n][m]{}; 18 | // initialization 19 | b[19][41] = 20 | b[20][40] = 21 | b[21][40] = 22 | b[22][40] = 23 | b[22][41] = 24 | b[22][42] = 25 | b[22][43] = 26 | b[19][44] = 1; 27 | // end of initialization 28 | std::cout << "Before:\n"; display(b); 29 | 30 | decltype(b) nextb; 31 | for (int k = 0; k < g; ++k) { 32 | for (int i = 0; i < n; ++i) { 33 | constexpr int nm1 = n - 1; 34 | int up = i != 0 ? i - 1 : nm1, down = i != nm1 ? i + 1 : 0; 35 | for (int j = 0; j < m; ++j) { 36 | constexpr int mm1 = m - 1; 37 | int left = j != 0 ? j - 1 : mm1, right = j != mm1 ? j + 1 : 0; 38 | int count = 39 | b[up ][left ] + 40 | b[up ][j ] + 41 | b[up ][right] + 42 | b[i ][right] + 43 | b[down][right] + 44 | b[down][j ] + 45 | b[down][left ] + 46 | b[i ][left ]; 47 | nextb[i][j] = count == 2 && b[i][j] || count == 3; 48 | } 49 | } 50 | std::memcpy(b, nextb, sizeof b); 51 | } 52 | std::cout << "After " << g << " generations:\n"; display(b); 53 | return {}; 54 | } 55 | -------------------------------------------------------------------------------- /life.js: -------------------------------------------------------------------------------- 1 | // life.js -- Conway's Game of Life in JavaScript/ECMAScript (via Node.js) 2 | 3 | n = 40, m = 80; 4 | g = 66000; 5 | 6 | function display(b) { 7 | for (var i = 0; i < n; i++) { 8 | for (var j = 0; j < m; j++) process.stdout.write(b[i][j] ? '*' : ' '); 9 | process.stdout.write('\n'); 10 | } 11 | } 12 | 13 | function main() { 14 | var b = []; 15 | for (var i = 0; i < n; i++) { 16 | b[i] = []; 17 | for (var j = 0; j < m; j++) b[i][j] = 0; 18 | } 19 | // initialization 20 | b[19][41] = 21 | b[20][40] = 22 | b[21][40] = 23 | b[22][40] = 24 | b[22][41] = 25 | b[22][42] = 26 | b[22][43] = 27 | b[19][44] = 1; 28 | // end of initialization 29 | process.stdout.write('Before:\n'); display(b); 30 | 31 | var nextb = []; 32 | for (var i = 0; i < n; i++) nextb[i] = []; 33 | var nm1 = n - 1, mm1 = m - 1; 34 | for (var k = 0; k < g; k++) { 35 | for (var i = 0; i < n; i++) { 36 | var up = i != 0 ? i - 1 : nm1, down = i != nm1 ? i + 1 : 0; 37 | for (var j = 0; j < m; j++) { 38 | var left = j != 0 ? j - 1 : mm1, right = j != mm1 ? j + 1 : 0; 39 | var count = 40 | b[up ][left ] + 41 | b[up ][j ] + 42 | b[up ][right] + 43 | b[i ][right] + 44 | b[down][right] + 45 | b[down][j ] + 46 | b[down][left ] + 47 | b[i ][left ]; 48 | nextb[i][j] = count == 2 ? b[i][j] : count == 3 ? 1 : 0; 49 | } 50 | } 51 | var tmp = b; b = nextb; nextb = tmp; 52 | } 53 | process.stdout.write('After ' + g + ' generations:\n'); display(b); 54 | } 55 | main(); 56 | -------------------------------------------------------------------------------- /life.lua: -------------------------------------------------------------------------------- 1 | -- life.lua -- Conway's Game of Life in Lua 2 | 3 | n, m = 40, 80 4 | g = 66000 5 | 6 | function display(b) 7 | for i = 0, n - 1 do 8 | for j = 0, m - 1 do 9 | if b[i][j] ~= 0 then io.write("*") else io.write(" ") end 10 | end 11 | io.write("\n") 12 | end 13 | end 14 | 15 | function main() 16 | local b = {} 17 | for i = 0, n - 1 do 18 | b[i] = {} 19 | for j = 0, m - 1 do 20 | b[i][j] = 0 21 | end 22 | end 23 | -- initialization 24 | b[19][41] = 1 25 | b[20][40] = 1 26 | b[21][40] = 1 27 | b[22][40] = 1 28 | b[22][41] = 1 29 | b[22][42] = 1 30 | b[22][43] = 1 31 | b[19][44] = 1 32 | -- end of initialization 33 | io.write("Before:\n"); display(b) 34 | 35 | local nextb = {} 36 | for i = 0, n - 1 do 37 | nextb[i] = {} 38 | end 39 | local nm1, mm1 = n - 1, m - 1 40 | for k = 1, g do 41 | for i = 0, nm1 do 42 | local up, down 43 | if i ~= 0 then up = i - 1 else up = nm1 end 44 | if i ~= nm1 then down = i + 1 else down = 0 end 45 | for j = 0, mm1 do 46 | local left, right 47 | if j ~= 0 then left = j - 1 else left = mm1 end 48 | if j ~= mm1 then right = j + 1 else right = 0 end 49 | local count = 50 | b[up ][left ] + 51 | b[up ][j ] + 52 | b[up ][right] + 53 | b[i ][right] + 54 | b[down][right] + 55 | b[down][j ] + 56 | b[down][left ] + 57 | b[i ][left ] 58 | if count == 2 then nextb[i][j] = b[i][j] 59 | elseif count == 3 then nextb[i][j] = 1 60 | else nextb[i][j] = 0 61 | end 62 | end 63 | end 64 | b, nextb = nextb, b 65 | end 66 | io.write("After ", g, " generations:\n"); display(b) 67 | end 68 | main() 69 | -------------------------------------------------------------------------------- /life.mnl: -------------------------------------------------------------------------------- 1 | -- life.mnl -- Conway's Game of Life in MANOOL (version 0.5) 2 | 3 | { {extern "manool.org.18/std/0.5/all"} in 4 | : let { AllocOpt = True } in 5 | : let { N = 40; M = 80 } in 6 | : let { G = 1000 } in 7 | 8 | : let 9 | { Display = 10 | { proc { B } as 11 | : for { I = Range[N]$ } do 12 | : do Out.WriteLine[] after 13 | : for { J = Range[M]$ } do 14 | Out.Write[{if B[I; J] <> 0 then "*" else " "}] 15 | } 16 | } 17 | in 18 | 19 | : var { B = {array N of: array M of 0} } in 20 | -- initialization 21 | B[19; 41] = 1 22 | B[20; 40] = 1 23 | B[21; 40] = 1 24 | B[22; 40] = 1 25 | B[22; 41] = 1 26 | B[22; 42] = 1 27 | B[22; 43] = 1 28 | B[19; 44] = 1 29 | -- end of initialization 30 | Out.WriteLine["Before:"]; Display[B] 31 | 32 | { var { NextB = B } in 33 | : repeat G do 34 | : do {if ~AllocOpt then (B = NextB)' else {assign B = NextB; NextB = B}'}% after 35 | : for { I = Range[N]$ } do 36 | : var { Up = {if I <> 0 then I - 1 else (N - 1)$}; Down = {if I <> (N - 1)$ then I + 1 else 0} } in 37 | : for { J = Range[M]$ } do 38 | : var { Left = {if J <> 0 then J - 1 else (M - 1)$}; Right = {if J <> (M - 1)$ then J + 1 else 0} } in 39 | : var 40 | { Count = 41 | B[Up ; Left ] + 42 | B[Up ; J ] + 43 | B[Up ; Right] + 44 | B[I ; Right] + 45 | B[Down; Right] + 46 | B[Down; J ] + 47 | B[Down; Left ] + 48 | B[I ; Left ] 49 | } 50 | in 51 | NextB[I; J] = 52 | { if Count == 2 then B[I; J] else 53 | : if Count == 3 then 1 else 54 | 0 55 | } 56 | } 57 | Out.WriteLine["After " G " generations:"]; Display[B] 58 | } 59 | -------------------------------------------------------------------------------- /life.php: -------------------------------------------------------------------------------- 1 |