=========== Performance =========== .. Modified: 2019-02-23/17:09-0500 btiffin .. Copyright 2016 Brian Tiffin .. GPL 3.0+ :ref:`license` .. This file is part of the Unicon Programming documentation .. image:: images/unicon.png :align: center .. only:: html :ref:`genindex` :floatright:`Unicon` .. index:: performance, benchmark .. _performance: Unicon performance ================== Unicon is a very high level language. The runtime engine, with generators, co-expressions, threading, and the assortment of other features means that most operations need to include a fair number of conditional tests to verify correctness. While this is some overhead, Unicon still performs at a very respectable level. The C compilation mode can help when performance is a priority, and :ref:`loadfunc` is always available when C or Assembly level speed is necessary for critical operations. Unicon, interpreting :ref:`icode` files, ranges from 20 to 40 times slower than optimized C code in simple loops and straight up computations. On par with similar code in Python. Compiled Unicon (via ``unicon -C``) is probably about 10 times slower than similar C when timing a tight numeric computation loop. This is mainly due to the overhead that is required for the very high level language features of Unicon, mentioned above. As always, those are somewhat unfair comparisons. The tasks that Unicon can be applied to and the development time required to get correct solutions can easily outweigh a few seconds of runtime per program pass. Saving a week of effort can mean that many thousands of program runs are required before a developer hits a break even point. Five eight hours days total up 144,000 seconds. If the delta for a program run between C and Unicon is 5 seconds per run, you'd need to run a program over 28,000 times to make up for a one week difference in development time. Critical routines can leverage loadable functions when needed. All these factors have to be weighed when discussing performance issues. With all that in mind, it's still nice to have an overall baseline for daily tasks, to help decide when to bother with :ref:`loadfunc` or which language is the right tool for the task at hand\ [#notwrong]_. .. [#notwrong] Or more explicitly; *not the wrong tool for the task at hand*. Most general purpose programming languages are capable of providing a workable solution to any computing problem, but sometimes the problem specific advantages in one language make it an obvious choice for mixing with Unicon for performance and or development time benefits. .. note:: This section is unashamedly biased towards Unicon. It's the point of the exercise. All comparisons assume that point of view and initial bias. .. index:: tightloop .. _tightloop: Summing integers ================ Given a simple program, creating a sum of numbers in a tight loop. Comparing ``Unicon`` with ``Python``, and ``C``. Other scripting and compiled languages are included for general interest sake. This lists simple code running 16.8 million iterations while tallying a sum in each language. .. note:: The representative timings below each listing are approximate, and results will vary from run to run. There is a fixed number included with each the listing to account for those times when the document generation may have occurred while the build system was blocked or busy at the point of timing run capture. Tested running Xubuntu with an AMD A10-5700 quadcore APU chipset. Different hardware would have different base values, but should have equivalent relative timings. .. index:: tightloop; sources, tightloop; Unicon Unicon ------ **Unicon**, *tightloop.icn* .. literalinclude:: examples/performance/tightloop.icn :language: unicon :start-after: ##+ Representative timing: 2.02 seconds, 0.55 seconds (-C compiled) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop.icn` *unicon (icode)* .. command-output:: time -p unicon -s tightloop.icn -x :cwd: examples/performance *unicon -C* .. command-output:: unicon -s -o tightloop-uc -C tightloop.icn :cwd: examples/performance .. command-output:: time -p ./tightloop-uc :cwd: examples/performance .. index:: Python, tighloop; Python Python ------ **Python**, *tightloop-py.py* .. literalinclude:: examples/performance/tightloop-py.py :language: python :start-after: ##+ Representative timing: 2.06 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-py.py` .. command-output:: time -p python tightloop-py.py :cwd: examples/performance .. index:: C, tightloop; C C --- **C**, *tightloop-c.c* .. literalinclude:: examples/performance/tightloop-c.c :language: c :start-after: +*/ Representative timing: 0.05 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-c.c` .. command-output:: gcc -o tightloop-c tightloop-c.c :cwd: examples/performance .. command-output:: time -p ./tightloop-c :cwd: examples/performance .. index:: Ada, tightloop; Ada Ada --- **Ada**, *tightloopada.adb* .. literalinclude:: examples/performance/tightloopada.adb :language: ada :start-after: -- + Representative timing: 0.06 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloopada.adb` *GNAT Ada, 5.4.0* .. command-output:: gnatmake tightloopada.adb :cwd: examples/performance .. command-output:: time -p ./tightloopada :cwd: examples/performance .. index:: ALGOL, tightloop; ALGOL ALGOL ----- **ALGOL**, *tightloop-algol.a68* [#notauto]_ .. literalinclude:: examples/performance/tightloop-algol.a68 :language: text :start-after: #+# Representative timing: 5.91 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-algol.a68` :: prompt$ a68g tightloop-algol +140737496743936 real 5.91 user 5.86 sys 0.03 .. index:: Assembler, tightloop; Assembler Assembler --------- **Assembler**, *tightloop-assembler.s* .. literalinclude:: examples/performance/tightloop-assembler.s :language: gas :start-after: ##+ Representative timing: 0.01 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-assembler.s` .. command-output:: gcc -o tightloop-assembler tightloop-assembler.s :cwd: examples/performance .. command-output:: time -p ./tightloop-assembler :cwd: examples/performance .. index:: BASIC, BaCon, tightloop; BASIC BASIC ----- **BASIC**, *tightloop-basic.bac* .. literalinclude:: examples/performance/tightloop-basic.bac :language: basic :start-after: REM+ Representative timing: 0.05 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-basic.bac` .. command-output:: bacon -y tightloop-basic.bac >/dev/null :cwd: examples/performance :shell: .. command-output:: time -p ./tightloop-basic :cwd: examples/performance C (baseline) ------------ See above, Unicon, C and Python are the ballpark for this comparison. .. index:: COBOL, tightloop; COBOL COBOL ----- **COBOL**, *tightloop-cobol.cob* .. literalinclude:: examples/performance/tightloop-cobol.cob :language: cobol :start-after: *>+<* Representative timing: 0.06 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-cobol.cob` *GnuCOBOL 2.0-rc3* .. command-output:: cobc -x tightloop-cobol.cob :cwd: examples/performance .. command-output:: time -p ./tightloop-cobol :cwd: examples/performance .. index:: D, tightloop; D D --- **D**, *tightloop-d.d* .. literalinclude:: examples/performance/tightloop-d.d :language: d :start-after: +*/ Representative timing: 0.05 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-d.d` *gdc* .. command-output:: gdc tightloop-d.d -o tightloop-d :cwd: examples/performance .. command-output:: time -p ./tightloop-d :cwd: examples/performance .. index:: ECMAScript, Javascript, Duktape, nodejs, tightloop; ECMAScript ECMAScript ---------- **ECMAScript**, *tightloop-js.js* .. literalinclude:: examples/performance/tightloop-js.js :language: js :start-after: +*/ Representative timing: 0.83 seconds (node.js), 10.95 (gjs), 63.37 (duktape) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-js.js` *nodejs* .. command-output:: time -p nodejs tightloop-js.js :cwd: examples/performance *gjs* [#notauto]_ :: prompt$ time -p gjs tightloop-js.js 140737496743936 real 10.96 user 10.95 sys 0.00 *Duktape* [#notauto]_ :: prompt$ time -p duktape tightloop-js.js 140737496743936 real 63.37 user 63.36 sys 0.00 .. index:: Elixir, tightloop; Elixir Elixir ------ **Elixir**, *tightloop-elixir.ex* .. literalinclude:: examples/performance/tightloop-elixir.ex :language: elixir :start-after: #+ Representative timing: 2.03 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-elixir.ex` *elixirc 1.1.0-dev* .. command-output:: time -p elixirc tightloop-elixir.ex :cwd: examples/performance .. index:: Forth, Ficl, gforth, tightloop; Forth Forth ----- **Forth**, *tightloop-ficl.fr* .. literalinclude:: examples/performance/tightloop-ficl.fr :start-after: \ \+ Representative timing: 0.52 seconds (Ficl), 0.12 seconds (Gforth) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-ficl.fr` *ficl* .. command-output:: time -p ficl tightloop-ficl.fr :cwd: examples/performance *gforth* .. command-output:: time -p gforth tightloop-ficl.fr :cwd: examples/performance .. index:: Fortran, tightloop; Fortran Fortran ------- **Fortran**, *tightloop-fortran.f* .. literalinclude:: examples/performance/tightloop-fortran.f :language: fortran :start-after: !!+ Representative timing: 0.06 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-fortran.f` *gfortran* .. command-output:: gfortran -o tightloop-fortran -ffree-form tightloop-fortran.f :cwd: examples/performance .. command-output:: time -p ./tightloop-fortran :cwd: examples/performance .. index:: Groovy, tightloop; Groovy Groovy ------ **Groovy**, *tightloop-groovy.groovy* .. literalinclude:: examples/performance/tightloop-groovy.groovy :language: groovy :start-after: +*/ Representative timing: 0.47 seconds (will use multiple cores) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-groovy.groovy` *groovyc 1.8.6, OpenJDK 8* .. command-output:: groovyc tightloop-groovy.groovy :cwd: examples/performance .. command-output:: time -p java -cp ".:/usr/share/groovy/lib/*" TightloopGroovy :cwd: examples/performance .. index:: Java, tightloop; Java Java ---- **Java**, *tightloopjava.java* .. literalinclude:: examples/performance/tightloopjava.java :language: java :start-after: +*/ Representative timing: 0.11 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloopjava.java` *OpenJDK javac* .. command-output:: javac tightloopjava.java :cwd: examples/performance .. command-output:: time -p java -cp . tightloopjava :cwd: examples/performance .. index:: Lua, tightloop; Lua Lua --- **Lua**, *tightloop-lua.lua* .. literalinclude:: examples/performance/tightloop-lua.lua :language: lua :start-after: --+ Representative timing: 0.73 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-lua.lua` *lua* .. command-output:: time -p lua tightloop-lua.lua :cwd: examples/performance .. index:: Neko, tightloop; Neko Neko ---- **Neko**, *tightloop-neko.neko* .. literalinclude:: examples/performance/tightloop-neko.neko :language: text :start-after: +*/ Representative timing: 0.89 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-neko.neko` *nekoc, neko* .. command-output:: nekoc tightloop-neko.neko :cwd: examples/performance .. command-output:: time -p neko tightloop-neko :cwd: examples/performance .. index:: Nickle, tightloop; Nickle Nickle ------ **Nickle**, *tightloop-nickle.c5* .. literalinclude:: examples/performance/tightloop-nickle.c5 :language: c :start-after: +*/ 4.85 seconds representative .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-nickle.c5` *Nickle 2.77* [#notauto]_ :: prompt$ time -p nickle tightloop-nickle.c5 140737496743936 real 4.85 user 4.83 sys 0.01 .. index:: Nim, tightloop; Nim Nim --- **Nim**, *tightloopNim.nim* .. literalinclude:: examples/performance/tightloopNim.nim :language: nim :start-after: ##+ Representative timing: 0.31 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloopNim.nim` *nim* .. command-output:: nim compile --verbosity:0 --hints:off tightloopNim.nim :cwd: examples/performance .. command-output:: time -p ./tightloopNim :cwd: examples/performance .. index:: Perl, tightloop; Perl Perl ---- **Perl**, *tightloop-perl.pl* .. literalinclude:: examples/performance/tightloop-perl.pl :language: perl :start-after: ##+ Representative timing: 1.29 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-perl.pl` *perl 5.22* .. command-output:: time -p perl tightloop-perl.pl :cwd: examples/performance .. index:: PHP, tightloop; PHP PHP ---- **PHP**, *tightloop-php.php* .. literalinclude:: examples/performance/tightloop-php.php :language: php :start-after: */?> Representative timing: 0.39 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-php.php` *PHP 7.0.15*, see :ref:`PHP`. .. command-output:: time -p php tightloop-php.php :cwd: examples/performance Python ------ See above. Unicon, C and Python are the ballpark for this comparison. .. index:: REBOL, tightloop; REBOL REBOL ----- **REBOL**, *tightloop-rebol.r* .. literalinclude:: examples/performance/tightloop-rebol.r3 :language: rebol :start-after: ;;+ 2.22 seconds representative .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-rebol.r3` *r3* .. command-output:: time -p r3 tightloop-rebol.r3 :cwd: examples/performance .. index:: REXX, ooRexx, Regina, tightloop; REXX REXX ---- **REXX**, *tightloop-rexx.rex* .. literalinclude:: examples/performance/tightloop-rexx.rex :language: rexx :start-after: +*/ 2.38 seconds representative (oorexx), 4.48 (regina) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-rexx.rex` *oorexx 4.2* .. command-output:: time -p /usr/bin/rexx tightloop-rexx.rex :cwd: examples/performance *regina 3.9*, slowed by NUMERIC DIGITS 16 for clean display [#notauto]_ :: prompt$ time -p rexx tightloop-rexx.rex 140737496743936 real 4.84 user 4.84 sys 0.00 .. index:: Ruby, tightloop; Ruby Ruby ---- **Ruby**, *tightloop-ruby.rb* .. literalinclude:: examples/performance/tightloop-ruby.rb :language: ruby :start-after: ##+ Representative timing: 1.16 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-ruby.rb` *ruby 2.3* .. command-output:: time -p ruby tightloop-ruby.rb :cwd: examples/performance .. index:: Rust, tightloop; Rust Rust ---- **Rust**, *tightloop-rust.rs* .. literalinclude:: examples/performance/tightloop-rust.rs :language: rust :start-after: //+ Representative timing: 0.44 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-rust.rs` *rustc 1.16.0* .. command-output:: /home/btiffin/.cargo/bin/rustc tightloop-rust.rs :cwd: examples/performance .. command-output:: time -p ./tightloop-rust :cwd: examples/performance .. index:: Scheme, Guile, tightloop; Scheme Scheme ------ **Scheme**, *tightloop-guile.scm* .. literalinclude:: examples/performance/tightloop-guile.scm :language: scheme :start-after: ;;+ Representative timing: 0.85 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-guile.scm` *guile 2.0.11* .. command-output:: time -p guile -q tightloop-guile.scm :cwd: examples/performance .. index:: tightloop; shell Shell ----- **Shell**, *tightloop-sh.sh* .. literalinclude:: examples/performance/tightloop-sh.sh :language: bash :start-after: ##+ Representative timing: 281.29 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-sh.sh` *bash 4.3.46* [#notauto]_ :: prompt$ time -p source tightloop-sh.sh 140737496743936 real 281.29 user 280.08 sys 1.11 .. index:: S-Lang, tightloop; S-Lang S-Lang ------ **S-Lang**, *tightloop-slang.sl* .. literalinclude:: examples/performance/tightloop-slang.sl :language: text :start-after: %%+ Representative timing: 4.92 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-slang.sl` *slsh 0.9.1 with S-Lang 2.3* [#notauto]_ :: prompt$ time -p slsh tightloop-slang.sl 140737496743936 real 4.92 user 4.92 sys 0.00 .. index:: Smalltalk, gst, tightloop; Smalltalk Smalltalk --------- **Smalltalk**, *tightloop-smalltalk.st* .. literalinclude:: examples/performance/tightloop-smalltalk.st :language: text :start-after: +" Representative timing: 4.60 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-smalltalk.st` *GNU Smalltalk 3.2.5* [#notauto]_ :: prompt$ time -p gst tightloop-smalltalk.st 140737496743936 real 4.56 user 4.55 sys 0.00 .. index:: SNOBOL, tightloop; SNOBOL SNOBOL ------ **SNOBOL**, *tightloop-snobol.sno* .. literalinclude:: examples/performance/tightloop-snobol.sno :language: snobol :start-after: *+* Representative timing: 5.83 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-snobol.sno` *snobol4 CSNOBOL4B 2.0* [#notauto]_ :: prompt$ time -p snobol4 tightloop-snobol.sno 140737496743936 real 5.83 user 5.82 sys 0.00 .. index:: Tcl, jimsh, tightloop; Tcl Tcl --- **Tcl**, *tightloop-tcl.tcl* .. literalinclude:: examples/performance/tightloop-tcl.tcl :language: tcl :start-after: ##+ Representative timing: 4.59 seconds (jimsh), 17.69 seconds (tclsh) .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-tcl.tcl` *jimsh 0.76* [#notauto]_ :: prompt$ time -p jimsh tightloop-tcl.tcl 140737496743936 real 4.59 user 4.59 sys 0.00 *tclsh 8.6* [#notauto]_ :: prompt$ time -p tclsh tightloop-tcl.tcl 140737496743936 real 17.69 user 17.67 sys 0.00 .. index:: Vala, Genie, tightloop; Vala Vala ---- **Vala**, *tightloop-vala.vala* .. literalinclude:: examples/performance/tightloop-vala.vala :language: vala :start-after: +*/ Representative timing: 0.16 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-vala.vala` *valac 0.34.2* .. command-output:: valac tightloop-vala.vala :cwd: examples/performance .. command-output:: time -p ./tightloop-vala :cwd: examples/performance Genie ----- **Vala/Genie**, *tightloop-genie.gs* .. literalinclude:: examples/performance/tightloop-genie.gs :language: vala :start-after: +*/ Representative timing: 0.16 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-genie.gs` *valac 0.34.2* .. command-output:: valac tightloop-genie.gs :cwd: examples/performance .. command-output:: time -p ./tightloop-genie :cwd: examples/performance .. index:: loadfunc, tightloop; loadfunc Unicon loadfunc --------------- A quick test for speeding up Icon **Unicon loadfunc**, *tightloop-loadfunc.icn* .. literalinclude:: examples/performance/tightloop-loadfunc.icn :language: unicon :start-after: ##+ Representative timing: 0.05 seconds .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-loadfunc.icn` **C loadfunc for Unicon**, *tightloop-cfunc.c* .. literalinclude:: examples/performance/tightloop-cfunc.c :language: c :start-after: +*/ .. only:: html .. rst-class:: rightalign :download:`examples/performance/tightloop-cfunc.c` *unicon with loadfunc* .. command-output:: gcc -o tightloop-cfunc.so -O3 -shared -fpic tightloop-cfunc.c :cwd: examples/performance .. command-output:: time -p unicon -s tightloop-loadfunc.icn -x :cwd: examples/performance -------- Summary ------- .. image:: images/performance-chart.png .. only:: html .. rst-class:: leftalign :download:`images/performance-chart.png` *In the image above, bars for Bash shell and Tclsh are not included. ECMAScript value is from Node.js (V8).* [#charting]_ There was no attempt to optimize any code. Compile times are only included when that is the *normal* development cycle. Etcetera. Routines can always be optimized, tightened, and fretted over. The point of this little exercise is mainly for rough guidance (perhaps with healthy doses of confirmation, self-serving, halo effect, academic, and/or experimenters bias [#bias]_). [#fretted]_ *While there may be favouritism leaking through this summary, to the best of my knowledge and belief there is no deliberate shilling. As this is free documentation in support of free software, I can attest to no funding, bribery or insider trading bias as well*. *Smiley*. I will also attest to the belief that **Unicon is awesome**. You should use it for as many projects as possible, and not feel any regret or self-doubt while doing so. *Double smiley*. With that out of the way, here is a recap: :t:`Unicon` translated to :ref:`icode` is nearly equivalent to :t:`Python` and :t:`Elixir` in terms of the timing (variants occur between runs, but within a few tenths or hundredths of a second difference, up and down). :t:`C` wins, orders of magnitude faster than the scripted language trials and marginally faster than most of the other compiled trials. A later addition of ``gcc -O3`` compiles and an Assembler sample run faster, but that counts as fretting [#fretted]_. The :t:`GnuCOBOL`, :t:`gfortran`, :t:`GNAT/Ada` and :t:`BaCon` programs fare well, only a negligible fraction slower than the baseline :t:`C`. Both :t:`GnuCOBOL` and :t:`BaCon` use :t:`C` intermediates on the way to a native compile. :t:`gfortran` uses the same base compiler technology as :t:`C` in these tests (GCC). :t:`Unicon` can load any of these modules when pressed for time. :t:`D` also fares well, with sub tenth of a second timing. :t:`Java` and :t:`Gforth` test at a third as fast as C, admirable, neck and neck with :t:`Vala` and :t:`Genie`. :t:`Nim`, :t:`PHP` and :t:`Rust` clock in shortly after those timings. :t:`Unicon` compiled via ``-C`` completes roughly 4 times faster than the :t:`icode` virtual machine interpreter version, at about 1/10th C speed. :t:`Ruby`, :t:`Perl` and :t:`Neko`, are faster than interpreted :t:`Unicon` for this sample. :t:`Elixir` clocks in next and like :t:`Python`, pretty close to the bar set by interpreted :t:`Unicon`. :t:`REBOL` and :t:`REXX` clock in just a little slower than the :t:`Unicon` mark. :t:`Python` and :t:`Elixir` perform this loop with similar timings to interpreted :t:`Unicon`, all within 2% of each others elapsed time. Widening the circle a little bit, :t:`REXX` and :t:`REBOL` become comparable as well. *Revealing a bit of the author's bias, let's call these* **the close competition** *while discussing raw performance. I have a different (overlapping) set of languages in mind when it comes to overall competitive development strategies* [#overall]_. :t:`Tcl` takes about twice as long as :t:`Unicon` when using the :t:`JimTcl` interpreter, and approaching 9 times slower with a full :t:`Tcl` interpreter. :t:`Gjs` ended up timing in between the two :t:`Tcl` engines. :t:`ALGOL`, :t:`S-Lang`, :t:`Smalltalk` and :t:`SNOBOL` also took about twice as long as :t:`Unicon` when running this trial. :t:`Duktape` ran at second to last place, just over a minute. :t:`bash` was unsurprisingly the slowest of the bunch, approaching a 5 minute run time for the 16.8 million iterations. Native compiled :t:`Unicon` timing is on par with :t:`Ficl`, and a little faster than :t:`Lua`, :t:`node.js`, and then :t:`Guile`, but still about 10 times slower than the compiled :t:`C` code. (:t:`Unicon` includes automatic detection of native integer overflow, and will seamlessly use large integer support routines when needed. Those tests will account for some of the timing differences in this particular benchmark when compared to straight up :t:`C`). Once again, these numbers are gross estimates, no time spent fretting over how the code was run, or worrying about different implementations, just using the tools as *normal* (for this author, when not fretting [#fretted]_). Unicon stays reasonably competitive, all things considered. .. csv-table:: Timings :header: "Scale", "Languages" :widths: 5, 40 <1x, "Assembler, -O3 C" 1x, "C, Ada, BASIC, COBOL, D, Fortran, **Unicon loadfunc**" 3x, "Java, GForth, Vala, Genie" 6x, "Nim, PHP, Rust" 10x, "**Unicon -C**" 15x, "Ficl, Lua, Scheme" 20x, "Neko, Node.js" 25x, "Perl, Ruby" 40x, "**Unicon**, Elixir, Python, REBOL, REXX" 100x, "Algol, JimTcl, S-Lang, Smalltalk, SNOBOL" 200x, "Gjs" 350x, "Tcl" 1200x, "Duktape" 5600x, "Shell" The Unicon perspective ...................... With Unicon in large (or small) scale developments, if there is a need for speed, you can always write critical sections in another compiled language and load the routines into Unicon space. The overhead for this is minimal in terms of both source code wrapper requirements and runtime data conversions\ [#overhead]_. The number of systems that can produce compatible loadable objects is fairly extensive; C, C++, COBOL, Fortran, Vala/Genie, Ada, Pascal, Nim, BASIC, Assembler, to name but a few. This all means that in terms of performance, Unicon is never a bad choice for small, medium or large projects. The leg up on development time may outweigh a lot of the other complex considerations. A note on development time .......................... With a program this short and simple minded, development time differences are negligible. Each sample takes a couple of minutes to write, test and document [#mystery]_. That would change considerably as problems scaled up in size. Not just in terms of lines of code needed, but also the average time to get each line written, tested and verified correct. General know how with each environment would soon start to influence productivity and correctness, and bring up a host of other issues. A lot can be said for domain expertise with the language at hand. Without expertise, development time may extend; referencing materials, searching the ecosystem for library imports, becoming comfortable with idioms, with testing and with debugging. The less lines that need to be written to solve tasks can add up to major benefits when comparing languages in non-trivial development efforts. .. [#notauto] Due to the length of the timing trials, some results are not automatically captured during documentation generation. Results for those runs were captured separately and copied into the document source. All other program trials are evaluated during each Sphinx build of this document *(results will vary slightly from release to release)*. .. [#charting] The bar chart graphic was generated with a small Unicon program. .. only:: html .. rst-class:: rightalign :download:`examples/performance/charting.icn` .. [#fretted] *Ok, I eventually fretted*. Added :u:func:`loadfunc` to show off mixed programming for speed with :t:`Unicon`. Also added -O3 gcc timing and an assembler sample that both clock in well under the baseline timing. .. [#overhead] There are a few examples of how much (little) code is required to wrap compiled code in Unicon in this docset. See :ref:`soldout` for one example, wrapping a C library to perform Markdown to HTML processing in about 30 lines of mostly boilerplate source. Other entries in the :doc:`programs` chapter exercise and demonstrate the :ref:`loadfunc` feature that allows these mixed language integrations. .. [#mystery] (Except for the :t:`BaCon` trial). That example stole a few extra minutes for debugging, out of the blue, many days after the initial code writing and verification pass. It turns out ``BaCon`` generated a temporary :file:`Makefile` during its translation to C phase, and I had to track down the mystery when an unrelated :file:`Makefile` sample kept disappearing during generation of new versions of this document. That led to moving all the performance samples to a separate sub-directory to avoid the problem, and any others that may occur in the future when dealing with that many programming environments all at the same time and in the same space. *BaCon was fixed the same day as the inconvenience report.* .. [#overall] Not to leave you hanging; I put C, C++, C#, Erlang, Go, Java, Lua, Node.js, Perl, Python, PHP, Red, and Ruby firmly in the Unicon competitive arena. Bash/Powershell and Javascript count as auxiliary tools, that will almost always be mixed in with project developments. Same can be said for things like HTML/CSS and SQL, tools that will almost always be put to use, but don't quite count as "main" development systems. For Erlang, I also count Elixir, and for Java that includes Scala, Groovy and the like. I live in GNU/Linux land, so my list doesn't include Swift or VB.NET etc, your short list may differ. I also never quite took to the Lisp-y languages so Scheme and Closure don't take up many brain cycles during decision making. And finally, I'm a huge COBOL nerd, and when you need practical programming, COBOL should always be part of the development efforts. -------- Development time ================ Even though execution time benchmarking is hard to quantify in an accurate way (there are always issues unaccounted for, or secrets yet to uncover) and is fraught with biased opinion\ [#bias]_, judging development time is magnitudes harder. Sometimes lines pour out of fingers and code works better than expected. Sometimes an off by one error can take an entire afternoon to uncover and flush productivity down the toilet. In general, for complex data processing issues, very high level languages beat high level languages which beat low level languages when it comes to complete solution development time. It's not a hard and fast rule, but in general. Unicon counts as a very high level language. There are features baked into Unicon that ease a lot of the day to day burdens faced by many developers. Easy to wield data structures, memory managed by the system and very high level code control mechanisms can all work together to increase productivity and decrease development time. *In general*. For some tasks, ``Ruby`` may be the better choice, for others tasks ``Python`` or ``C`` or ``Tcl``, or some language you have never heard of may be the wisest course. Each with a strength, and each having skilled practitioners that can write code faster in that language than in any other. Within the whole mix, ``Unicon`` provides a language well suited to productive, timely development with good levels of performance. Other languages can be mixed with ``Unicon`` when appropriate, including loadable routines that can be as close to the hardware as hand and machine optimized assembler can manage. If the primary factor is development time, Unicon offers an extremely competitive environment. The feature set leaves very few domains of application left wanting. .. index:: downsides .. _downsides: Downsides --------- Unicon has a few places that can expose hard to notice construction problems. Goal-directed evaluation can spawn exponential backtracking problems when two or more expressions are involved. Some expression syntax can end up doing a lot more work in the background than it would seem at a glance. Bounded expressions (or lack thereof) can cause some head scratching at times. There are tools in place with Unicon to help find these issues, but nothing will ever beat experience, and experience comes from writing code, lots of code. Luckily, Unicon is at home when programming in the small as it is with middling and large scale efforts\ [#scale1]_. The :ref:`class`, :ref:`method`, and :ref:`package` features, along with the older :ref:`link` directive make for a programming environment that begs for application. There are a lot of problem domains that Unicon can be applied to, and that can all help gaining experience. Due to some of the extraordinary features of Unicon, it can be applied to very complex problems\ [#complex]_. Complex problems always shout out for elegant solutions, and that lure can lead to some false positives with initial Unicon programs. It can take practice to know when an edge case is not properly handled, or when a data dependent bug sits waiting for the right combination of inputs to cause problems. Rely on Unicon, but keep a healthy level of skepticism when starting out. *This is advice from an author that is just starting out, so keep that in mind. Read the other Technical Reports, articles, and Unicon books; as this document is very much entry level to intermediate Unicon*. Wrap expressions in small test heads and throw weird data at your code. Experiment. Turn any potential Unicon downsides into opportunities. .. [#bias] With biased opinions comes cognitive filtering. While writing the various ``tightloop`` programs, I wanted Unicon to perform well in the timing trials. That cognitive bias may have influenced how the results were gathered and reported here. Not disappointed with the outcomes, but ``C`` sure sets a high bar when it comes to integer arithmetic. .. [#scale1] Having no actual experience beyond middling sized projects, I asked the forum if anyone has worked on a large Unicon system with over 100,000 lines of code. Here are a couple of the responses: The biggest project I worked on using Unicon was CVE (http://cve.sourceforge.net/) not sure if we broke the 100,000 LOC [*mark*] though. I don't see any reason why you can't write large projects using Unicon. With the ability to write very concise code in Unicon, I'd argue it is even easier to go big. --Jafar The two largest known Unicon projects are SSEUS at the National Library of Medicine, and mKE by Richard McCullough of Context Knowledge Systems, not necessarily in that order. They are in the 50-100,000 lines range. CVE approaches that ballpark when client and server are bundled together. Ralph Griswold used to brag that Icon programs were often 10x smaller than corresponding programs in mainstream languages, so this language designed for programs mostly under 1,000 lines is applicable for a far wider range of software problems than it sounds. While Icon was designed for programming in the small, its size limits have gradually been eliminated. Unicon has further improved scalability in multiple aspects of the implementation, both the compiler/linker and the runtime system. In addition, Unicon was explicitly intended to further support larger scale software systems, and that is why classes and packages were added. Clint Those quotes don't answer all the questions, like what maintainers go through, or how long it takes new contributors to get up to speed, but as anecdotes, I now feel quite comfortable telling anyone and everyone that Unicon is up to the task of supporting large scale development and deployments, *along with the small*. .. [#complex] I once overheard a C++ engineer with a problem distilling a Grady/Booch style Rapid Application Development system output into a usable API for the project at hand. (I was programming a Forth system that was being upgraded as part of the C++ project, working in the same office space (the big rewrite flopped eventually)). There was a Tcl/Tk prototype ready for user screening but no easy way of getting smooth data flow across systems, due to the utterly complex cloud diagrams to C++ class data interactions. 1995 timeframe. Icon, a one day development effort, decoding the RAD binaries and text, creating templated sources in C++, Forth, Tcl/Tk (all of it simplified, as a prototype) with surprisingly accurate (to those experiencing first exposure to Icon) data sub fielding in drop downs linked to recursive sub lists. *Ok, two days, and an all nighter; from the middle of one working day until near the end of the next, to a working demo*. Source code all generated by an Icon translator from RAD to programming languages, data access names, structures, all synced for inter-system transfer. RAD to C++, Forth, Tcl/Tk; thanks to Icon. I had overheard and then bragged that their Grady/Booch project plan wasn't too big or complex for Icon version 6; then had to deliver. There was motivation with nerd points at stake. Icon shone, but no one really noticed. Management was more impressed by having (prototype) GUI screens with data connectivity for the big project than how it was developed overnight, leveraging goal directed evaluation, string scanning, and utterly flexible Icon data structures. And yes, further use uncovered some interesting hot spots when it came to performance. Learning done the hard way. Implicit back tracking can lead to unnecessary cycles if some care is not shown when nesting conditionals; for instance, the choice of early truth detection in loops can mean the difference between impressing the team, or losing nerd cred on bets and brags. It is worth spending some time with Unicon profiling, and memory map visualization. http://www2.cs.arizona.edu/icon/docs/docs.htm Get used to the feel of common case decision order for tests across a set of fields in an application. It takes practise when trying to shave branches off a decision tree (or to avoid computing completely new forests needlessly). There will likely be some hard to figure out fails when gaining wisdom in Unicon, but the language can handle extremely complicated data mashing, with flair; and should be used so, repeatedly. -------- .. index:: benchmark .. _benchmark: Unicon Benchmark Suite ====================== There is a Technical Report (UTR16a) 2014-06-09, by Shea Newton and Clinton Jeffery detailing ``A Unicon Benchmark Suite`` http://unicon.org/utr/utr16.pdf and sourced in the Unicon source tree under ``doc/utr/utr16.tex``. The results table shows that ``unicon`` runs of the benchmarks range from - ``345.2x`` (``n-body`` calculation) to - ``1.6x`` for the ``thread-ring`` trial, compared to the C code baseline timings. ``uniconc`` compiled versions range from - ``57.9x`` (``n-body``) to - ``0.6x`` (``regex-dna``) of the C baseline. Uniconc increasing the ``n-body`` run speed by a factor of ``6`` compared to the :ref:`icode` interpreter. The ``regex-dna`` trial actually ran faster with Uniconc than in C. Take a look at the TR for full details. *With another caveat; runtime vs development time.* There should be a column in any benchmark result sheet that quantifies the development time to get correctly functioning programs. I'd wager that Unicon would shine very bright in that column. run-benchmark ------------- You can run your own test pass in the :file:`tests/bench` sub-directory of the source tree. :: prompt$ cd tests/bench prompt$ make prompt$ ./run-benchmark A local pass came up looking like :: prompt$ make ... ./generate generating input files.............done! prompt$ ./run-benchmark Times reported below reflect averages over three executions. Expect 2-20 minutes for suite to run to completion. Word Size Main Memory C Compiler clock OS 64 bit 7.272 GB gcc 5.4.0 3.4 GHz UNIX CPU 4x AMD A10-5700 APU with Radeon(tm) HD Graphics Elapsed time h:m:s |Concurrent | benchmark |Sequential| |Concurrent| |Performance| concord concord.dat 3.213 N/A deal 50000 2.469 N/A ipxref ipxref.dat 1.345 N/A queens 12 3.554 N/A rsg rsg.dat 2.815 N/A binary-trees 14 5.172 7.736 0.668x chameneos-redux 65000 N/A 5.706 fannkuch 9 3.601 N/A fasta 250000 3.174 N/A k-nucleotide 150-thou.dat 5.153 N/A mandelbrot 750 13.877 7.662 1.811x meteor-contest 600 4.793 N/A n-body 100000 4.326 N/A pidigits 7000 2.903 N/A regex-dna 700-thou.dat 4.231 3.452 1.225x reverse-complement 15-mil.dat 4.828 N/A spectral-norm 300 3.469 N/A thread-ring 700000 N/A 6.460 To compare (and take part) visit http://unicon.org/bench If there are no results for your particular machine type and chipset on the Accumulated Results chart, :ref:`clint` collects summaries with information on how to report them at http://unicon.org/bench/resform.html The :file:`makefile` creates some fairly large test files, so you'll probably want to clean up after running the benchmark pass. :: prompt$ make clean prompt$ rm *-thou.dat *-mil.dat ipxref.dat Unfortunately, ``make clean`` does not remove the benchmarking data files. .. only:: html .. -------- :ref:`genindex` | Previous: :doc:`monitoring` | Next: :doc:`ipl` |