Memcache Client API performance (version 2)
Written by Brian Moon, dealnews.com developer, on June 29, 2006
After writing up my findings in my last memcached test, I posted a link to it on the memcached mailing list. Some users had some useful questions and suggestions. So, I basically started from scratch and ran the tests again.
The Setup
I started by installing a fresh build (from Gentoo's Portage) of Apache 1.3.34, PHP 5.1.4, mod_perl 1.29 and mod_python 2.7.11. The previous tests had been run using Apache 2 and versions of the modules built for it. At the time I did those tests, we were moving to Apache 2. However, some problems we encountered led us back to Apache 1.3. I thought it best to test using what we will be using in production. I also installed fresh copies of all the memcached clients, including the rewritten Perl client mentioned on the list.
I then configured a virtual host for running the perl and python scripts. If someone more familiar with mod_perl and mod_python and sees some huge flaw in my config that would affect these tests, please let me know and I will correct it.
Perl
# without these, I got errors in my Apache error log
# even though the pages returned data
PerlModule ("/var/www/localhost/htdocs/perl/Hello.pm")
PerlModule ("/var/www/localhost/htdocs/perl/Large.pm")
PerlModule ("/var/www/localhost/htdocs/perl/Small.pm")
PerlModule ("/var/www/localhost/htdocs/perl/SetsGets.pm")
PerlModule ("/var/www/localhost/htdocs/perl/Gets.pm")
PerlModule ("/var/www/localhost/htdocs/perl/LargeMemcache.pm")
PerlModule ("/var/www/localhost/htdocs/perl/SmallMemcache.pm")
PerlModule ("/var/www/localhost/htdocs/perl/LargeMemcacheNoOutput.pm")
PerlModule ("/var/www/localhost/htdocs/perl/SmallMemcacheNoOutput.pm")
Listen 8080
NameVirtualHost *:8080
<VirtualHost *:8080>
DocumentRoot "/var/www/localhost/htdocs"
<Location /perl/hello>
SetHandler perl-script
PerlHandler Hello
</Location>
<Location /perl/large>
SetHandler perl-script
PerlHandler Large
</Location>
<Location /perl/small>
SetHandler perl-script
PerlHandler Small
</Location>
<Location /perl/sets-gets>
SetHandler perl-script
PerlHandler SetsGets
</Location>
<Location /perl/gets>
SetHandler perl-script
PerlHandler Gets
</Location>
<Location /perl/large-memcache>
SetHandler perl-script
PerlHandler LargeMemcache
</Location>
<Location /perl/small-memcache>
SetHandler perl-script
PerlHandler SmallMemcache
</Location>
<Location /perl/large-memcache-no-output>
SetHandler perl-script
PerlHandler LargeMemcacheNoOutput
</Location>
<Location /perl/small-memcache-no-output>
SetHandler perl-script
PerlHandler SmallMemcacheNoOutput
</Location>
</VirtualHost>Python
Listen 8081 NameVirtualHost *:8081 <VirtualHost *:8081> DocumentRoot "/var/www/localhost/htdocs" PythonPath "sys.path+['/var/www/localhost/htdocs/python']" <Location /python/hello> SetHandler python-program PythonHandler hello </Location> <Location /python/large> SetHandler python-program PythonHandler large </Location> <Location /python/small> SetHandler python-program PythonHandler small </Location> <Location /python/sets-gets> SetHandler python-program PythonHandler sets-gets </Location> <Location /python/gets> SetHandler python-program PythonHandler gets </Location> <Location /python/large-memcache> SetHandler python-program PythonHandler large-memcache </Location> <Location /python/small-memcache> SetHandler python-program PythonHandler small-memcache </Location> <Location /python/large-memcache-no-output> SetHandler python-program PythonHandler large-memcache-no-output </Location> <Location /python/small-memcache-no-output> SetHandler python-program PythonHandler small-memcache-no-output </Location> </VirtualHost>
I do want to note that when I ran each test, I only loaded the needed module into Apache.
The Tests
In the last tests, there were some questsions unanswered. For example, was the Perl and Python performance on the large memcached request slower due to its memcached client or some other factor. So, this time I ran nine tests to help answer some of those questions.
Hello World
The old reliable Hello World test was run so we can again establish a base line for how each language peforms within Apache. Again a static file was used as a base line comparison
Small File
In this test, a small file (10k, the HTML from http://dealnews.com/developers/) is read from disk and sent back to the browser.
Large File
In this test, a large file (266k, the HTML from http://dealnews.com/) is read from disk and sent back to the browser.
100 Sets/Gets
In this test, 100 memcached sets are performed, each time setting a new key with a new, small value. Then those 100 keys are fetched. No output is sent to the browser.
100 Gets
In this test, only the 100 gets of the same keys from above are performed.
Large Memcache
In this test, the large file from above is retrieved from memcached and sent back to the browser. The first request would fill the cache. Given that I ran a test on the scripts before starting the benchmark, no sets should have been performed.
Large Memcache - No Output
This is the same test as above, but the contents of the cache are not sent back to the browser.
Small Memcache
In this test, the small file from above is retrieved from memcached and sent back to the browser. The first request would fill the cache. Given that I ran a test on the scripts before starting the benchmark, no sets should have been performed.
Small Memcache - No Output
This is the same test as above, but the contents of the cache are not sent back to the browser.
The Results
| (numbers are requests per second) | Perl | Perl (new) | Python | PHP | Static File |
|---|---|---|---|---|---|
| Hello World | 478.67 | — | 399.11 | 461.66 | 786.90 |
| Small File | 239.33 | — | 259.08 | 259.34 | 602.77 |
| Large File | 36.41 | — | 37.98 | 36.23 | 42.84 |
| 100 Sets/Gets | 5.14 | 5.18 | 7.75 | 42.07 | — |
| 100 Gets | 7.13 | 7.38 | 13.38 | 63.47 | — |
| Large Memcache | 30.64 | 29.78 | 32.96 | 43.05 | — |
| — No Output | 47.01 | 45.88 | 51.23 | 103.24 | — |
| Small Memcache | 197.93 | 200.11 | 201.48 | 262.19 | — |
| — No Output | 214.25 | 226.53 | 263.56 | 292.83 | — |
So, this looks a lot like the last tests. There is a little change here and there. One surprise is that the new Perl client did not perform that well for me. I could have done something wrong, but I don't know what. The new perl client seems to really not like large chunks of data from memcached. I suppose for many users, this is a non-issue. 266k is a lot to stick in one cache. Reducing the size of the HTML on our front page is on my list of things to do.
There was a question on the list about compression. The PHP client uses compression by default. The Perl client used compression (see source). I could find no mention of compression in the Python client code.
Here is the static files, Perl, Python and PHP code I used:
Addendum
I was asked on the list to remove Apache from the equation. Frankly, Apache is part of my application so, its not valid for my needs to do this. However, I ran the following tests. Their output follows them.
Perl (current CPAN client)
# cat gets.pl
use strict;
use warnings;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
# timing example from http://perl.active-venture.com/lib/Time/HiRes.html
my $start = [gettimeofday];
use Cache::Memcached;
my $memd = new Cache::Memcached {
'servers' => [ "127.0.0.1:11211" ],
'debug' => 0,
'compress_threshold' => 10_000,
};
my $content = "";
my $x = 0;
for (0..99) {
$content = $memd->get("item$_");
}
my $end = [gettimeofday];
my $elapsed = tv_interval($start, $end);
print "$elapsed\n";
# perl gets.pl
0.110083
# perl gets.pl
0.107015
# perl gets.pl
0.107577
# perl gets.pl
0.106776
# perl gets.pl
0.105072
# perl gets.pl
0.103913
# perl gets.pl
0.108042Perl (new client)
# cat gets.pl
use strict;
use warnings;
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval );
# timing example from http://perl.active-venture.com/lib/Time/HiRes.html
my $start = [gettimeofday];
use Cache::Memcached;
my $memd = new Cache::Memcached {
'servers' => [ "127.0.0.1:11211" ],
'debug' => 0,
'compress_threshold' => 10_000,
};
my $content = "";
my $x = 0;
for (0..99) {
$content = $memd->get("item$_");
}
my $end = [gettimeofday];
my $elapsed = tv_interval($start, $end);
print "$elapsed\n";
# perl gets.pl
0.065681
# perl gets.pl
0.065462
# perl gets.pl
0.067464
# perl gets.pl
0.067719
# perl gets.pl
0.066819
# perl gets.pl
0.066642
# perl gets.pl
0.065069Python
# cat getscl.py import time time.time() start = time.time() import memcache mc = memcache.Client(['127.0.0.1:11211'], debug=0) for i in range(100): key = "thing" + str(i) rc = mc.get(key) end = time.time() elapsed = end - start print str(elapsed) # python getscl.py 0.084823846817 # python getscl.py 0.0830268859863 # python getscl.py 0.085088968277 # python getscl.py 0.0827698707581 # python getscl.py 0.0841782093048 # python getscl.py 0.0829510688782 # python getscl.py 0.0845789909363
PHP
# cat gets.php
<?php
$start = microtime(true);
$MEMCACHE = new Memcache();
$MEMCACHE->addServer ( "127.0.0.1" );
$MEMCACHE->setCompressThreshold(10240);
for($x=0;$x<100;$x++){
$var = $MEMCACHE->get("thing$x");
}
$end = microtime(true);
$elapsed = $end - $start;
echo "$elapsed\n";
?>
# /usr/bin/php gets.php
0.0155520439148
# /usr/bin/php gets.php
0.0157949924469
# /usr/bin/php gets.php
0.0156910419464
# /usr/bin/php gets.php
0.0163218975067
# /usr/bin/php gets.php
0.0159170627594
# /usr/bin/php gets.php
0.0155100822449
# /usr/bin/php gets.php
0.015743970871