100% found this document useful (2 votes)
164 views

PHP & Performance: By: Ilia Alshanetsky

PHP & Performance discusses optimizing PHP and web server performance. Some key points include: 1. PHP scripts are compiled on every request, so reducing compilation time improves performance. Opcode caches store pre-compiled scripts in memory for faster execution. 2. Profiling tools like APD and XDebug can identify bottlenecks and measure resource usage. This helps optimize code. 3. Configuration settings like output buffering, compression, and minimzing system calls can boost performance. Proper Apache and OS settings also help maximize throughput.

Uploaded by

Alex
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
100% found this document useful (2 votes)
164 views

PHP & Performance: By: Ilia Alshanetsky

PHP & Performance discusses optimizing PHP and web server performance. Some key points include: 1. PHP scripts are compiled on every request, so reducing compilation time improves performance. Opcode caches store pre-compiled scripts in memory for faster execution. 2. Profiling tools like APD and XDebug can identify bottlenecks and measure resource usage. This helps optimize code. 3. Configuration settings like output buffering, compression, and minimzing system calls can boost performance. Proper Apache and OS settings also help maximize throughput.

Uploaded by

Alex
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF or read online on Scribd
You are on page 1/ 77

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

• Each PHP script is compiled only once for


each revision.
• Reduced File IO, opcodes are being read
from memory instead of being parsed from
disk.
• Opcodes can optimized for faster execution.

3
Quick Comparison
200

150

100

50 Stock PHP 4.4.0


APC
FUDforum PHP Accelerator
Smarty
0 eAccelerator
phpMyAdmin Zend Platform

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"

• Enable all compiler optimizations with -O3


• Tune the code to your CPU via -march –mcpu
• CPU specific features -msse –mmmx
-mfpmath=sse
• Drop debug data -fomit-frame-pointer
5
5
Web Server: File IO
✓ Keep ✓ If logs are
DirectoryIndex file unnecessary disable
list as short as possible. them.
✓ Whenever possible ✓ If logging is a must,
disable .htaccess via log everything to 1 file
AllowOverride and break it up during
none. the analysis stage.
✓ Use Options
FollowSymLinks to
simplify file access
process in Apache.

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

PHP Apache OS Client

• The goal is to pass off as much work to the kernel


as efficiently as possible.
• Optimizes PHP to OS Communication
• Reduces Number Of System Calls

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

• The idea is to hand off entire page to the


kernel without blocking.

Apache OS

• Set SendBufferSize = PageSize

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

✴ Be careful on low memory systems!


12
Static Content Serving
• While Apache is great for dynamic requests,
static requests can be served WAY FASTER
by other web servers.
๏ lighttpd
๏ Boa
๏ Tux
๏ thttpd
• For static requests these servers are easily
300-400% faster then Apache 1 or 2.
13
13
Less Output == Faster
• Saves server bandwidth (saves $$ too).
• Reduces server resource usage (CPU/
Memory/Disk)
• Pages load faster for clients.
• Reduces network IO high traffic sites,
where it is the primary bottleneck in
most cases.

14
Content Compression
• Most browsers support content compression.
• Compressed pages are on average are 6-8 times smaller.

๏ Apache 1 (mod_gzip / mod_deflate)


๏ Apache 2 (mod_deflate)
๏ PHP
‣ From PHP configuration zlib.output_compression=1

‣ From inside the script ob_start(“ob_gzhandler”)

✴ Compression will utilize 3%-5% of CPU.

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

• http_load (Excellent for latency tests)


‣ http://www.acme.com/software/http_load/

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

• XDebug (Profiler & Debugger)


‣ http://xdebug.org/

• DBG (Profiler & Debugger)


‣ http://dd.cron.ru/dbg/

22
22
Profiling with APD

• Installation Steps
APD Start
• pecl install apd
Zend Execute
• Modify php.ini
APD Finish

zend_extension=apd.so

Process repeated for


every function/method call
23
Generating A Trace
• Profiling of a script starts from the point when the
apd_set_pprof_trace() function is called.
• All code executed prior, will not be profiled.
$parts = preg_split("!\s!", "a b c");
function test(&$var) {
$var = base64_encode(trim($var));
}
apd_set_pprof_trace();
array_walk($parts, 'test');

✴ Use the auto_prepend_file php.ini setting to activate


profiling for an entire application.
24
24
Interpreting the Results
Real User System secs cumm.
%Time (excl/cumm) (excl/cumm) (excl/cumm) Calls call s/call Name
-----------------------------------------------------------------------------------------

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

• Hard-drive is in most cases the slowest


part of the system, yet all the data
eventually comes from it.
• By adjust the drive configuration
parameters you can help your OS get
the most out of it.

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

Benchmark the affect of the changes using:


hdparm -tT /dev/[drive]

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

# Accelerate Scripts Directory


mount --bind -ttmpfs /home/webroot /home/webroot

29
29
Session Storage

• PHP’s session extension by default stores each


session inside a separate file.
• Many files in one directory reduce access speed.
➡ Assign each user their own session directory
➡ Split sessions into multiple directories
session.save_path = "N;/path"

