Posts

Showing posts with the label performance

For or Foreach? PHP vs. Javascript, C++, Java, HipHop (update: HHVM v2)

Lessons learned: Foreach is 4-5 times faster than For Nested Foreach is 4-5 times faster than nested For Foreach with key lookup is 2 times slower than Foreach without C++ is 60 times faster than PHP running For/Foreach on Arrays Javascript is 2-20 times slower than C++/Java running For on Arrays HipHop is currently no alternative to C++ Using a better CPU makes the code 4-5 times faster Here is a sample script, running on a 1.4 GHz machine with PHP 5.4.0: // init arrays $array = array(); for ($i=0; $i<50000; $i++) $array[] = $i*2; $array2 = array(); for ($i=20000; $i<21000; $i++) $array2[] = $i*2; // test1: foreach big-array (foreach small-array) $start = microtime(true); foreach ($array as $val) { foreach ($array2 as $val2) if ($val == $val2) {} } echo (microtime(true)-$start)."\n"; // test1b: foreach big-array (foreach small-array) $start = microtime(true); foreach ($array as $val) { foreach ($array2 as $val2) if ($val === $val2) {} } echo (microtime(true)-$st...

How to implement really small and fast ORM with PHP (Part 7: IDE)

Image
Queries are gaining more and more complexity, data is getting bigger and bigger. Most optimizations in database technology are done in the database server. This is an approach to optimize queries on the client side. With this ORM, queries ... don't select more data than needed contain less joins when data is expected to be consistent can be written manually in pure SQL are not written in a new query language We need a good API, so ... it should be easy to learn method names must be short and intuitive the goal is to map datasets and relations to objects the API should offer method chaining special features like auto-increments should be included the code should be small, no getters and setters the database schema is created before writing PHP code relationships should be defined in the database, not in the code we get low latencies combined with low memory usage To make things easier, we make some restrictions: only UTF-8 only MySQL/MariaDB (mysqli) only PHP 5.4.0+ only buffer...

Performance of integer casting

