Сравнение производительности: curl, php curl, php socket, python pycurl

Мне предстоит проект, модуль которого будет большую часть времени работать с другим сервером, отправляя ему GET запросы.
Я провел тесты чтобы определить каким способом получать страницу будет быстрее (в рамках предполагаемых технологий проекта).

Первые 3 теста: каждым из способов выполнялось по 50 запросов подряд к одному сайту.

image

image
Поясню, что curl — то получение страницы консольной утилитой curl в Linux. Все тесты проводились в Linux.
Также был пятый тест — вызов curl из php через exec, но эту глупость я отбросил.

Если усреднить тесты, то получиться такой результат:
image

Места:
  1. php socket
  2. curl
  3. python pycurl
  4. php curl

Если отталкиваться от нижнего значения, то результат меняется:
image

  1. php socket
  2. php curl
  3. python pycurl
  4. curl

Консольный curl без ЯП меня уже не интересует после таких тестов, но кто же все таки быстрее php+curl или python+pycurl? Еще 4 теста, в которых участвовала только эта пара:
image

image
php честно отработал быстрее во всех 4 тестах.

Итоги тестов


Использовать библиотеку curl при простых GET запросах
— это снижение скорости выполнения почти в 2 раза по сравнению с работой через сокеты.
Кроме этого мы заметили, что питон с библиотекой pycurl справляется немного медленнее, чем php с curl.
Возможно тесты в чем то необъективны, если Вы считаете, что это так, обоснуйте в комментариях.

Код для этих тестов


Небольшая программа на C

Запускает программу переданную в параметре и замеряет время ее работы в миллисекундах.
#include <sys/time.h>
#include <stdlib.h>

struct timeval tv1, tv2, dtv;
struct timezone tz;

//time_ functions from http://ccfit.nsu.ru/~kireev/lab1/lab1time.htm
void time_start() 
{ 
	gettimeofday(&tv1, &tz); 
}
long time_stop()
{
	gettimeofday(&tv2, &tz);
	dtv.tv_sec= tv2.tv_sec -tv1.tv_sec;
	dtv.tv_usec=tv2.tv_usec-tv1.tv_usec;

	if(dtv.tv_usec < 0) 
	{ 
		dtv.tv_sec--; 
		dtv.tv_usec += 1000000; 
	}

    return dtv.tv_sec * 1000 + dtv.tv_usec / 1000;
}

int main(int argc, char **argv)
{	
	if(argc > 1)
	{
		time_start();
		system(argv[1]);
		printf("\nTime: %ld\n", time_stop());
	}
	else
		printf("Usage:\n timer1 command\n");	
	
	return 0;
}


PHP

$t = 'http://www.2ip.ru/'; //target

for($i=0; $i < 50; $i++)
{
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $t);
	curl_setopt($ch, CURLOPT_HEADER, 1);
	curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/17.0 Firefox/17.0');
	curl_setopt($ch, CURLOPT_ENCODING, 'utf-8');
	curl_setopt($ch, CURLOPT_TIMEOUT, 200);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	$data = curl_exec($ch);        
	curl_close($ch);
}

Самый быстрый вариант (сокеты)
$t = 'http://www.2ip.ru/'; //target

for($i=0; $i < 50; $i++)
{
	$service_port = 80;
	$address = gethostbyname('www.2ip.ru');
	$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
	if ($socket === false) {
		echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
	} else {
		//echo "OK.\n";
	}

	$result = socket_connect($socket, $address, $service_port);
	if ($result === false) {
		echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
	} else {
		//echo "OK.\n";
	}
	$in = "GET / HTTP/1.1\r\n";
	$in .= "Host: www.example.com\r\n";
	$in .= "Connection: Close\r\n\r\n";
	$out = '';
	$r = '';
	socket_write($socket, $in, strlen($in));
	
	while ($out = socket_read($socket, 2048)) {
		$r .= $out;
	}
	socket_close($socket);
	//echo $r;
}


Python

import pycurl
import cStringIO
 
for i in range(50):
	buf = cStringIO.StringIO()
	c = pycurl.Curl()
	c.setopt(c.URL, 'http://www.2ip.ru/')
	c.setopt(c.WRITEFUNCTION, buf.write)
	c.perform()
	#print buf.getvalue()
	buf.close()


Скрипты bash