30
30
Session Storage Alternatives

• File system is slow, lets use memory


• mm - native shared memory storage
• apc - use APC’s store/fetch/delete
• memcache - memory storage daemon

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

• Parsed at compile time, no execution


overhead.
• Faster lookups due to a smaller hash.
• “Namespacing” & shorter hash names.
• Cleaner code speeds up debugging ;-)

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";

lstat64("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=7368, ...}) = 0


lstat64("/tmp/a.php", {st_mode=S_IFREG|0644, st_size=6, ...}) = 0
open("/tmp/a.php", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=6, ...}) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=6, ...}) = 0

lstat64("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=7368, ...}) = 0


lstat64("/tmp/a.php", {st_mode=S_IFREG|0644, st_size=6, ...}) = 0
open("/tmp/a.php", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=6, ...}) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=6, ...}) = 0

37
• If you absolutely cannot avoid
require_once and include_once use full
paths.

• In PHP 5.2>= this will allow PHP to


avoid opening the file twice.

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 == '\\';

• Does not use functions

• Does not care about


WinXP, WinNT, Windows,
Windows98, NT 5.0, etc...

• Always available

41
What time is it?
Rather then calling time(),
time() and time() again, use

$_SERVER[‘REQUEST_TIME’]

Provides a timestamp, with a


second precision, without any
function calls.

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 );

• This means PHP/PCRE does not need to


allocate memory to store the matched content
block.
$text = preg_replace( 
'"/(?:\n|\t|\r\n|\s)+/"', ' ', $origtext );
44
End Result
Capuring
Non-Capturing

35.00
30.92
26.25
26.38
17.50

8.75

0
Seconds

A 15% performance improvement, with


a 2 character change.
45
If Possible Avoid Regex
<?php
// Slow
if (preg_match("!^foo_!i", "FoO_")) { }
// Much faster
if (!strncasecmp("foo_", "FoO_", 4)) { }

// 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);

In this case it would be simpler and to


mention faster to use a regular str_replace()

$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

preg_replace str_replace strtr

48
Use strtr() Properly!
$rep = array( '-' => '*', '.' => '*' );

if ( sizeof( $globArr ) > 1 ) {
$glob = "-" . strtr( $globArr[1], $rep );
} else {
$glob = strtr( $globArr[0], $rep );
}

Any ideas on how we can make this code


10 times faster?

49
Use Strings!
if ( sizeof( $globArr ) > 1 ) {
$glob = "-" . strtr( $globArr[1], '-.', '**' );
} else {
$glob = strtr( $globArr[0], '-.', '**' );
}

Elimination of array operations speeds up


the code and simplifies the internal work
in strtr() function. 0 15 30 45 60

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.

• A quick strpos() to determine if any


replacement is actually needed can save
memory and improve performance!

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

Case Sensetive Non-Case Sensetive

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

700% difference on average!!!


59
Fix “harmless” error messages

‣ Each error results in:


๏ Generation of a complex error string
๏ Output to stdout/stderr
๏ Potential write to a file or syslog
๏ In pre-5.2 releases may leak memory
in some rare instances.

60
Simplify for() loop
Avoid function calls within for() loop
control blocks.
<?php
for ( $i = 1; $i < sizeof($array); $i++ ) {}

for ( $i = 0; $i < count($array); $i++ ) {}

for ( $i = 0; $i < strlen($string); $i++ ) {}

Otherwise function is called for every loop


iteration.
61
Speed Check
for ($j = 0; $j < strlen('foo'); $j++) {}
/* vs */
$c = strlen('foo'); for ($j = 0; $j < $c; $j++) {}

for ($j = 0; $j < count($_SERVER); $j++) {}
/* vs */
$c = count($_SERVER); for ($j = 0; $j < $c; $j++){}

0 0.4 0.8 1.2 1.6 2.0


0.21
count()
0.11

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

• While it is convenient(??) to do require


“foo.php” and have it work, internally
it leads to significant overhead.

• Whenever possible you should use full


paths, that require no resolution in PHP.

64
The internals of file ops.

stat64("./b.php", {st_mode=S_IFREG|0644, st_size=6, ...}) = 0


getcwd("/tmp", 4096) = 5
lstat64("/tmp", {st_mode=S_IFDIR|S_ISVTX|0777, st_size=18008, ...}) = 0
lstat64("/tmp/b.php", {st_mode=S_IFREG|0644, st_size=6, ...}) = 0
open("/tmp/b.php", O_RDONLY)

The issue can be further exasperated by


the use of include_path.

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;

// much faster reference based approach


$ref =& $a['b']['c'];
for($i = 0; $i < 5; $i++)
$ref[$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 |
+----------+------+---------------+------+---------+------+-------+------------+

Most databases offers tools for analyzing


query execution.

EXPLAIN select * from users where login LIKE 'ilia%';


+----------+-------+---------------+-------+---------+------+------+------------+
| table | type | possible_keys | key | key_len | ref | rows | Extra |
+----------+-------+---------------+-------+---------+------+------+------------+
| mm_users | range | login | login | 50 | NULL | 2 | 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

Kinda Slow Optimizer

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

You might also like