<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Matthew Turland &#187; Uncategorized</title>
	<atom:link href="http://matthewturland.com/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>http://matthewturland.com</link>
	<description></description>
	<lastBuildDate>Sun, 18 Jul 2010 14:29:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Benchmarking PHP HTTP Clients</title>
		<link>http://matthewturland.com/2008/11/23/benchmarking-php-http-clients/</link>
		<comments>http://matthewturland.com/2008/11/23/benchmarking-php-http-clients/#comments</comments>
		<pubDate>Sun, 23 Nov 2008 05:02:55 +0000</pubDate>
		<dc:creator>Matthew Turland</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[If you read my blog semi-regularly, you might remember when I mentioned that my book would be released later on this year. Unfortunately, that project had to be put on hold in favor of a few other projects. Now that those are winding down, however, I&#8217;m able to return to working on the book. I&#8217;m [...]]]></description>
			<content:encoded><![CDATA[<p>If you read my blog semi-regularly, you might remember when I <a title="i should be coding :: aloofness" href="http://matthewturland.com/2008/08/29/aloofness">mentioned</a> that my book would be released later on this year. Unfortunately, that project had to be put on hold in favor of a few other projects. Now that those are winding down, however, I&#8217;m able to return to working on the book. I&#8217;m hoping the manuscript will be completed by the end of March 2009.</p>
<p>One of the interesting bits of research that I&#8217;ve done is benchmarking various mainstream PHP HTTP clients. Of course, we all know that there are <a title="Lies, damned lies, and statistics - Wikipedia, the free encyclopedia" href="http://en.wikipedia.org/wiki/Lies,_damned_lies,_and_statistics">lies, damned lies, statistics, and benchmarks</a>, so take these with a grain of salt. They were run on my Sony Vaio, which is an Intel C2D T5550 @ 1.83GHz with 2 GB of RAM running Ubuntu Ibex and its standard php5 package. According to <a title="Speedtest.net - The Global Broadband Speed Test" href="http://speedtest.net">Speedtest.net</a>, my <a title="Welcome to Cox.net" href="http://cox.net">Cox Cable</a> connection has a 12,375 kb/s download rate and a 5,998 kb/s upload rate.</p>
<pre>&lt;?php
// pecl_http (1.6.1)
$response = http_get(
    'http://paste2.org/new-paste',
    array(
        'connecttimeout' =&gt;  15
    )
);
echo 'http ', strlen($response), PHP_EOL;

// streams http wrapper
$response = file_get_contents('http://paste2.org/new-paste');
echo 'streams ', strlen($response), PHP_EOL;

// curl (php5-curl Ubuntu package:
// libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.8)
$ch = curl_init('http://paste2.org/new-paste');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo 'curl ', strlen($response), PHP_EOL;

// PEAR::HTTP_Client (PEAR 1.7.2, HTTP_Client 1.2.1)
$error = error_reporting(E_ALL);
require_once 'HTTP/Client.php';
$client = new HTTP_Client();
$client-&gt;get('http://paste2.org/new-paste');
$response = $client-&gt;currentResponse();
$response = $response['body'];
echo 'pear ', strlen($response), PHP_EOL;
error_reporting($error);

// Zend_Http_Client (SVN r12780)
require_once 'Zend/Http/Client.php';
$client = new Zend_Http_Client('http://paste2.org/new-paste');
$response = $client-&gt;request()-&gt;getBody();
echo 'zend ', strlen($response), PHP_EOL;</pre>
<p>The Ubuntu packages for <a title="Xdebug - Debugger and Profiler Tool for PHP" href="http://xdebug.org">Xdebug</a> (php5-xdebug) and <a title="KCachegrind" href="http://kcachegrind.sourceforge.net/html/Home.html">KCachegrind</a> produced the following results for this script.</p>
<table style="margin-bottom: 15px;">
<tbody>
<tr>
<td>pecl_http</td>
<td>20.08%</td>
</tr>
<tr>
<td>streams</td>
<td>19.81%</td>
</tr>
<tr>
<td>curl</td>
<td>19.83%</td>
</tr>
<tr>
<td>pear</td>
<td>19.73%</td>
</tr>
<tr>
<td>zend</td>
<td>19.88%</td>
</tr>
</tbody>
</table>
<p>So the performance of these components is roughly equivalent. One thing that&#8217;s interesting is that the call tree for PEAR is actually the longest (four calls underneath the one shown in the source here) and at the bottom is a call to <a title="PHP: gethostbyname - Manual" href="http://php.net/gethostbyname">gethostbyname</a>, which takes 18.97% of the script&#8217;s runtime, putting the amount used by the calls above it at 0.76%. This suggests that the majority of the time taken by the other components is likely due to the same reason.</p>
<p>Let&#8217;s try a slightly more complex request.</p>
<pre>&lt;?php
$post = array(
    'lang' =&gt; 'php',
    'description' =&gt; '',
    'code' =&gt; 'test',
    'parent' =&gt; '0'
);

// pecl_http
$response = http_post_fields(
    'http://paste2.org/new-paste',
    $post,
    null,
    array('connecttimeout' =&gt; 15)
);
echo 'http ', strlen($response), PHP_EOL;

// streams http wrapper
$context = stream_context_create(array(
    'http' =&gt; array(
        'method' =&gt; 'POST',
        'header' =&gt; 'Content-Type: application/x-www-form-urlencoded',
        'content' =&gt; http_build_query($post)
    )
));
$response = file_get_contents('http://paste2.org/new-paste', false, $context);
echo 'streams ', strlen($response), PHP_EOL;

// curl
$params = array(
    CURLOPT_URL =&gt; 'http://www.paste2.org/new-paste',
    CURLOPT_POST =&gt; true,
    CURLOPT_HEADER =&gt; true,
    CURLOPT_RETURNTRANSFER =&gt; true,
    CURLOPT_POSTFIELDS =&gt; $post
);
$ch = curl_init();
foreach ($params as $key =&gt; $value) {
    curl_setopt($ch, $key, $value);
}
$response = curl_exec($ch);
curl_close($ch);
echo 'curl ', strlen($response), PHP_EOL;

// PEAR::HTTP_Client
$error = error_reporting(E_ALL);
require_once 'HTTP/Client.php';
$client = new HTTP_Client();
$client-&gt;post('http://paste2.org/new-paste', $post);
$response = $client-&gt;currentResponse();
$response = $response['body'];
echo 'pear ', strlen($response), PHP_EOL;
error_reporting($error);

// Zend_Http_Client
require_once 'Zend/Http/Client.php';
$client = new Zend_Http_Client('http://paste2.org/new-paste');
$client-&gt;setParameterPost($post);
$response = $client-&gt;request('POST')-&gt;getBody();
echo 'zend ', strlen($response), PHP_EOL;</pre>
<p>And here are the Xdebug + KCachegrind results for the execution of this script.</p>
<table style="margin-bottom: 15px;">
<tbody>
<tr>
<td>pecl_http</td>
<td>12.56%</td>
</tr>
<tr>
<td>streams</td>
<td>25.02%</td>
</tr>
<tr>
<td>curl</td>
<td>12.69%</td>
</tr>
<tr>
<td>pear</td>
<td>24.81%</td>
</tr>
<tr>
<td>zend</td>
<td>24.81%</td>
</tr>
</tbody>
</table>
<p>The gethostbyname call in the PEAR call stack again takes up the majority of its runtime, 21.05% in this case. That puts the remainder of the time for PEAR at 3.76%. pecl_http and curl are roughly equivalent in performance to each other and twice that of the others. Oddly, streams (a C extension like pecl_http and curl) suffers a performance difference similar to the libraries written in PHP.</p>
<p>I have a semi-educated guess as to why this is. PEAR makes two gethostbyname calls to process the request, presumably one for the initial POST and one for a GET that follows because the POST response includes a Location header. Zend appears to make two stream_socket_client calls for the same reason. Streams do not appear to implicitly cache DNS lookups, so the HTTP streams wrapper is most likely in the same situation.</p>
<p>The existence of the <a title="PHP: curl_setopt - Manual" href="http://us3.php.net/curl_setopt">CURLOPT_DNS_USE_GLOBAL_CACHE</a> option and the <a title="PHP: Runtime Configuration - Manual" href="http://us3.php.net/manual/en/http.configuration.php">http.request.datashare.dns</a> configuration setting and the fact that both are enabled by default lead me to believe that the curl and pecl_http extensions do cache DNS lookups and thus don&#8217;t suffer the performance hit of repeating them. Can anyone confirm or deny this?</p>
]]></content:encoded>
			<wfw:commentRss>http://matthewturland.com/2008/11/23/benchmarking-php-http-clients/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
