Blacklisting users for inbound mail in Exim

18.09.2016 12:00

You can prevent existing local users from receiving mail by redirecting them to :fail: in /etc/aliases. For example, to make SMTP delivery to list@... fail with 550 Unrouteable address:

list: :fail:Unrouteable address

See special items section in the Redirect router documentation.

By default, Exim in Debian will attempt to deliver mail for all user accounts, even non-human system users. System users (like list above) typically don't have a traditional home directory set in /etc/passwd. This means that mail for them will get stuck in queue as Exim tries and fails to write to their mailbox. Because spam also gets sent to such addresses, mail queue will grow and various things will start to complain. Traditionally, mail for system accounts is redirected to root in /etc/aliases, but some accounts just receive a ton of spam and it's better to simply reject mail sent to them.

Another thing worth pointing out is the Handling incoming mail for local accounts with low UID section in README.Debian.gz in case you want to reject mail sent to all system accounts.

This took way too much time to figure out. There's a ton of guides on how to blacklist individual users for outgoing mail, but none I could find for cases like this. I was half-way into writing a custom router before I stumbled upon this feature.

Posted by Tomaž | Categories: Code | Comments »

Script for setting up multiple terminals.

16.09.2016 19:04

Sometimes I'm working on software that requires running a lot of different inter-dependent processes (are microservices still a thing?). Using systemd or some other init system for starting up such systems is fine for production. While debugging something on my laptop however it's useful to have each process running in its own X terminal. This allows me to inspect any debug output and to occasionally restart something. I used to have scripts that would run commands in individual GNU screen sessions, but that had a number of annoying problems.

I've recently came up with the following:


set -ue

if [ "$#" -ne 1 ]; then
	echo "USAGE: $0 path_to_file"
	echo "File contains one command per line to be started in a terminal window."
	exit 1

cat "$1" | while read CMD; do
	if [ -z "$CMD" -o "${CMD:0:1}" = "#" ]; then


	cat > "$RCFILE" <<END
source ~/.bashrc
history -s $CMD
echo $CMD

	gnome-terminal -e "/bin/bash --rcfile \"$RCFILE\""
	rm "$RCFILE"

This script reads a file that contains one command per line. Empty lines and lines starting with a hash sign are ignored. For each line it opens a new gnome-terminal (adjust as needed - most terminal emulators support the -e argument) and runs the command in a way that:

  • The terminal doesn't immediately close after the command exits. Instead it drops back to bash. This allows you to inspect any output that got printed right before the process died.
  • The command is printed on top of the terminal before the command runs. This allows you to identify the terminal running a particular process in case that is not obvious from the command output. For some reason, gnome-terminal's --title doesn't work.
  • The command is appended on top of bash' history list. This allows you to easily restart the process that died (or you killed with Ctrl-C) by simply pressing the up cursor and enter keys.
Posted by Tomaž | Categories: Code | Comments »

The NumPy min and max trap

30.08.2016 20:21

There's an interesting trap that I managed to fall into a few times when doing calculations with Python. NumPy provides several functions with the same name as functions built into Python. These replacements typically provide a better integration with array types. Among them are the min() and max(). In vast majority of cases, NumPy versions are a drop-in replacement for built-ins. In a few, however, they can cause some very hard-to-spot bugs. Consider the following:

import numpy as np

print(max(-1, 0))
print(np.max(-1, 0))

This prints (at least in NumPy 1.11.1 and earlier):


Where is the catch? The built-in max() can be used in two distinct ways: you can either pass it an iterable as the single argument (in which case the largest element of the iterable will be returned), or you can pass multiple arguments (in which case the largest argument will be returned). In NumPy, max() is an alias for amax() and that only supports the former convention. The second argument in the example above is interpreted as array axis along which to perform the maximum. It appears that NumPy thinks axis zero is a reasonable choice for a zero-dimensional input and doesn't complain.

Yes, recent versions of NumPy will complain if you have anything else than 0 or -1 in the axis argument. Having max(x, 0) in code is not that unusual though. I use it a lot as a shorthand when I need to clip negative values to 0. When moving code around between scripts that use NumPy, those that don't and IPython Notebooks (which do "from numpy import *" by default), its easy to mess things up.

I guess both sides are to blame here. I find that flexible functions that interpret arguments in multiple ways are usually bad practice and I try to leave them out of interfaces I design. Yes, they are convenient, but they also often lead to bugs. On the other hand, I would also expect NumPy to complain about the nonsensical axis argument. Axis -1 makes sense for a zero-dimensional input, axis 0 doesn't. The alias from max() to amax() is dangerous (and as far as I can see undocumented). A possible way to prevent such mistakes would be to support only the named version of the axis argument.

