Faster Linux on low memory using zram (ubuntu 22.04)


I have a beefy workstation at home for “serious work” – 24 logical CPU cores, 64 GB of system memory, plenty to do everything development-wise that I want to do.

However, I also have an aging Acer Aspire V3-772 that I upgraded with an SSD and exchanged the 4 GB of memory with 8GB. To this date, I like the big 17-inch display it has, the full keyboard with numpad and that it is relatively modular with disks bays and user upgradable memory.

However, now after 8 years it is beginning to show its age: the system memory is limiting. For general tasks, like browsing the web or office, 8 GB is plenty, but for anything software development this is not enough.

Software development and memory

A typical set of software that I run for software development consists of:

  • Eclipse or IntelliJ IDE or VS Code
  • Some sort of light VM or docker container (low compute, but maybe 1-2 GB of memory)
  • A webbrowser for checking help files and references

I also need to be able to switch between any of these applications in an instant. Once you have written some code and you typically want to test it in your VM, and it should run quickly and at an instant. And after checking the reference manual in my webbrowser I want to be ableback to coding quickly as well.

Each of these software packages clocks in at about 1-3 GB of constant memory use. In addition, one needs to often use auxiliary applications, like I do for writing this document, for which I use a program called ghostwriter, and all of a sudden, it becomes very easy to exhaust a mere 8 GB of memory rather quickly.

zramswap vs swap

What happens when you exhaust your memory? Swapping. Swapping on an SSD is – okay – but still too slow for instant application switching. So what to do, if I do not want to upgrade my system memory yet again?

I remembered that Android comes with a feature which allows in-memory compression of unused memory pages. In essence a sort-of “swapfile in memory”. However, I also remembered that this was a way for Android to circumvent an issue with low-end phones using very slow storage backends and with very limited amounts of writes. Therefore, memory compression on Android serves the purpose of preventing the need to swap to the storage backend in the first place. With a fairly modern SATA-SSD, these are likely not issues for my aging laptop though.

So, would memory-compressed pages actually bring anything to the table for my aging computer? I did not know. I know that Windows includes this feature now, so Microsoft thinks that it can be beneficial. On my freshly installed ubuntu 22.04, the kernel module and a matching zram-tools apt-package were there. So time to try it out on my Linux machine!

Installing zramswap on ubuntu 22.04

Installing and configuring zram on ubuntu 22.04 is a breeze compared to earlier versions of Linux. Install the zram-tools package:

sudo apt-get install zram-tools

This installs a service zramswap which configures zram on the machine, gives you a handy utility called zramctl, and creates a /etc/default/zramswap configuration file that allows simple configuration of the zram settings. By default, a single zram device /dev/zram0 is created as well.

Now comes the the an interesting decision for the configuration: zram statically reserves a section of your system memory for the purpose 1. So how much should I reserve? Compressed memory behaves like swap, meaning that it is not available for standard memory operation and comes with a relatively huge overhead when it needs to be accessed. On the other hand, compression can allow for a lot more data to be stored in memory in the first place, and going to the SSD for swapping is expected to be even slower.

After some manual tinkering, I went with a 10% of my system memory. In addition, I configured the compression algorithm to use zstd instead of the default, which is lz4. So, my configuration file /etc/default/zramswap looks like this:

ALGO=zstd
PERCENT=10

After adapting the configuration file, you must reload the zram-service to apply the changes:

systemctl restart zramswap

And then you are done – your fancy new zram-device now acts as extra swap space. You can check if your zram is configured correctly by running swapon:

$ swapon
NAME       TYPE      SIZE   USED PRIO
/swapfile  file        2G 487,3M   -2
/dev/zram0 partition 783M   4,4M  100

This example shows that I am using a /swapfile of 2GB size (on my SSD) and in addition a /dev/zram0 swap of 1.5GB size. In addition, to get more details about the zram-device itself, you can use the zramctl command (no arguments):

$ zramctl 
NAME       ALGORITHM DISKSIZE  DATA COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 zstd          783M  4,4M 95,6K  644K       4 [SWAP]

Among other things, very interesting here will be the “DATA” and “COMPR” columns which indicate the compression ratio of the compressed pages.

Testing

