If you follow me (I know, you don’t), you know I render my own maps. For that I use mapnik, a library that can take data and a style and generate the maps I want. I install mapnik using Debian sid’s original packaging. Lately, I’ve been wondering if I can shave some time from its rendering time.
Debian binary packages are compiled from source (duh). Since there is a single binary package per CPU architecture, the compile options must be generic enough to run on all alternative CPUs in that arch. This means that they’re optimized, but in the most generic way, nothing specialized to the CPU you actually have (unless you have the most generic one, which might not exist).
So the question is: can I get some more juice if I recompile at least mapnik, the lib that does most o…
If you follow me (I know, you don’t), you know I render my own maps. For that I use mapnik, a library that can take data and a style and generate the maps I want. I install mapnik using Debian sid’s original packaging. Lately, I’ve been wondering if I can shave some time from its rendering time.
Debian binary packages are compiled from source (duh). Since there is a single binary package per CPU architecture, the compile options must be generic enough to run on all alternative CPUs in that arch. This means that they’re optimized, but in the most generic way, nothing specialized to the CPU you actually have (unless you have the most generic one, which might not exist).
So the question is: can I get some more juice if I recompile at least mapnik, the lib that does most of the CPU crunching? Let’s see.
But first, and this is the question that took me more time to answer: how do I personalize the flags using during compilation? You can donwload Debian source packages with apt source mapnik and the packages needed for compiling it with sudo apt build-dep mapnik. In particular, mapnik uses cmake and it’s developed in C++, so what we’re looking for is to provide options for gcc and/or g++. Luckily, cmake understands CFLAGS and CXXFLAGS, so now it’s a matter of telling Debian’s build system to set those.
Unluckily, it seems like dpkg-buildpackage ignores those. But that tool uses dpkg-buildflags, and that tool has several ways to modify those envvars. It has two sets of operations to modify the flags passed to, in this case, cmake. One set is for the package’s maintainer, and the other can be used by a user like us to customize the compilation.
So, without further addo:
DEB_CFLAGS_APPEND='-O3 -march=native -flto' DEB_CXXFLAGS_APPEND='-O3 -march=native -flto' dpkg-buildpackage --no-sign binary
This will build a new set of .deb files compiled with those flags. But it doesn’t mean you can use them as is.
I mean, you can, but I would rather do this in a more clean manner. I would like a package that it’s seen like an update to the one already provided by Debian, but that also looks like an older version to any update that might come from Debian too (Debian sid is like a rolling release, and I update once a week). This can be achieved with version suffixes1 by adding, for instance, +0.1mdione to the Debian version, and a new entry in the debian/changelog:
mapnik (4.1.3+ds-2+0.1mdione) unstable; urgency=medium
* Recompile with -O3 -march=native -flto
-- Marcos Dione <mdione@grulic.org.ar>  Sun, 02 Nov 2025 11:55:22 +0200
More info at Section 6.3 of the Guide for Debian Maintainers.
And now, to test our optimizations. I decided to run a small batch, once for warm up, once to measure, then upgrade, then run it again. But I run (!!!) into trouble: the second run took more time than the first one! In the end, I found a 16%+ swing between the slowest and the fastest runs (I run it several times more), way more than what any optimization of this kind might give. Still, I had no option than to continue, just in case.
And the winner is: no win. After several runs, I couldn’t even get numbers as good as the fastest from the original version. So, at least in my case, Gentoo’ing it did not make any sense2.
this is not an official name. ↩ 1.
OK, several caveats here: First, I didn’t optimize the whole system. But in this case I’m not sure how much sense it would have made. I could optimize postgres3 (the data is mostly stored there) and the myriad of dependencies for mapnik4, but that would take more time from me that what I evidently can (or, actually, can’t) get out of it. Second, the system was not 100% idle, so maybe that explains the swings. Third, I had a third, but I forgot. ↩
1.
I’m going to end this post here, but I notice that at least for zoom level 11, postgres was using a core 100% for ~2/3rds of the time it takes to render that single tile (3m+!), but afterwards its usage is lower, even when more cores (1 vs up to 8) are rendering at the same time. ↩
1.
168 libraries, according to ldd, and I don’t really know how many are used in the code paths I exercise. ↩