It looks like you're new here. If you want to get involved, click one of these buttons!
Code Critique: nothing.py
TL;DR Extracting meaning from code requires understanding (modeling) what is left unsaid.
The code I'll be critiquing is an empty python script, perhaps the computer science equivalent of 4'33''.
Creating the code is trivial on any *nix machine:
$ python --version
$ touch nothing.py
$ ls -l
-rw-r--r-- 1 rountree rountree 0 Jan 18 14:06 nothing.py
touch creates and empty file with the specified filename, and
ls -l shows the size of the file is indeed 0. Running this code is about as exciting as you might expect.
$ python ./nothing.py $
The temptation is to think that nothing happened: we didn't ask Python to do anything, and it complied with alacrity. In most computing environments, that summary is a perfectly serviceable abstraction of what happened. But it is an abstraction, and interesting things happen when ground truth starts diverging too far from our abstraction of it.
Let's take a closer look.
$ strace python ./nothing.py execve("/usr/bin/python", ["python", "./nothing.py"], 0x7ffc76d71d08 /* 56 vars */) = 0 brk(NULL) = 0x55a94fcaf000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 ....
strace captures all of the calls that the Python interpreter makes into the linux kernel (e.g.,
brk), lists the parameters for each of those calls (
NULL) and the value that's returned (
0x55a94fcaf000), possibly with an explanation if there's an error (
-1 ENOENT (No such file or directory)). What each of those individual lines mean to a systems programmer is beyond the scope of this critique. The aggregate numbers are more interesting.
$ strace python ./nothing.py 2>&1 | wc -l 746
2>&1 is a bit of magic to put output routed to
strace uses) to
stdout (which the rest of the tools expect). The pipe
| routes the
stdout of the prior command to the
stdin of the next command.
wc -l simply counts how many lines there are in
stdin and outputs that number.
Roughly speaking, in order to run
nothing.py, our Python interpreter made 746-ish calls into the linux kernel.
Which seems a little excessive.
Through the magic of bash one-liners we can get a sense of what's going on.
$ strace python ./nothing.py 2>&1 | cut -d "(" -f 1 | sort | uniq -c | sort -rn | head 234 openat 99 fstat 98 read 87 stat 68 rt_sigaction 66 close 25 mmap 14 mprotect 8 brk 8 access [only the top 10 shown]
That's 234 distinct attempts to open files. On a laptop, that happens so fast as to be unnoticeable. On a supercomputer, however....
KULL  is a large multi-physics application developed at LLNL. When it was first run on Dawn, an IBM BlueGene/P system, its start-up exposed serious scaling challenges and significantly disrupted the entire computing center. The KULL executable was dynamically linked to about one thousand shared libraries, most of them staged in an NFS file system to ease maintenance. Loading these libraries during program start-up scaled extremely poorly. The time from job launch to the initial invocation of KULL’s
mainfunction, for instance, took about one hour at 2,048 MPI processes and ten hours at 16,384 processes. Further, during start-up, the NFS server was overwhelmed with load requests from KULL, and other users and jobs were unable to connect to the NFS while KULL start-up continued. [Frings 2013, emphasis in original]
The problem was that both the python libraries and those used by KULL itself were all located on single filesystem that the compute nodes accessed over the network. 16,384 processes starting up and simultaneously demanding 1,000+ libraries each utterly overwhelmed the filesystem. The fix was clever enough to be published (distribute libraries via a tree network, see the paper for details), but doesn't inform this critique.
And with that background in place we can now do a code critique.
So what does this code --- or lack of code --- actually mean?
Trawling through the Python source code to understand its initialization process would be tedious and error-prone. It's much faster to run Python under instrumentation (in this case,
strace) and watch what it does. The empty script guaranteed that all the activity we'd be seeing would be coming from Python, not our code.
Ok, that's what you did, but you still haven't told me what it means.
First, the text of code is an unreliable guide to its performance. There may be a strong guarantee as to correct behavior, but there are rarely any guarantees about how that behavior will be accomplished. The behavior of code is generally more important than its text, but that behavior is contingent upon everything from the rest of the computing ecosystem to the society in which the code operates.
Second, there might be a temptation to claim that if we had enough information about the system we could have perfect knowledge of the code's behavior. We're never going to be able to get that much information out of nontrivial systems, so we're left with using simplified models. As Box remarked: "All models are wrong, but some are useful." Becoming skilled in the art of system design, programming or code critiquing requires figuring out where your model can simplify without doing too much violence to your results.
Third, CCS understandably puts a great deal of emphasis on text. This example is a corner case of how that approach can break down. Unfortunately, learning how to do code instrumentation is at least as difficult as learning how to code in the first place, and there's definitely a lack of friendly guides to performance analysis. One way around the problem might be more collaboration.
Questions for discussion:
A. What examples of implied, misleading or ironic code have you come across in your own work?
B. What tools do you use to compare code-as-text versus code-as-performance?
C. How should CCS handle implied, misleading or ironic code?
Notes: I generated the example using Python 2.7.17 running on a Linux 5.0.0-37 kernel provided by Ubuntu 18.04.3 (Bionic Beaver). Running Python 3.6.9 on that same machine only generates 67 calls to
OSX users can substitute
strace, but doing so requires running as root and disabling system integrity protection.