After Tobias Markmann told me that he was running
into resource limitations with a Swiften-based tool for testing server load, I
decided to do a small experiment myself. I created a
small benchmarking tool, and ran it through the memory allocation profiler from
Apple’s Instruments. It turned out that the combination of TLS and ZLib
compression (aka “squishy data”) was causing a much higher memory usage than I would have expected.
The benchmark tool is simple: it creates 100 instances of Swiften’s CoreClient class (which handles connecting and authenticating with the server), and connects them all simultaneously. Running this through Instruments reports about 97 Mb of live data after the stream initialization process (i.e. encryption, compression, authentication). Since 1 Mb per client object is a bit on the high side, it seems worthwile to dig through the call stack a bit, to see who is hogging all the memory:
ZLib compression seems to be allocating quite a lot of memory in 3 places: twice in OpenSSL (for the built-in TLS compression), and once in Swiften itself (for XMPP stream compression). OpenSSL calling deflateInit twice sounds like a bug, although I haven’t verified whether this has been fixed recently (I’m using OpenSSL 0.9.8l, which comes bundled with Mac OS X). Another problem is that the XMPP server my benchmark was connecting to offered ZLib XMPP stream compression, even though the TLS layer was already compressing my traffic (which is a SHOULD NOT in XEP-0138). As a workaround for both problems, I disabled compression in the OpenSSL layer:
After this change, my benchmark ‘only’ has 42 Mb of live data after connecting 100 clients with TLS and ZLib stream compression. The 26 Mb of allocated memory in the deflateInit can probably be reduced by a tweaked deflateInit2 call, but I didn’t invest any effort in this.
Some final numbers for those who are interested: the same benchmark takes 12 Mb when disabling compression altogether and using only TLS when connecting, and uses only 4.5 Mb when disabling both TLS and ZLib compression. From the remaining 4.5 Mb, LibXML uses the biggest chunk of memory (about 1.5 Mb, roughly the same for Expat), and there is still a bit of room for bringing the rest of the memory usage down (by sharing the element parser & serializer factories across client instances, by lowering the network connection data buffers, …).