How fast is out-of-the-box buffered disk I/O in C++, Go, Python, Ruby, and Scala on Linux?
After evaluating disk I/O on Raspberry Pi, I was curious about doing the same on my laptop. So, I ran the same evaluation on a laptop.
I used the same five programs written in C++, Go, Python, Ruby, and Scala from the previous/above evaluation. [The programs are listed at the end of the post.]
Each program relied on default buffered I/O support provided by the language platform to write 256 million bytes (values 0 thru 255) one at a time into a file and then read these bytes one at a time from the file. The time for these actions was measured at nanosecond granularity (with the exception of fractional second granularity in case of Python) using timing support available via standard libraries. These actions were repeated six times and the last five of the six measurements were averaged (to discount warm up period) to arrive at the final read and write speeds.
The programs were executed on a laptop powered by Intel i7 processor and running Pop OS 18.04. The programs created files in
- a ext4 file system on an internal SSD connected via SATA 3 bus. The average read and write rates of this drive was 440 MB/s and 395 MB/s, respectively.
- a ext4 file system on an internal non-SSD connected via SATA 3 bus. The average read and write rates of this drive was 97 MB/s and 55 MB/s, respectively.
- a exFAT file system on an external USB3 non-SSD connected via the USB3 port. The average read and write rates of this drive was 84 MB/s and 43 MB/s, respectively.
The average read and write rates were measured using Disks utility.
From the measurements given below, Scala gives the best read and write throughput across all internal disks followed by Go. In the context of external USB3 disk, Scala gives the best write throughput (followed by Scala) while Go gives the best read throughput (followed by Scala).
Interestingly, across all devices, read and write speeds in C++ is at least 2x lower than the speed in Scala and Go. As observed in the earlier experiment, this could be due to the implementation of libstd++:
fstream uses quite a bit of virtual functions without inlining (StackOverFlow) and a small buffer (StackOverFlow).
As for Python and Ruby, they offered the lowest read and write speed.
Considering the average read and write speeds determined using Disks tool, the speeds offered by all languages was less than this average for internal SSD. In case of non-SSDs, both Scala and Go offered speeds that exceeded this average. While this is impressive, I wonder how is this possible.
Comparing to Performance on Raspberry PI
While Go offered the best read and write speeds on a Raspberry Pi (see table below), Scala offered the best read and write speeds on a laptop.
While the I/O speeds offered by Scala was ~20% better than the I/O speeds offered by Go on the laptop, this improvement was at least ~2x (~200%) on the Raspberry Pi. Rather substantial difference.
Interestingly, the ratio of I/O speeds offered by Go and C++ on the laptop and the Raspberry Pi were comparable, i.e., ~2x for reading and ~4x for writing.
As in case of Raspberry Pi experiment, the I/O speeds reported by dd tool were comparable to that reported by Disks tool. Something to explore.
[See Updates below]
In general, the findings this time are no different from the earlier findings in the experiment involving Raspberry Pi :
- Scala and Go provide great out-of-the-box buffered I/O speeds on a laptop.
- Default buffered I/O speeds may vary greatly from language to language.
The code and collected data are available on GitHub.
- the read performance improved by 16–47% — 140 MB/s on SSD, 138 MB/s on SATA, and 140 MB/s on USB3, and
- the write performance improved by at least 100%— 128 MB/s on SSD, 129 MB/s on SATA, and 110 MB/s on USB3.
Similar improvements were observed on the Raspberry Pi. Even with these improvements, C++ still lags behind Scala and Go.