NextCloudPi‘s target audience is domestic hosters and one of the use cases that attracts users is to self-host their own picture collection.
The main complaint for such users is that gallery is too slow. When you open the gallery previews are generated on the fly which affects responsiveness. Once they are generated, the next time it will load much faster.
The current solution is to generate the previews offline through a cron job using the Preview Generator App so that they are ready to be fetched when we open the Gallery.
Even when using this App browsing our pictures is usually very slow. This happens typically for the following reasons
- Lack of computing power. SBCs are not very powerful.
- Misconfigured preview sizes (and lack of documentation thereof).
Even using the Preview Generator, it can take days or even weeks on a Raspberry Pi to generate the previews for a big collection which throws many people off.
Another complaint is that too many previews are generated so often the previews folder takes even more space that the pictures themselves.
The most obvious low hanging fruit to try to deal with this issue is noticing the fact that even though our systems are multi processor, only one CPU is used to generate the previews so there are plenty unutilized resources available that are not being used.
First, let’s take a quick look at the Nextcloud Previews system.
How previews work
Every time we upload an image two previews are generated
- Small 32×32 preview. This is the icon in the Files list.
- Max preview. This is the big picture that shows in the browser when you click on it.
The Max preview size defaults to 4096×4096, and can be configured with
occ config:system:set preview_max_x --value 2048 occ config:system:set preview_max_y --value 2048
root@293626d9121d:/var/www/nextcloud# ls -R data/appdata_ocn7c0nrnm7u/preview/* data/appdata_ocn7c0nrnm7u/preview/63405: 1365-2048-max.jpg 256-256-crop.jpg
There are other previews that are not generated by default but are used by other parts of Nextcloud. These will be generated on the fly unless they are pregenerated with the App.
- The Gallery App requests (js/thumbnail.js)
- 400×200 -> 384×256 / 256×384 (**) for regular thumbnails.
- 200×200 -> 256×171 / 171×256 for folder thumbnails, which shows the first four square thumbnails of the directory.
- The Files App in grid view mode requests (js/filelist.js)
- 250×250 -> 256×256
(*) it requests 400×200 but the backend rounds it up to powers of two -> 384×256. These are examples only.
(**) depending on photo orientation
How the Preview Generator works
We can see that we need a properly configured Preview Generator or we might be generating previews that are useless since they are not going to be used.
By default the App generates all possible sizes from 32×32 to Max which is both a waste of CPU and space since as we just saw many of those won’t be used.
root@293626d9121d:/var/www/nextcloud# ls -R data/appdata_ocn7c0nrnm7u/preview/* data/appdata_ocn7c0nrnm7u/preview/63405: 1024-1024-crop.jpg 171-256.jpg 256-384.jpg 2731-4096-max.jpg 64-64-crop.jpg 683-1024.jpg 1024-1536.jpg 256-256-crop.jpg 2731-2731-crop.jpg43-64.jpg 64-96.jpg
If we don’t want this behavior, the App allows us to configure what sizes we want to generate, but it is not documented anywhere what sizes are really needed unless you dig in the code. To configure this setting
occ config:app:set previewgenerator squareSizes --value="32 256" occ config:app:set previewgenerator widthSizes --value="256 384" occ config:app:set previewgenerator heightSizes --value="256"
So we end up with either people complaining that the previews take more GiB than the actual photos or people naively trying different sizes assuming that they will be used when they will most likely not be.
For the reasons stated above, I recommend the following settings for a SBC
occ config:app:set previewgenerator squareSizes --value="32 256" occ config:app:set previewgenerator widthSizes --value="256 384" occ config:app:set previewgenerator heightSizes --value="256" occ config:system:set preview_max_x --value 2048 occ config:system:set preview_max_y --value 2048 occ config:system:set jpeg_quality --value 60 occ config:app:set preview jpeg_quality --value="60"
In my opinion these should be the default settings for the Preview Generator App.
root@293626d9121d:/var/www/nextcloud# ls -R data/appdata_ocn7c0nrnm7u/preview/* data/appdata_ocn7c0nrnm7u/preview/63405: 1365-2048-max.jpg 171-256.jpg 256-256-crop.jpg 256-384.jpg 64-64-crop.jpg
Just configuring the Preview Generator properly results in a 4x speed up and saves lots of wasted space (see results below).
In order to try to find a way of using our CPUs more efficiently and to improve performance on low end devices, I forked Nextcloud and the Previews Generator App to create a Proof of Concept implementation that I could run some tests on.
We don’t need very good quality using computationally expensive filters for the small thumbnails, maybe only for the Max preview, so the proposal plays with scaling without interpolation and parallel generation to try to achieve better results.
Using these modifications I was able to achieve a 5x speed up in a 4 core Raspberry Pi and a 10x speed up in my 16 core PC while retaining good image quality.
If we compare with an unconfigured Preview Generator the gain is 20x for the Raspberry Pi and 50x for the PC (see Github link).
You can check more details about these benchmark results on Github.