To verify that the zram swap actually does something, speed-wise, I conducted a few tests that mimic my usual workload on my laptop.

There were two types of tests intended to measure system responsiveness under different CPU and memory loads. I measured swap-use during the tests using the Linux programs free -m and for zram use zramctl --output-all.

I devised a set of two different memory-speed tests “normal memory” and “low memory” tests and ran these with three different zram-configurations (other than the 10% configured above). I rebooted my machine after changing the zram-configurations but I did not reboot between repeated test runs for a single zram-configuration.

Normal memory test

For the “normal memory” test, I did the following sequence of preparations before taking a time measurement:

  • I ran Android Studio IDE and one instance the of Android Device Emulator and loaded my application Baby Buddy for Android.
    • Before each test, I cleaned and rebuilt the app from source code and ran it in the emulator.
  • I opened Firefox with 6 tabs of Android references and 6 tabs of Kotlin references.
    • Before each test, I reloaded all the 12 tabs to make sure that Firefox would load the tabs into active memory.
  • I ran LibreOffice 7.3 Writer and opened a small document with an image and a table and some text.
    • Before each test, I added an exclamation mark to the document and saved it to make sure LibreOffice was swapped into active memory.

These steps were intended to “clean active memory” and swap out pages that were not in use for some time, but do so in a manner that mimics actual computer use.