Posted by Tomaž | Categories: Code | Comments »

Linux loader for DOS-like .com files

25.08.2016 18:09

Back in the olden days of DOS, there was a thing called a .com executable. They were obsolete even then, being basically inherited from CP/M and replaced by DOS MZ executables. Compared to modern binary formats like ELF, .com files were exceedingly simple. There was no file header, no metadata, no division between code and data sections. The operating system would load the entire file into a 16-bit memory segment at offset 0x100, set the stack and the program segment prefix and jump to the first instruction. It was more of a convention than a file format.

While this simplicity created many limitations, it also gave rise to many fun little hacks. You could write a binary executable directly in a text editor. The absence of headers meant that you could make extremely small programs. A minimal executable in Linux these days is somewhere in the vicinity of 10 kB. With some clever hacking you might get it down to a hundred bytes or so. A small .com executable can be on the order of bytes.

A comment on Hacker News recently caught my attention. One could write a .com loader for Linux pretty easily. How easily would that be? I had to try it out.

/* Map address space from 0x00000 to 0x10000. */

void *p = mmap(	(void*)0x00000, 0x10000, 
		-1, 0);

/* Load file at address 0x100. */

(a boring loop here reading from a file to memory.)

/* Set stack pointer to end of allocated area, jump to 0x100. */

	"mov    $0x10000, %rsp\n"
	"jmp    0x100\n"

This is the gist of it for Linux on x86_64. First we have to map 64 kB of memory at the bottom of our virtual address space. This is where NULL pointers and other such beasts roam, so it's unallocated by default on Linux. In fact, as a security feature the kernel will actively prevent such calls to mmap(). We have to disable this protection first with:

$ sysctl vm.mmap_min_addr=0

Memory mappings have to be aligned with page boundaries, so we can't simply map the file at address 0x100. To work around this we use an anonymous map starting at address 0 and then fill it in manually with the contents of the .com file at the correct offset. Since we have the whole 64 bit linear address space to play with, choosing 0x100 is a bit silly (code section usually lives somewhere around 0x400000 on Linux), but then again, so is this entire exercise.

Once we have everything set up, we set the stack pointer to the top of our 64 kB and jump to the code using some in-line assembly. If we were being pedantic, we could set up the PSP as well. Most of it doesn't make much sense on Linux though (except for command-line parameters maybe). I didn't bother.

Now that we have the loader ready, how do we create a .com binary that will work with it? We have to turn to assembly:

    call   greet
    mov    rax, 60
    mov    rdi, 0

# A function call, just to show off our working stack.
    mov     rax, 1
    mov     rdi, 1
    mov     rsi, msg
    mov     rdx, 14

    msg db      "Hello, World!", 10

This will compile nicely into an x86_64 ELF object file with NASM. We then have to link it into an ELF executable using a custom linker script that tells the linker that all sections will be placed in a chunk of memory starting at 0x100:

	RAM (rwx) : ORIGIN = 0x0100, LENGTH = 0xff00

