1
$\begingroup$

I'm looking for an application that I can compile live, in class to show the students that compilation takes time, but brings speed benefits in the end, whereas interpreted languages always take time, but need no compiling.

Of course I can just simply compile and run some hello world (e.g. this FORTRAN version), but that would just zip through in microseconds, whereas compiling, say, Firefox would take forever, involve many confusing additional steps and be way too complicated to illustrate the point (and probably fail anyways).

Ideally, I'm looking for an application that takes a minute to 3 (on a tad older hardware, think 5 years old i5) to compile, and really just takes one gfortran -o hello hello.f90 like command to get started and results in a binary that I can run in terminal that produces some simple and easily visible result (A plot? Some calculator?).

I'd prefer fortran or C and it all should be happening on a Linux system.

The point of that application would not be to show anything in fortran or C, or to detail the intricacies of compiling, it's solely to show that this can take time, but generally is fast afterwards. I intend to maybe talk 5 minutes about the whole thing, about 3 of which, it'll compile in the background. The main subject of the course is teaching python, an interpreted language.

$\endgroup$

2 Answers 2

3
$\begingroup$

One crazy suggestion, install Python from source. Configure time about 42 sec. and compile time either around 3 minutes, or 48 sec with make -j.

Your class will be using Python anyway, and seeing how long it takes to build from C vs. how long it takes to run Hello World in Python ought to begin to give them some appreciation of the differences.


Otherwise, two options that I'd considered are one with a relative fast compile and the other with a longer compile.

Fast compile - C source

The option for a fast, but watchable, compile is curl, which is probably already on the system. curl is written in C. It is available on GitHub. curl is released under its own license which is similar to MIT/X, but not identical. If curl is on the system already you can perform the configure and make commands, and then stop. The students will be able to see what goes into the process of compiling it, and you won't have to modify your system with the actual install. The original install of curl can be used to show what it does. Of course, once you demonstrate the speed of the binary it's nice to have a flashy result. As curl does not require the returned file to be rendered in a browser, you could have a simple ASCII-art file to download. Hosted on the same computer, and using the 127.0.0.1 IP would, of course, be the fastest, and best show of the difference between compile and run times. A simple demo of what could be downloaded could be "HELLO CLASS". curl https://www.gypsyspellweaver.work/hello_class or from the Gist,

My expirements, using an AMD FX 8300 8-core CPU, gives configure times in the one minute range and compile times in the one minute range. In both cases there shouldn't be the need to add any switches or options, and while speaking you won't have to think about what options was I supposed to use? for the commands. Timing your delivery around the process should be reasonably simple, and the process is fast enough to not become boring for the students to observe.

Slow compile - FORTRAN source

There is a project for physics (good place to look for FORTRAN programs) called Octopus for doing virtual experiments. Octopus is released under the GPL ver 2 license, and contains external libraries licensed under a variety of other OpenSource license. The current list of licenses can be found in the GitLab repository. The program does have some dependencies, all of which were easily found in my SUSE repositories for simple installation.

Again, using my system, I found the ./configure command to be quite fast, typically under 30 sec. The compile time, however, was much longer, avg. of 8 min. Use of the -j flag on make was significant in this case, avg. of 3 minutes to compile. As in the curl case, there's no need of switches or options (unless you want to use the -j swwitch on make for the time savings.

Octopus dependencies

The non-optional dependencies listed on the installation page are:

Since SUSE had these in their repos, I'm guessing they're likely in the repos of other major repos. Just be sure that the development packages, -devel or -dev, are installed as those include the headers needed to build other programs, such as Octopus.

In the case of Octopus I have no real idea what would make a fast, and simple, output for the maiden launch of the program. Their website gives a simple run to use. It involves creating an input file, always named imp, in the current directory and running octopus. The simple input file is:

CalculationMode = gs

%Coordinates
 'H' | 0 | 0 | 0
%

Using that example, the best I can create for a demo is to trap the output to a log file, then display the convergence result data. octopus >&log; cat static/convergence. Using my system as a reference, the typical run time is between 1.000s and 1.025s. Changing the command to octopus >&log; octopus >&log; cat static/convergence so that the command runs twice, using the results of the first run to fine-tune the results of the second run, adds approx. .650s to each run. The output of the single run is a table of 9 results and the double run produces a table of one result.


As a final resource, if none of these ideas work for your situation, or instructional style, you could look through the list of programs, organized by language, on dmoz. Surely something there will work for your presentation.

$\endgroup$
1
  • 1
    $\begingroup$ That sounds like what I'm looking for! Gotta play around with it a bit first to see if it's really the answer I was looking for. $\endgroup$
    – JC_CL
    Commented Apr 24, 2020 at 14:44