After these initial steps were executed I used my mobile phone to measure how long it took my laptop to do complete the following steps:

  1. Start a new Firefox window using a shortcut
  2. Navigating to the YouTube homepage (http://www.youtube.com) using a bookmark
  3. Clicking one the first suggested video
  4. Wait until the video starts playback with video and sound

I repeated this measurement three times for each test.

Low memory test

The “low memory” test consisted of the same steps as the “Normal memory test”, plus one extra step following the LibreOffice 7.3 step. After saving the LibreOffice document, I opened a new Firefox window and loaded the “Crypt Demo” from http://crypt-webgl.unigine.com/game.html.

This extra step was to taken to create a situation where the CPU was more stressed than in the other test, plus adding a bit of extra forced-active memory requirements to the test suite by running a game engine (windowed mode).

The procedure to measurement laptop responsiveness (timings) were the same as for the “normal memory” test.

zramswap configurations

I tried out three different zramswap configurations for each of the two tests:

  • No zramswap (only swap space on my SSD)
  • zramswap set to 5% system memory
  • zramswap set to 20% system memory

As mentioned before, I rebooted the laptop after changing the zramswap configuration. I did not reboot the laptop between the three repeated measurements I took.

Results

First, a short overview of the memory use of my Laptop without zramswap configured and with only a few programs running. Firefox was running, but Android Studio and LibreOffice were not running yet. This is more or less the situation when doing light work on the computer:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        1275        4324         399        2229        5883
Swap:           2047           0        2047

As can be seen, most memory is “available” under these circumstances. Swap is not used at all.

Swap only – Normal memory

After running through the initialization steps for the test, the available memory looked as follows:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        4455         130         895        3244        2175
Swap:           2047         946        1101

During the three repetitions I measured 14s, 10s, and, 13s of Firefox-to-video-playback delays. Median: 13s.

Swap only – Low memory

Memory situation after initial steps:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        5158         205        1101        2466        1266
Swap:           2047        1127         920

Time measurements for Firefox-to-video-playback delays: 14s, 15s, and 12s. Median: 14s.

ZRAM (5%) – Normal memory

Memory situation after initial steps:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        4865         135         872        2828        1810
Swap:           2439        1623         816

# zramctl --output-all
NAME       DISKSIZE  DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0   391,5M  103M 24,8M zstd            4       3996 26,3M        0B    27,2M       0B [SWAP]

Time measurements for Firefox-to-video-playback delays: 10s, 11s, and 10s. Median: 10s.

ZRAM (5%) – Low memory

Memory situation after initial steps:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        5106         244        1090        2478        1330
Swap:           2439        1763         675

$ zramctl --output-all
NAME       DISKSIZE   DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0   391,5M 317,1M 68,4M zstd            4      12223 71,6M        0B    71,6M       0B [SWAP]

Time measurements for Firefox-to-video-playback delays: 13s, 13s, and 13s. Median: 13s.

ZRAM (20%) – Normal memory

Memory situation after initial steps:

$ free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        5184         128         818        2516        1544
Swap:           3613        1724        1889

$ zramctl --output-all
NAME       DISKSIZE  DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0     1,5G  183M 40,4M zstd            4       5754 42,3M        0B    42,3M       0B [SWAP]

Time measurements for Firefox-to-video-playback delays: 9s, 12s, and 8s. Median: 9s.

ZRAM (20%) – Low memory

Memory situation after initial steps:

# free -m
               total        used        free      shared  buff/cache   available
Mem:            7829        5142         152         958        2534        1447
Swap:           3613        2124        1489

# zramctl --output-all
NAME       DISKSIZE   DATA COMPR ALGORITHM STREAMS ZERO-PAGES  TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0     1,5G 635,6M  145M zstd            4      16121 151,8M        0B   151,9M     1,1K [SWAP]

Time measurements for Firefox-to-video-playback delays: 12s, 16s, and 9s. Median: 12s.

Additional observations

In order to verify that the configured zram was used during the repeated tests, I also took a snapshot of zramctl after running the “normal memory” tests. These can be compared to the measurements taken listed above as “after initial steps” measurements.

For the zram=5% case, the zramctl output was:

$ zramctl --output-all
NAME       DISKSIZE   DATA COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0   391,5M 294,5M 66,1M zstd            4       7710 69,1M        0B    69,1M       0B [SWAP]

For the zram=20% case, the zramctl output was:

$ zramctl --output-all
NAME       DISKSIZE   DATA  COMPR ALGORITHM STREAMS ZERO-PAGES TOTAL MEM-LIMIT MEM-USED MIGRATED MOUNTPOINT
/dev/zram0     1,5G 528,8M 121,6M zstd            4      15354  127M        0B     127M       0B [SWAP]

As can be seen, the DATA-field for both tests increased after running the tests. For the zram=5% case from 103M to 294.5M, and for the zram=20% case from 183M to 528.8M.

Data aggregation/plots

The plot shows the median of the times-to-playback (TTP) for all the cases recorded. All normal-memory test cases show faster TTP than their low-memory counterparts. Within each test category, it can be seen that with increasing zramswap-allocation from 0% to 5% to 20%, the TTP decreases.

Interpretation

My measurements suggest that zram increases the responsiveness of a system in low-memory situations. Working only with swap space on an SSD makes the slower for the laptop I tested compared to swap space with zram.

However, the testing methodology is very limited. I only did three repetitions for each measurement and the manual execution and could have been severely improved by automating them which would lead to more repeatable tests. If someone feels like replicating this test, I suggest using a virtual machine and scripting application interactions in some form to make these tests repeatable.

For the zram-conclusions: I am personally a bit surprised how clear-cut the results were. Subjectively, I felt an improvement on the system myself, but I also think that the test-case that I used to illustrate the improvement does not capture all aspects of the zram configuration.

First and foremost, a problem is that zram is not available as standard memory when allocated. That means adding a static configuration, like 5% zram, will decrease the memory available to the system by 5% permanently. This reserved zram memory will not be available to Linux as regular memory or file buffers and caches.

In addition, subjectively and contradicting my measurments, the system felt better to me when using the 5% configuration instead of 20% zram configuration. The configuration felt better when launching programs outside the scope of the tests, likely because there was more memory left to buffer important system libraries that did not have to be stored in swap-space.

This a makes my advise on using zram a bit of a mixed bag: It really depends on the workload that the machine faces. If the machine is normally running with not much memory utilization at all, zram probably worsens the experience. However, when tasks repeatedly call for swap-sapce utilization, as illustrated in these tests, zram can significantly speed up the system’s responsiveness.

It is possible that using the hotadd feature of zram could yield a nice balanced solution where zram only is added when low-memory situations are detected. However, those configurations are more complicated to configure and more complicated to test as well. As such, this is out of the scope of this little test.

Footnotes


  1. There also is a hot-add feature in the kernel module, but that does not seem to be “easy to configure”, so I did omit that for my setup. 

Leave a Reply

Your email address will not be published. Required fields are marked *