PHP & Performance: By: Ilia Alshanetsky
PHP & Performance: By: Ilia Alshanetsky
1
• This cycle happens
PHP Script for every include
file, not just for the
"main" script.
Zend Compile
Zend Execute i re
re qu
method e/
l u d • Compilation can
function i n c
call @ easily consume
more time than
execution.
2
2
Compiler/Opcode Caches
3
Quick Comparison
200
150
100
4
Compiler Optimizations
• For absolute maximum performance, ensure that
all of the software is compiled to take advantage
of the available hardware.
export CFLAGS="-O3 -msse -mmmx -march=pentium3 \
-mcpu=pentium3 -funroll-loops -mfpmath=sse \
-fomit-frame-pointer"
6
Web Server: Syscalls
• Syscall is function executed by the Kernel.
The goal is to minimize the number of these
calls needed to perform a request.
• Do not enable ExtendedStatus.
• For Deny/Allow rules use IPs rather then
domains.
• Do not enable HostnameLookups.
• Keep ServerSignature off
7
7
Web Server: KeepAlive
• In theory KeepAlive is supposed to make
things faster, however if not used carefully
it can cripple the server.
• In Apache set KeepAlive timeout,
KeepAliveTimeout as low as possible.
Suggested value: 10 seconds.
• If the server is only serving dynamic
requests, disable KeepAlive all together.
8
8
Matching Your IO Sizes
9
9
PHP: Output Control
• Efficient
PHP Apache
• Flexible
• In your script, with ob_start()
• Everywhere, with output_buffering = On
• Improves browser’s rendering speed
10
10
Apache: Output Control
Apache OS
11
OS: Output Control
OS (Linux)
OS Client
/proc/sys/net/ipv4/tcp_wmem
4096 16384 maxcontentsize
min default max
/proc/sys/net/ipv4/tcp_mem
(maxcontentsize * maxclients) / pagesize
14
Content Compression
• Most browsers support content compression.
• Compressed pages are on average are 6-8 times smaller.
15
15
Content Reduction
<?php
$o = array("clean" => true, • Use a post-
"drop-proprietary-attributes" => true,
"drop-font-tags" => true, processor like
"drop-empty-paras" => true, Tidy to
"hide-comments" => true,
"join-classes" => true, remove
"join-styles" => true
); formatting,
comments and
$tidy = tidy_parse_file("php.html", $o);
tidy_clean_repair($tidy); CCSify the
echo $tidy; code.
?>
16
Tuning PHP Configuration
➡ register_globals = Off **
➡ magic_quotes_gpc = Off
➡ expose_php = Off
➡ register_argc_argv = Off
➡ always_populate_raw_post_data = Off **
➡ session.use_trans_sid = Off **
➡ session.auto_start = Off **
➡ session.gc_divisor = 1000 or 10000
17
17
Profiling & Benchmarking
• Identify Bottlenecks
• Track Resource Usage
• Generate Call Trees
• Create Progress Tracking Data
18
18
Testing Web Servers
• Apache Bench
‣ ab utility bundled with Apache
• Siege
‣ http://www.joedog.org/JoeDog/Siege
19
19
Web Server Testing
Concurrency Level: 10
Time taken for tests: 0.265 seconds
Complete requests: 100
Failed requests: 0
Broken pipe errors: 0
Total transferred: 5077082 bytes
HTML transferred: 5061168 bytes
Requests per second: 377.36 [#/sec] (mean)
Time per request: 26.50 [ms] (mean)
Time per request: 2.65 [ms] (mean)
Transfer rate: 19158.80 [Kbytes/sec]
20
Latency Test
1000 fetches, 5 max parallel,
2.9648e+07 bytes,
in 0.813035 seconds
29648 mean bytes/connection
1229.96 fetches/sec,
3.64658e+07 bytes/sec
msecs/connect:
0.463202 mean, 12.082 max, 0.045 min
msecs/first-response:
3.12969 mean, 50.783 max, 0.811 min
HTTP response codes:
code 200 -- 1000
1 msec = 0.0001 seconds
21
Profiling PHP Code
• APD (Pure profiler)
‣ http://pecl.php.net/apd
22
22
Profiling with APD
• Installation Steps
APD Start
• pecl install apd
Zend Execute
• Modify php.ini
APD Finish
zend_extension=apd.so
82.4 0.00 0.00 0.00 0.00 0.00 0.00 1 0.0007 0.0007 apd_set_pprof_trace
10.2 0.00 0.00 0.00 0.00 0.00 0.00 3 0.0000 0.0000 trim
4.3 0.00 0.00 0.00 0.00 0.00 0.00 3 0.0000 0.0000 base64_encode
1.9 0.00 0.00 0.00 0.00 0.00 0.00 3 0.0000 0.0000 test
0.6 0.00 0.00 0.00 0.00 0.00 0.00 1 0.0000 0.0001 array_walk
0.6 0.00 0.00 0.00 0.00 0.00 0.00 1 0.0000 0.0008 main
25
Drive Tuning
26
26
Drive Tuning Parameters
• Use the hdparm utility to adjust
settings.
• -c1 - set IDE 32-bit I/O setting
• -d1 - enable DMA
• -u1 - enable IRQ unmasking
• -m16 - turn on multicount
• -X 34|66|100|133 - transfer mode
27
27
Validating Changes
28
RAM Disk
• One way to accelerate File IO operations is by
moving the files and directories to a RAM disk.
• On Linux this is extremely simple to do using via
tmpfs.
# Speed Up /tmp Directory
mount --bind -ttmpfs /tmp /tmp
29
29
Session Storage
30
30
Session Storage Alternatives
31
Now let’s tune PHP code
32
OOP Tips
• Always declare your static methods!
• Cleaner & Faster code
<?php
class bench {
public function a() { return 1; }
public static function b() { return 1; }
}
$s = microtime(1);
for ($i = 0; $i < 100000; $i++) bench::a();
$e = microtime(1);
echo "Dynamic Static Method: ".($e - $s)."\n";
$s = microtime(1);
for ($i = 0; $i < 100000; $i++) bench::b();
$e = microtime(1);
echo "Declared Static Method: ".($e - $s)."\n";
33
Speed Comparison
0.20
0.15
0.10
0.05
Declared Non-Declared
34
Use Class Constants
35
Avoid Magic
• Magic methods such as __get()/__set()
• Magic loading functions such as
__autoload()
• Dynamic methods via __call()
36
require_once() is once too many
<?php
require_once "./a.php";
require_once "./a.php";
37
• If you absolutely cannot avoid
require_once and include_once use full
paths.
38
Avoid Pointless Function Calls
๏ php_version()
✓ PHP_VERSION constant
๏ php_uname(‘s’)
✓ PHP_OS constant
๏ php_sapi_name()
✓ PHP_SAPI constant
39
Quick Comparison
2.500
2.08
1.88
1.875
1.250
0.625
0.04
0
PHP_OS php_uname() php_uname(’s’)
40
Fastest Win32 Detection in the West
$isWindows =
DIRECTORY_SEPARATOR == '\\';
• Always available
41
What time is it?
Rather then calling time(),
time() and time() again, use
$_SERVER[‘REQUEST_TIME’]
42
PCRE Slowdowns
$text = preg_replace( '/=\?([^?]+)\?/',
'=?iso-8859-1?', $origtext );
$text = preg_replace(
'"/(\n|\t|\r\n|\s)+/"', ' ', $origtext );
43
Use non-capturing patterns
• Placing ?: at the start of a sub-pattern makes it
non-capturing.
$text = preg_replace( '/=\?(?:[^?]+)\?/',
'=?iso-8859-1?', $origtext );
35.00
30.92
26.25
26.38
17.50
8.75
0
Seconds
// Slow
if (preg_match("![a8f9]!", "sometext")) { }
// Faster
if (strpbrk("a8f9", "sometext")) { }
// Slow
if (preg_match("!string!i", "text")) {}
// Faster
if (stripos("text", "string") !== false) {}
46
More Regex Avoidance
$text = preg_replace( "/\n/", "\\n", $text);
$text = str_replace( "/\n/", "\\n", $text);
47
Speed Comparison
45.00
33.75
22.50
11.25
0
1 to 1 1 to 2 1 to 3 2 to 2
48
Use strtr() Properly!
$rep = array( '-' => '*', '.' => '*' );
if ( sizeof( $globArr ) > 1 ) {
$glob = "-" . strtr( $globArr[1], $rep );
} else {
$glob = strtr( $globArr[0], $rep );
}
49
Use Strings!
if ( sizeof( $globArr ) > 1 ) {
$glob = "-" . strtr( $globArr[1], '-.', '**' );
} else {
$glob = strtr( $globArr[0], '-.', '**' );
}
strtr(string) 4.29
strtr(array) Seconds
55.70
50
Don’t Replace When you
• Any replacement operation requires
memory, if only to store the “modified”
result.
51
Test Scenario
$str is a PHP 5.2 news files, roughly 95kb in size.
$s = microtime(1);
7.00
for ($i = 0; $i < 10000; $i++)
6.76
6.75
str_replace('Ilia', 'Derick', $str);
3.50 5.25
$e = microtime(1);
echo "non-check (match): ".($e - $s)."\n";
4.34
$s = microtime(1);
for ($i = 0; $i < 10000; $i++)
if (strpos($str, 'Ilia') !== false)
1.75
1.88
str_replace('Ilia', 'Derick', $str);
$e = microtime(1);
echo "check (match): ".($e - $s)."\n";
0
match
no-match
regular w/check
52
@ operator is evil!
• The error blocking operator, is the most
expensive character in PHP’s alphabet.
@action();
• This seemingly innocuous operator
actually performs fairly intensive
operations in the background.
$old = ini_set(“error_reporting”, 0);
action();
ini_set(“error_reporting”, $old);
53
Better String Comparison
<?php
// The Good
if (!strncmp(PHP_OS, 'WIN', 3)) {
if (!strncasecmp(PHP_OS, 'WIN', 3)) {
// The Bad
if (substr(PHP_OS, 0, 3) == 'WIN') {
if (strtolower(substr(PHP_OS, 0, 3))) == 'win') {
// And The Ugly
if (preg_match('!^WIN!', PHP_OS)) {
if (preg_match('!^WIN!i', PHP_OS)) {
54
Quick Benchmark
20
15
15.71
13.29
13.07 13.06
10
8.67 8.73 9.59
10.42
strcmp() 0
substr()
PCRE
EREG
55
Comparing From An Offset
• As of PHP 5, you don’t need to substr()
string segments from non-start position to
compare them thanks to substr_compare().
if (substr($class, -15) != 'text')
/* == */
if (substr_compare($class, 'text', -15))
56
Don’t Mis-use Constants
One of my biggest pet-peeves in PHP is
this kind of nonsense:
$foo = array("bar"=>0);
$foo[bar] = 1;
57
Why is this bad?
๏ 1 strtolower
๏ 2 hash lookups
๏ E_NOTICE error message generated
๏ temporary string being created on the
fly.
58
Performance Check
$foo[bar] = 1; /* vs */ $foo['bar'] = 1;
25.00
21.35 18.75
15.52
16.70
12.50
6.25
2.50
3 chars 2.70
0
6 chars 3.07
constant 17 chars
string
60
Simplify for() loop
Avoid function calls within for() loop
control blocks.
<?php
for ( $i = 1; $i < sizeof($array); $i++ ) {}
for ($j = 0; $j < count($_SERVER); $j++) {}
/* vs */
$c = count($_SERVER); for ($j = 0; $j < $c; $j++){}
1.53
strlen()
0.42
Before
After
62
Don’t Re-invent the Wheel
• It is surprising how frequently people try
to re-invent the wheel.
• Now a days PHP has
✓ 2,700 functions
✓ 80 core extensions
✓ 154 PECL extensions
• Chances are what you need already exists!
63
Use Full File Paths
64
The internals of file ops.
65
Reference Tricks
References can be used to simply & accelerate access
to multi-dimensional arrays.
$a['b']['c'] = array();
// slow 2 extra hash lookups per access
for($i = 0; $i < 5; $i++)
$a['b']['c'][$i] = $i;
66
66
Optimization Myths
✦ Removing comments makes code faster
✦ Using “ is faster then ‘
✦ Passing things by-reference makes code
faster
✦ Objects make code faster
✦ Ternary ? : is faster then if () { } else {}
67
Caching is the recognition and exploitation of
the fact that most "dynamic" data does not
change every time you request it.
68
68
Most applications will end up using databases
for information storage. Improper use of this
resource can lead to significant and
continually increasing performance loss.
69
69
Check Your Queries
EXPLAIN select * from users where login LIKE '%ilia%';
+----------+------+---------------+------+---------+------+-------+------------+
| table | type | possible_keys | key | key_len | ref | rows | Extra |
+----------+------+---------------+------+---------+------+-------+------------+
| mm_users | ALL | NULL | NULL | NULL | NULL | 27506 | where used |
+----------+------+---------------+------+---------+------+-------+------------+
70
70
Bitwise Option Packing
Rather then creating a column for every Boolean
option, you can pack 32 of them into a single integer
field.
CREATE TABLE users (
CREATE TABLE users ( user_opt INT,
is_active INT, ...
is_banned INT, );
is_admin INT,
... user_opt & 1 // active
); user_opt & 2 // banned
user_opt & 4 // admin
71
71
KISS = Performance
• The simpler the code, the faster it runs,
it really is that simple.
• Syntactic sugar.
• Unnecessary wrappers.
• Wrapping one liners in functions.
• OO for the sake of OO.
72
72
Thank You For Listening!
• These slides
• http://www.ilia.ws/
• APC
• http://pecl.php.net/apc
• XDebug
• http://www.xdebug.org/
73
Optimizer (Why?)
i
• The opcodes generated by Zend
Engine are often inefficient.
• Some operations can be avoided
• A lot of temporary vars are not
necessary.
• Every compiler needs an
optimizer ;-)
74
Optimizer (How?)
PHP Script
Zend Compile
Zend Execute
75
What Can It Do?
• opt. heredoc • resolve partial file
paths.
• print to echo
• optionally inline
• GLOBALS[foo] to define() calls
foo
• 60+ function calls
• inline known with static values
constants resolved.
• eliminate NOP • Much more...
76
Any other ideas?
77