Lessons learned: $var+0 is as fast as (int)$var intval($var) is 2.5 times slower than (int)$var is_numeric($var) is 3 times slower than (int)$var is_numeric($var) is 7 percent slower than intval($var) settype($var) is 40 percent slower than is_numeric($var) Here is the code: test("10000a"); test("100000"); function test($i) { $start = microtime(true); for ($i=0; $i<1000000; $i++) $p = (int)$i; echo (microtime(true)-$start)."\n"; // 0.0435, 0.0440 $start = microtime(true); for ($i=0; $i<1000000; $i++) $p = $i*1; echo (microtime(true)-$start)."\n"; // 0.0481, 0.0487 $start = microtime(true); for ($i=0; $i<1000000; $i++) $p = $i+0; echo (microtime(true)-$start)."\n"; // 0.0439, 0.0449 $start = microtime(true); for ($i=0; $i<1000000; $i++) $p = intval($i); echo (microtime(true)-$start)."\n"; // 0.1173, 0.1196 $start = microtime(true); for ($i=0; $i<1000000; $i++) $p = doubleval($i); ...

Building PHP extensions with C++, the easy way (update: MySQL, threads)

Image
Here is an easy way to build a PHP extension with C++ and default PHP packages on a Ubuntu system. I use SWIG to wrap C++ code to the Zend API. When using loops and recursion intensively, porting a few functions to C++ can give you some extra power . First, install all required packages (tested with Ubuntu 12.10) : apt-get install php5-cli php5-dev swig g++ Second, write the code: // example.swig %{ #include <iostream> #include <vector> using namespace std; int HelloWorld(char *str) { cout << "Hello World: " << str << endl; // run some slow code vector<int> array; for (int i=0; i < 10000000; i++) array.push_back(i*2); for (int i=0; i < array.size(); i++) array[i]++; return 0; } %} %module example int HelloWorld(char *str); Third, compile the code and load the exension: swig -c++ -php5 example.swig g++ `php-config --includes` -O2 -march=native -mtune=native -std=c++11 -fPIC -c *.cpp g++ -shared *.o -o exa...

Runtime vs. memory usage

Oftentimes, better runtime can result in higher memory usage. Here is an example to create some strings to test bulk inserts on Redis: $cmd = ""; $start = microtime(true); for ($i=0; $i<1000000; $i+=2) $cmd .= "SET entity:".$i.":key value_".($i+1)."\r\n"; echo number_format(microtime(true)-$start, 1)."s\n"; // 0.7s echo number_format(memory_get_usage(true)/1048576, 1)." MB\n"; // 17.5 MB echo number_format(memory_get_peak_usage(true)/1048576, 1)." MB\n"; // 17.5 MB $cmd = ""; $start = microtime(true); $cmd = vsprintf(str_repeat("SET entity:%d:key value_%d\r\n", 500000), range(0,1000000)); echo number_format(microtime(true)-$start, 1)."s\n"; // 0.4s echo number_format(memory_get_usage(true)/1048576, 1)." MB\n"; // 30.8 MB echo number_format(memory_get_peak_usage(true)/1048576, 1)." MB\n"; // 128.5 MB (PHP 5.4.5, 2.5 GHz, win64) We see that the same result can be c...

How to implement a real-time chat server in PHP using Server-Sent Events (update: added C benchmark)

Lessons learned: A web server written in PHP can give more than 10000 req/s on small hardware A web server written in PHP is not slower than being written in Java (without threads) A web server written in PHP is 30 percent slower than being written in C (without threads) Realtime applications can be developed in PHP without problems PHP normally runs inside a web server like Apache or nginx. This keeps all requests separate from each other and does not allow sharing memory or connections. To implement a chat server, the browser has to poll the server regularly for new data. The data is stored in a database and looked up for each request. This is very slow, takes a lot of resources on the server and does not give messages in realtime. Newer browsers support data being pushed from the server to the client. There are two techniques used: WebSockets (full-duplex) and Server-Sent Events (push notifications) Using these techniques, one connection stays open for each client and the server...

PHP Framework Comparison (update: opcode caching)

Image
Reading about PHP frameworks, you'll get a lot about Symfony and Zend Framework. It is true that these frameworks have a lot of features and do great marketing. But what about performance and scalability? There are 2 kinds of frameworks: those written in C as an extension and those written in PHP Using a framework written in C will definitively give the best performance. But fixing (security) bugs and maintaining them requires C knowledge. If the documentation is not complete or incorrect, you might need to read the code to understand the functionality or wait for the maintainer to help you. So if you don't need super high performance, my recommendation is using a framework written in PHP. Here is a test from Laruence (PHP core developer): higher numbers are better, source Here is another test from Zeev Suraski (Zend/PHP core developer): higher numbers are better, source Here is another excellent test from Wang Rui: smaller numbers are better, source (with call graphs, respon...

Members, __set, __get, ArrayAccess and Iterator

Lessons learned: __set() is 5 times slower than setting a member __get() + __set() is 13 times slower than incrementing a member Iterator is 4 times slower than using a member ArrayAccess is 3 times slower than setting an element in a member array ArrayAccess is 6 times slower than incrementing an element in a member array Here is the code: class test1 { public $test = null; private $_test = null; public function __set($id, $val) { $this->_test = $val; } } $start = microtime(true); $c = new test1(); for ($i=0; $i<1000000; $i++) $c->test = $i; echo number_format(microtime(true)-$start, 4)."\n"; // 0.1830s $start = microtime(true); $c = new test1(); for ($i=0; $i<1000000; $i++) $c->test2 = $i; echo number_format(microtime(true)-$start, 4)."\n"; // 0.9570s class test2 { private $_data = ['test4'=>0]; public $test3 = 0; public function __set($id, $val) { $this->_data[$id] = $val; } public function __get($id) { ...

Mass inserts, updates: SQLite vs MySQL (update: delayed inserts)

Lessons learned: SQLite performs inserts 8-14 times faster then InnoDB / MyISAM SQLite performs updates 4-8 times faster then InnoDB and as fast as MyISAM SQLite performs selects 2 times faster than InnoDB and 3 times slower than MyISAM SQLite requires 2.6 times less disk space than InnoDB and 1.7 times more than MyISAM Allowing null values or using synchronous=NORMAL makes inserts 5-10 percent faster in SQLite Using SQLite instead of MySQL can be a great alternative on certain architectures. Especially if you can partition the data into several SQLite databases (e.g. one database per user) and limit parallel transactions on one database. Replication, backup and restore can be done easily over the file system. The results: (MySQL 5.6.5 default config without binlog, SQLite 3.7.7, PHP 5.4.5, 2 x 1.4 GHz, disk 5400rpm) insert [s] sum [s] update [s] size [MB] JSON 1.84 1.30 2.92 2.96 CSV 1.97 2.25 3.7 2.57 SQLite (memory) 2.74 0.12 0.52 0.00 SQLite (memory, not null) 3.00 0...

How to implement a real life benchmark with PHP

To determine the maximum capacity of a web page, Apache ab is often used in the first step. Fetching one URL very often is optimal for caching and gives a best case . To get the worst case for caching, it is necessary to fetch different URLs in a random order. Here is a PHP script to walk randomly on a web page: To get the average case concerning caching and response times, we need to choose the most relevant links. For example, we skip links from headers and footers. This can be done by using a different xpath expression in the code: // fetch all links under <div id="content">...</div> $xpath = '//div[@id="content"]//a'; // fetch all links under <div id="content"> and <div id="menu"> $xpath = '//div[@id="content" or @id="menu"]//a'; To make the benchmark more realistic, you can define a waiting period between two requests: Uncomment "// sleep(1)" at the end of the scrip...

MySQLi prepared statements

Lessons learned: Prepared statements are 13 percent faster than normal statements with escaping Prepared statements are 8 percent faster than normal statements without escaping To get improvements, you need at least 10000 inserts for 1 statement Using insert...set is 0.5-1 percent faster than insert...values Here is the code: $db = new mysqli('127.0.0.1', 'root', '', 'test'); $db->query('create table if not exists prep (i1 int, i2 int, s1 varchar(255)) engine=myisam'); $db->query('truncate table prep'); $start = microtime(true); $stmt = $db->prepare('insert into prep (i1,i2,s1) values (?,?,?)'); $i=0; $j=0; $s=null; $stmt->bind_param('iis', $i, $j, $s); for ($i=0; $i<100000; $i++) { $j = $i*2; $s = 'hello world'.$i; $stmt->execute(); } echo 'prep values '.number_format(microtime(true)-$start, 2)."\n"; assert($db->query('select count(*) from prep')->fetch_ro...

Using V8 Javascript engine as a PHP extension (update: write PHP session)

"We Are Borg PHP. We Will Assimilate You. Resistance Is Futile!" Just got to something described as: This extension embeds the V8 Javascript Engine into PHP. It is called v8js and the documentation is already available on php.net , examples and the sources are here . V8 is known to work well in browsers and webservers like node.js, but does it work inside PHP? YES! Here is the installation on Ubuntu 12.04: sudo apt-get install php5-dev php-pear libv8-dev build-essential sudo pecl install v8js sudo echo extension=v8js.so >>/etc/php5/cli/php.ini sudo echo extension=v8js.so >>/etc/php5/apache2/php.ini php -m | grep v8 Let's run a small test script: <?php $start = microtime(true); $array = array(); for ($i=0; $i<50000; $i++) $array[] = $i*2; $array2 = array(); for ($i=20000; $i<21000; $i++) $array2[] = $i*2; foreach ($array as $val) { foreach ($array2 as $val2) if ($val == $val2) {} } echo (microtime(true)-$start)."\n"; // 8.60s $star...

MySQL or MySQLi or PDO

Lessons learned: MySQLi is 3-4 times slower than MySQL when fetching less then 500 datasets MySQLi is 2-4 times faster than MySQL when fetching more than 500 datasets PDO is 2-5 times slower than MySQL/MySQLi Unbuffered queries are 15-40 percent faster than buffered queries in MySQLi Unbuffered queries are 10-25 percent faster than buffered queries in MySQL for less than 10000 datasets Unbuffered queries are 3-7 percent slower than buffered queries in MySQL for more than 10000 datasets Unbuffered queries are 0-5 percent faster than buffered queries in PDO Non thread safe versions of PHP on win32 are 50 percent faster than thread safe versions Here is the test script: $table = 'test1.test2'; benchmark($table, 100); benchmark($table, 500); benchmark($table, 1000); benchmark($table, 5000); benchmark($table, 10000); benchmark($table, 50000); benchmark($table, 100000); function benchmark($table, $size) { mysql_connect('127.0.0.1', 'root', ''); mysql_qu...

ircmaxell: Is Autoloading A Good Solution?

ircmaxell: Is Autoloading A Good Solution? : at a 75% class usage tradeoff point, it doesn't really make sense not to autoload, especially given all of the other benefits. So in the end, it looks like autoloading is indeed a good solution... From the comments: Simply enabling APC sped up the fixed requires by 82%, and the autoloading by an amazing 91%.

Decorator or Subclassing?

Using anonymous functions in PHP is very nice to implement a decorator, but what about performance? Results: Subclassing is 40 percent faster than using a decorator Subclassing might require a bit more code Here is the code: class App { public static function route($pattern, $callback, $args) { // evaluate $pattern ... call_user_func_array($callback, $args); } } class AppJson extends App { public static function route($pattern, $callback, $args) { // evaluate $pattern ... $str = json_encode(call_user_func_array($callback, $args)); } } $start = microtime(true); for ($i=0; $i<10000; $i++) { AppJson::route('/json/range', 'range', [0,10]); } echo ' '.(microtime(true)-$start); // 0.1058s $json = function ($func) { return function() use (&$func) { $str = json_encode(call_user_func_array($func, func_get_args())); }; }; $start = microtime(true); for ($i=0; $i<10000; $i++) { App::route('/json/range', $json('ran...

How to write a really small and fast controller with PHP (update: benchmark Slim, Silex, Zend Framework, Symfony2)

To handle a lot of traffic, we need a fast controller with very little memory overhead. First, we implement a dynamic controller . The design is based on the micro frameworks Slim and Silex . The first example maps the URL "http://server/index.php/blog/2012/03/02" to a function with the parameters $year, $month and $day: // index.php, handle /blog/2012/03/02 $app = new App(); $app->get('/blog/:year/:month/:day', function($year, $month, $day) { printf('%d-%02d-%02d', $year, $month, $day); }); Our controller is a class named App and uses the get() function to map a GET request. Parameters mapped to the function are marked with a colon. Optional parameters are written inside brackets. Here is an example: // handle /blog, /blog/2012, /blog/2012/03 and /blog/2012/03/02 $app = new App(); $app->get('/blog(/:year(/:month(/:day)))', function($year=2012, $month=1, $day=1) { printf('%d-%02d-%02d', $year, $month, $day); }); Instead of ...