1
$\begingroup$

Actually you have a misconception about compilers vs interpreters in the modern age. The stages of compilation (simplified) are

Syntax Analysis (scanners - find the words and symbols and replace with internal values)

Structural Analysis (parsers - build a tree equivalent)

Optimization (maybe several stages - tree pruning etc)

Code Generation (output the translated code)

The only difference between this and an interpreter is the last step. The output of the optimization code (likely still a tree but possibly an abstract machine code) is walked at execution.

So, it is incorrect to say that an interpreter doesn't need 'compilation'. What it doesn't need is code generation which is normally a small part of the time involved in running a compiler. The scanner takes up most of the time unless the optimizations are very sophisticated.

I'll leave this here for now but a simple language can be defined with a BNF. If it is LL(1) then a recursive descent parser is pretty simple. If it has few symbols then a scanner is pretty simple. Ignore optimization (null optimization). Then you can either produce a code generator for some simple abstract machine simulator that looks like machine language (a compilation) or you can directly interpret the tree output by the parser (an interpretation).

But no reasonable interpreter today would work directly off of the source code during the interpretation. Scanning and parsing at least are the same as in a compiler. And scanning is the slow part since a decision needs to be made at every character of the input. Actually several decisions for many.

I don't have a FOSS application for you, but it isn't terribly difficult to build one for a simple input language. One of the easiest is a prefix version of arithmetic, since it is easy to parse into a tree:

+ 3 4 should yield 7

+ 3 * 4 5 should yield 23.

This is Polish Notation, so named since no one wanted to remember how to spell Łukasiewicz, the name of its inventor.

If you stick to single digit literal values (no variables) and single character operators, it is dead simple to both scan and parse. The parser is the driver and calls the scanner for the next symbol. The parser produces a tree.

Whether it produces an output value (an interpreter) or an executable (a compiler is up to you. The simplicity comes from the fact that machine code is also often just polish notation. But if the architecture is a stack machine with all operations done on the stack the compiler version gets a bit more complex.

Then the first example translates to

push 3

push 4

add

print (producing 7)

However, the value of compilation isn't obvious from this example since the scanner and parser are so trivial (hence fast).

But it becomes more interesting if you make the language a bit more complicated. Suppose, for example that you permit both integer and non-integer values. Now you have to distinguish between integer and floating point operations. The beauty of pre-processing is that the disambiguation needs to be done only once, whether in a compiler or interpreter, not on every pass if the operation is in a loop, for example.

And if you add non-literal values, such as variables, then you also probably need a symbol table. Again, process once use many in both the interpreter and the compiler.

The overall lesson here is that a compiler and an interpreter differ by a lot less than most people think they do.


If you understand Pascal then an absolutely elegant introduction to the structure of compilers is On Pascal Compilers by Per Brinch Hansen. There aren't many CS books that can be described as literature, but this is one. Not cheap anymore, but it will be in many libraries.


I also just found the following which might be suitable for a first compiler course or as the basis of a demo: http://eli-project.sourceforge.net/pascal_html/pascal-.html. I haven't had a chance to evaluate it, though. My old compiler course used something similar.

$\endgroup$
6
  • $\begingroup$ Of course an interpreted language isn't just run as is. All the steps that a compiler does are still done, but while the program - or script - is running, every time it is running, whereas with compiled software, you'd have to do the first three steps only once. Hence using an interpreted language is only practical for rather small projects. I just want to have an illustrative example for this issue. $\endgroup$
    – JC_CL
    Commented Apr 23, 2020 at 11:40
  • $\begingroup$ No, not true. The first steps are done only once in any modern interpreter. Remember that Pascal was often run on a p-machine. $\endgroup$
    – Buffy
    Commented Apr 23, 2020 at 11:48
  • $\begingroup$ I'm not an expert on that, but I'm pretty sure that you are going to do all of those steps again on your machine if I did send you mydemo.py and asked you to run it. $\endgroup$
    – JC_CL
    Commented Apr 23, 2020 at 12:07
  • 1
    $\begingroup$ I am an expert, actually. But if you give me source, I need to run all the steps, of course, unless the "interpreter" will export an abstract tree that I can interpret. That is how the p-machine worked. And since it was implemented on various hardware you could create the abstract tree (first three steps) and then "run" on a different architecture. $\endgroup$
    – Buffy
    Commented Apr 23, 2020 at 12:51
  • 1
    $\begingroup$ Actually, Java works this way too. The "compiler" outputs JVM code, which runs on an interpreter - the Java Virtual Machine. There are tools that will take the JVM code and produce real machine code for your architecture, but they are used mostly in high performance applications. $\endgroup$
    – Buffy
    Commented Apr 23, 2020 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.