Для запуска curl:
#!/bin/bash
testdir="/home/developer/Desktop/tests"
i=0
while [ $i -lt 50 ]
do
  curl -s -o "$testdir/tmp_some_file" "http://www.2ip.ru/"
  let i=i+1
done

Для запуска всех тестов:
#!/bin/bash

testdir="/home/developer/Desktop/tests"
echo "php curl"
"$testdir/timer1" "php $testdir/testCurl.php"

echo "curl to file"
"$testdir/timer1" "bash $testdir/curl2file.sh"

#echo "curl to file (php)"
#"$testdir/timer1" "php $testdir/testCurl2.php"

echo "php socket"
"$testdir/timer1" "php $testdir/testCurl3.php"

echo "python pycurl"
"$testdir/timer1" "python $testdir/1.py"
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 16
  • 0
    Ну если уж внесли в список php + socket, то почему нет python + urllib2?
    • +1
      Провел дополнительный тест,
      python + urllib2 медленнее чем python + pycurl
      поэтому не будем сравнивать с php + socket
    • 0
      Коли уж Вы тестируете по 50 запросов, может имеет смысл использовать асинхронный вариант работы? И почему Вы не рассматривали вариант с использованием stream-функций?
      • 0
        Зачем в php каждый раз создавать и уничтожать инстанс curl?
        $t = 'http://www.2ip.ru/'; //target
        
        $ch = curl_init();
        for($i=0; $i < 50; $i++)
        {
            curl_setopt($ch, CURLOPT_URL, $t);
            curl_setopt($ch, CURLOPT_HEADER, 1);
            curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/17.0 Firefox/17.0');
            curl_setopt($ch, CURLOPT_ENCODING, 'utf-8');
            curl_setopt($ch, CURLOPT_TIMEOUT, 200);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
            $data = curl_exec($ch);        
        }
        curl_close($ch);
        

        Такой вариант отработает быстрее.
        • 0
          подразумевалось что в работающей системе не будет таких циклов, а будут атомарные запросы.
          Будет работать быстрее — да, Вы правы. Но не быстрее чес с сокетами.
          • 0
            А вот это зря. Если запросов будет много и будут идти потоком, то лучше их обрабатывать одним curl-ом, и еще подумать о возможности использования multi curl, чтобы не ждать каждого ответа.
          • 0
            логично, что отработает быстрее, потому что будет через одно соединение грузить и без лишних SYN и FIN на открытие и закрытие, но тут весь подход к тестам неправильный
          • +3
            То есть получается, что установление сетевого соединения с другим сервером + посылка запроса + выполнение удалённого скрипта + получение ответа, всё это выполняется в разы быстрее, чем пожирает обёртка над всем этим?
            • +1
              Запрос идет к домену. Часть времени уходит на dns резолвинг. По умолчанию php curl кэширует dns весьма глобально (даже если курл_инит делается каждый раз), да и остальные могут себя по разному в этом плане вести. Разница может быть здесь зарыта и не проявлять себя на разных доменах, при обращении по ИП или исправляться парой настроек.

              Интересно было бы увидеть аналогичный тест с обращением напрямую по IP или с заведомо включенным/отключенным днс кэшем.
              • 0
                Для реального теста такой подход не пойдёт, — вероятно что многое пойдёт из кэша после первых запросов и сложно в этом случае предсказать откуда данные пришли.
                Если хотите что-то доказать — включайте сниффер и смотрите какие данные и откуда идут на самом деле и какие запросы проходят, а также сколько данных передаётся и какие промежутки времени.

                Такие тесты как здесь без учёта названных параметров недостаточны для уверенного высказывания!
                • 0
                  Сами запросы не идентичны.
                  • 0
                    а чем не устроил стандартный time?
                    "$testdir/timer1" "php $testdir/testCurl.php"
                     -- vs --
                    time "php $testdir/testCurl.php"
                    
                    • 0
                      Не совсем понятно почему сокеты настолько лучше, должны быть лишь совсем чуть-чуть при корректных тестах

                      Разница между различными обертками curl незначительна и наверняка будет сильно меньше сетевых задержек и прочей фиги, которую придется решать через curl_multi
                      • 0
                        вчера проводил тесты между сокетами и курлом в php. Код использовал отсюда. Так вот, сокеты показывают в 3-4 раза худший результат.
                        • 0
                          а какая ОС и версия php?
                          тестировали на том же сайте?
                          сколько тестов провели?
                      • 0
                        Почему не использовать сокеты на python?

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.