Linker will create an executable which contains our code with all the proper offsets, but still has all the ELF cruft around it (it will segfault nicely if you try to run it with kernel's default ELF loader). As the final step, we must dump its contents into a bare binary file using objcopy:

$ objcopy -S --output-target=binary hello.elf

Finally, we can run our .com file with our loader:

$ ./loader
Hello, World!

As an extra convenience, we can register our new loader with the kernel, so that it will be invoked each time you try to execute a file with the .com extension (update-binfmts is part of the binfmt-support package on Debian):

$ update-binfmts --install com /path/to/loader --extension com
$ ./
Hello, World!

And there you have it, a nice 59 byte "Hello, World" binary for Linux. If you want to play with it yourself, see the complete example on GitHub that has a Makefile for your convenience. If you make something small and fun with it, please drop me a note.

One more thing. In case it's not clear at this point, this loader will not work with DOS executables (or CP/M in that case). Those expect to be run in 16-bit real mode and rely on DOS services. Needless to say, my loader makes no attempts to provide those. Code will run in the same environment as other Linux user space processes (albeit at a weird address) and must use the usual kernel syscalls. If you want to run old DOS stuff, use DOSBox or something similar.

Posted by Tomaž | Categories: Code | Comments »

A thousand pages of bullet journal

12.08.2016 17:27

A few weeks ago I filled the 1000th page in my Bullet Journal. Actually, I don't think I can call it that. It's not in fact that much oriented around bullet points. It's just a series of notebooks with consistently numbered, dated and titled pages for easy referencing, monthly indexes and easy-to-see square bullet points for denoting tasks. Most of the things I said two years ago still hold, so I'll try not to repeat myself too much here.

A thousand pages worth of notebooks.

Almost everything I do these days goes into this journal. Lab notes, sketches of ideas, random thoughts, doodles of happy ponies, to-do lists, pages worth of crossed-out mathematical derivations, interesting quotes, meeting notes and so on. Writing things down in a notebook often significantly clears them up. Once I have a concise and articulated definition of a problem, the solution usually isn't far away. Pen and paper helps me keep focus at talks and meetings, much like an open laptop does the opposite.

Going back through past notes gives a good perspective on how much new ideas depend on the context and mindset that created them. An idea for some random side-project that seems interesting and fun at first invariably looks much less shiny and worthy of attention after reading through the written version a few days or weeks later. I can't decide though whether it's better to leave such a thing on paper or hack together some half-baked prototype before the initial enthusiasm fades away.

The number of pages I write per month appears to be increasing. That might be because I settled on using cheap school notebooks. I find that I'm much more relaxed scribbling into a 50 cent notebook than ruining a 20€ Moleskine. Leaving lots of whitespace is wasteful, but helps a lot with readability and later corrections. Yes, whipping out a colorful children's notebook at an important meeting doesn't look very professional. Then again, most people at such meetings are too busy typing emails into their laptops to notice.

Number of pages written per month.

As much as it might look like a waste of time, I grew to like the monthly ritual of making an index page. I like the sense of achievement it gives me when I look back at what I've accomplished the previous month. It's also an opportunity for reflection. If the index gets hard to put all on one page, that's a good sign that the previous month was all too fragmented and that too many things wanted to happen at once.

The physical nature of the journal means that I can't carry the whole history with me at all times. That is also sometimes a problem. It is an unfortunate feature of my line of work that it is not uncommon for people to want to have unannounced meetings about a topic that was last discussed half a year ago. On the other hand, saying that I don't have my notes on me at that moment does present an honest excuse.

Indexes help, but finding things can be problematic. Then again, digital content (that's not publicly on the web) often isn't much better. I commonly find myself frustratingly searching for some piece of code or a document I know exists somewhere on my hard disk but can't remember any exact keyword that would help me find it. I considered making a digital version of monthly indexes at one point. I don't think it would be worth the effort and it would destroy some of the off-line quality of the notebook.

As I mentioned previously, gratuitous cross-referencing between notebook pages, IPython notebooks and other things does help. I tend not to copy tasks between pages, like in the original Bullet Journal idea. For projects that are primarily electronics related though, I'm used to keeping a separate folder with all the calculations and schematics, a habit I picked up long ago. There are not many such projects these days, but I did on one occasion photocopy pages from the notebook. I admit that made me feel absolutely archaic.

Posted by Tomaž | Categories: Life | Comments »

Oxidized diodes

08.08.2016 20:41

Out of curiosity, I salvaged these three LEDs from an old bicycle light that stopped working. Rain must have gotten inside it at one point, because the copper on the PCB was pretty much eaten away by the electrolysis. The chip that blinked the light was dead, but LEDs themselves still work.

Three LEDs damaged by water compared to a new one.

It's interesting how far inside the bulb the steel leads managed to oxidize. You can see that the right hand lead (anode) on all LEDs is brown all the way to the top, where a bond wire connects it to the die. I would have thought that the epoxy makes a better seal. For comparison, there's a similar new LED on the right. It's also interesting that the positive terminal was more damaged than the negative. On the right-most LED the terminal was actually eaten completely through just outside the bulb (although poking the remains with a needle still lights up the LED, so obviously the die itself was not damaged).

Posted by Tomaž | Categories: Life | Comments »

Recent free software work

30.07.2016 9:25

I've done a bit of open source janitorial work recently. Here is a short recap.

jsonmerge is a Python module for merging a series of JSON documents using arbitrarily complicated rules inspired by JSON schema. I have developed the gist of it with Sarah Bird during EuroPython 2014. Since then I've been maintaining it, but not really doing any further development. 1.2.1 release fixes a bug in internal handling of JSON references, reported by chseeling. The bug caused a RefResolutionError to be raised when merging properties with slash or tilde characters in them.

I believe jsonmerge is usable in its current form. The only larger problem that I know of is the fact that automatic schema generation for merged documents would need to be rethought and probably refactored. This would address incompatibility with jsonschema 2.5.0 and improve handling of some edge cases. get_schema() seems to be rarely used however. I don't have any plans to work on this issue at the moment as I'm not using jsonmerge myself. I would be happy to look into any pull requests or work on it myself if anyone would offer a suitable bounty.

aspell-sl is the Slovenian dictionary for GNU Aspell. Its Debian package was recently orphaned. As far as I know, this is currently the only Slovenian dictionary included in Debian. I took over as the maintainer of the Debian package and fixed several long-standing packaging bugs to prevent it from disappearing from next Debian stable release. I haven't updated the dictionary however. The word list, while usable, remains as it was since the last update somewhere in 2002.

The situation with this dictionary seems complicated. The original word list appears to have been prepared in 2001 or 2002 by a diverse group of people from JSI, LUGOS, University of Ljubljana and private companies. I'm guessing they were funded by the now-defunct Ministry of Information Society which was financing localization of open source projects around that time. The upstream web page is long gone. In fact, aspell itself doesn't seem to be doing that well, although I'm still a regular user. The only free and up-to-date Slovenian dictionary I've found on-line was in the Slovenian Dictionary Pack for LibreOffice. It seems the word list from there would require relatively little work to be adapted for GNU Aspell (Hunspell dictionaries use very similar syntax). However, the upstream source of data in the pack is unclear to me and I hesitate to mess too much with things I know very little about.

z80dasm is a disassembler for the Zilog Z80 microprocessor. I forked the dz80 project by Jan Panteltje when it became apparent that no freely available disassembler was capable of correctly disassembling Galaksija's ROM. The 1.1.4 release adds options for better control of labels generated at the start and end of sections in the binaries. It also fixes a memory corruption bug that could sometimes lead to a wrong disassembly.

Actually, I committed these two changes to the public git repository three years ago. Unfortunately it seems that I have forgotten to package them into a new release at that time. Now I also took the opportunity to update and clean up the autotools setup. I'll work towards updating the z80dasm Debian package as well. z80dasm is pretty much feature complete at this point and except any further bug reports I don't plan any further development.

Posted by Tomaž | Categories: Code | Comments »

Newsletters and other beasts

23.07.2016 10:23

It used to be that whenever someone wrote something particularly witty on Slashdot, it was followed by a reply along the lines of "I find your ideas intriguing and would like to subscribe to your newsletter". Ten-odd years later, it seems like the web took that old Simpsons joke seriously. These days you can hardly follow a link on Hacker News without having a pop-up thrown in your face. Most articles now end with a plea for an e-mail address, and I've even been to real-life talks where the speakers continuously advertised their newsletter to the audience.

Recently I've been asked several times why I didn't support subscriptions by e-mail, like every other normal website. The short answer is that I keep this blog in a state that I wish other websites I visit would adopt. This means no annoying advertisements, respecting your privacy by not loading third-party Javascript or tracking cookies, HTTPS and IPv6 support, valid XHTML... and good support for the Atom standard. Following the death of Google Reader, the world turned against RSS and Atom feeds. However, I still find them vastly more usable than any alternative. It annoys me that I can't follow interesting people and projects on modern sites like Medium and through this channel.

Twitter printer at 32C3

That said, you now can subscribe by e-mail to my blog, should you wish to do so (see also sidebar top-right). The thing that finally convinced me to implement this was hearing that some of you use RSS-to-email services that add their own advertisements to my blog posts. I did not make this decision lightly though. I used to host mailing lists and know what an incredible time sink they can be, fighting spam, addressing complaints and so on. I don't have that kind of time anymore, so using an external mass-mailing service was the only option. Running my own mail server in this era is lunacy enough.

Mailchimp seems to be friendly, so I'm using that at the moment. If turns to the dark side and depending how popular the newsletter gets, I might move to some other service - or remove it altogether. For the time being I consider this an experiment. It's also worth mentioning that while there are no ads, Mailchimp does add mandatory tracking features (link redirects and tracking pixels). Of course, it also collects your e-mail address somewhere.

Since I'm on the topic of subscriptions and I don't like writing meta posts like this, I would also like to mention here two ways of following my posts that are not particularly well known: if you are only interested in one particular topic I write about, you can search for it. Search results page has an attached Atom feed you can follow that only contains posts related to the query. If you on the other hand believe that Twitter is the new RSS, feel free to follow @aviansblog (at least until Twitter breaks the API again).

Posted by Tomaž | Categories: Life | Comments »

Visualizing frequency allocations in Slovenia

15.07.2016 17:34

If you go to a talk about dynamic spectrum access, cognitive radio or any other topic remotely connected with the way radio spectrum is used or regulated, chances are one of the slides in the introduction will contain the following chart. The multitude of little colorful boxes is supposed to impress on the audience that the spectrum is overcrowded with existing allocations and that any future technology will have problems finding vacant frequencies.

I admit I've used it myself in that capacity a couple of times. "The only space left unallocated is beyond the top-left and bottom-right edges, below 9 kHz and above 300 GHz", I would say, "and those frequencies are not very useful for new developments.". After that I would feel free to advertise the latest crazy idea that will magically create more space, brushing away the fact that spectrum seems to be like IPv4 address space - when there's real need, powers that be always seem to find more of it.

United States frequency allocations, October 2003.

Image by U.S. Department of Commerce

I was soon getting a bit annoyed by this chart. When you study it you realize it's showing the wrong thing. The crowdiness of it does fit with the general story people are trying to tell, but the ITU categories shown are not the problematic part of the 100 year legacy of radio spectrum regulations. I only came to realize that later though. My first thought was "Why are people discussing future spectrum in Europe, using a ten year old chart from U.S. Department of Commerce showing the situation on the other side of the Atlantic?"

Although there is a handful of similar charts for other countries on the web, I couldn't find one that I would be happy with. So two years back, with a bit of free time and encouraged by the local Open Data group, I set off to make my own. It would show the right thing, be up to date and describe the situation in my home country. I downloaded public PDFs with the Slovenian Electronic Communications Act, the National Table of Frequency Allocations and also a few assorted files with individual frequency licenses. Then I started writing a parser that would turn it all into machine-readable JSON. Then, as you might imagine if you ever encountered the words "PDF", "table" and "parsing" in the same sentence, I gave up after a few days of writing increasingly convoluted and frustrating Python code.

Tabela uporabe radijskih frekvenc

Fast forward two years and I was again going through some documents that discuss these matters. I remembered this old abandoned project. In the mean time, new laws were made, the frequency allocation table was updated several times and the PDFs were structured a bit differently. This meant I had to throw away all my previous work, but on the other hand new documents looked a bit easier to parse. I again took the challenge and this time I managed to parse most of the basic NTFA into JSON after a day of work and about 350 lines of Python.

I won't dive deep into technicalities here. I started with the PDF converted to whitespace-formatted UTF-8 text using the pdftotext tool which comes with Poppler. Then I had a series of functions that successively turned text into structured data. This made it easy to inspect and debug each step. Some of the steps included were "fix typos in document" (there are several, by the way, including inconsistent use of points and commas for decimal marks), "extract column widths", "extract header hierarchy", "normalize service names", etc. If there will be interest, I might present the details in a talk at one of the future Open Data Meetups in Ljubljana.

Once I had the data in JSON, drawing a visualization much like the U.S. one above took another 250 lines using matplotlib. Writing them was much more pleasant in comparison though. In hindsight, it would actually make sense to do the visualization part first, since it was much easier to spot parsing mistakes from the graphical representation than by looking at JSON.

Uporaba radijskih frekvenc v Sloveniji glede na storitev

While it is both local and relatively up-to-date, the visualization as such isn't very good. It still only works for conveying that general idea of fragmentation, but not much else. There are way too many categories for an unaided eye to easily match the colors in the legend with the bands in the graph. It would be much better to have an interactive version where you could point to a band and the relevant information would pop-up (or maybe even point you to the relevant paragraphs in the PDFs). Unfortunately this is beyond my meager knowledge of Javascript frameworks.

My chart also still just lists the ITU categories. Not only do they have very little to do with finding space for future allocations, they are useless for spotting interesting parts of the spectrum. For example, the famous 2.4 GHz ISM band doesn't stand out in any way here - it's listed simply under "FIXED, MOBILE, AMATEUR and RADIOLOCATION" services. All such interesting details regarding licensing and technologies in individual bands is hidden in various regulations, scattered across a vast amount of tables, appendices and different PDF documents. It is often in textual form that currently seems impossible to easily extract in an automated way.

I'm still glad that I now have at least some of this data in computer-readable form. I'm sure it will come handy in other projects. For instance, I might eventually use it to add some automatic labels to my real-time UHF and VHF spectrogram from the roof of the IJS campus.

I will not be publicly publishing JSON data and parsing code at the moment. I have concerns about its correctness and the code is so specialized for the specific document that I'm sure nobody will find it useful for anything else. However, if you have some legitimate use for the data, please send me an e-mail and I will be happy to share my work.

Posted by Tomaž | Categories: Life | Comments »

Raspberry Pi Compute Module eMMC benchmarks

03.07.2016 13:52

I have a Raspberry Pi Compute Module development kit on my desk at the moment. I'm doing some testing and prototyping because we're considering using it for a project at the Institute. The Compute Module is basically a small PCB with the Broadcom's BCM2835 system-on-chip, 4 GB of flash ROM on an eMMC connection and little else. Even providing power supply at a number of different voltages is left as an exercise for the user.

Raspberry Pi Compute Module

I was wondering how the eMMC flash performs compared to the SD card on the more common Pies. I couldn't find any good benchmarks on the web. Wikipedia says that the latest eMMC standard rivals SATA speeds, but there's not much info around on what kind the Compute Module uses. I've used Samsung's ARM Chromebook with eMMC flash a while ago and that felt pretty fast. On the other hand, watching package updates scroll by on the Compute Module gave me a feeling that it's quite sluggish.

To get some more objective benchmark, I decided to compare the I/O performance with my Raspberry Pi Zero. Zero uses the same BCM2835 SoC, so the results should be somewhat comparable. I used the SD card that originally came with Zero preloaded with the Noobs distribution. It only has the raspberry logo printed on it, so I don't know the exact model or manufacturer. Both Compute Module and Zero were running the latest Raspbian Jessie.

One surprising discovery during this benchmark was that CPU on Zero runs between 700 MHz and 1 GHz while the Compute Module will only run at 700 MHz. These are the ranges detected at boot by bcm2835-cpufreq and default /boot/config.txt that came with the Raspbian image (i.e. no special overclocking). Because of this I performed the benchmarks on Zero at 700 MHz and 1 GHz.

For comparison, I also ran the same benchmark on my Cubietruck that has an Allwinner A20 system-on-chip with SATA-connected Samsung EVO 840 SSD and runs vanilla Debian Jessie.

This is the benchmark script I used. For each run, I chose the fastest result out of 5:



while [ $I -lt $N ]; do
	hdparm -t $DEVICE

while [ $I -lt $N ]; do
	hdparm -T $DEVICE

while [ $I -lt $N ]; do
	dd if=/dev/zero of=tempfile bs=1M count=128 conv=fdatasync 2>&1

while [ $I -lt $N ]; do
	echo 3 > /proc/sys/vm/drop_caches
	dd if=tempfile of=/dev/null bs=1M count=128 2>&1

while [ $I -lt $N ]; do
	dd if=tempfile of=/dev/null bs=1M count=128 2>&1

Here is write performance, as measured by dd. I wonder if dd figures are affected by filesystem fragmentation since it writes an actual file that might not be contiguous. I've been using Zero for a while with this Raspbian image while the Compute Module has been freshly re-imaged. Fragmentation shouldn't be as significant as with spinning disks, but it probably still has some effect.

Comparison of write performance.

Read performance, as measured by hdparm as well as dd. To remove the effect of cache when measuring with dd, I explicitly dropped kernel block device caches before each run.

Comparison of read performance.

From this it seems Compute Module's eMMC flash is slightly faster than the SD card, both on read and writes when comparing to Zero running at the same CPU clock frequency. It's interesting that Zero's results change significantly with CPU frequency, which seems to suggest that some part of SD card I/O is CPU bound. That said, performance seems to be somewhere roughly on the same order of magnitude. Cubietruck is significantly faster than both. In light of this result, it's sad that never versions of Cubieboard (and cheap ARM SoCs in general) dropped the SATA interface.

Finally, I tested block device cache performance. This more or less shows only RAM and CPU performance and shouldn't depend on storage speed.

Comparison of cached read performance.

Interestingly, Zero seems to be somewhat faster than the Compute Module at 700 MHz here. /proc/cpuinfo shows a different revision, although it's not clear to me whether that marks board revision or SoC revision. It might be that processors in Zero and Compute Module are not identical pieces of silicon.

In the end, I should note that these results are not super accurate. Complexities of I/O benchmarking on Linux aside, there are several things that might have affected the results. I already mentioned different filesystem state. A different SD card in Zero might give very different results (I didn't have a second empty card at hand to try that). While Raspberry Pies were idle during these tests, Cubietruck was running my web server and various other little tidbits that tend to accumulate on such machines.

Posted by Tomaž | Categories: Digital | Comments »