By Brian Shire
High
Performance
APC
Performance is sacrificed after a server
restart.
The server is slammed with
requests while it attempts to cache files,
user variables, and spawn processes.
Gives users seemingly random and
unpredictible poor performance.
PHP
How "Compilation" works:
(the process of turning source code into opcodes)
Lexer
#line 5847 "Zend/zend_language_scanner.c"yy543:
YYDEBUG(543, *YYCURSOR);
yych = *++YYCURSOR;
if (yych == 'N') goto yy553;
if (yych == 'n') goto yy553;
goto yy281;
yy544:
YYDEBUG(544, *YYCURSOR);
yych = *++YYCURSOR;
if (yych == 'O') goto yy545;
if (yych != 'o') goto yy281;
yy545:
YYDEBUG(545, *YYCURSOR);
yych = *++YYCURSOR;
if (yych == 'L') goto yy546;
if (yych != 'l') goto yy281;
yy546:
YYDEBUG(546, *YYCURSOR);
yych = *++YYCURSOR;
if (yych == 'E') goto yy551;
if (yych == 'e') goto yy551;
goto yy548;
yy547:
YYDEBUG(547, *YYCURSOR);
++YYCURSOR;
YYFILL(1);
yych = *YYCURSOR;
Parser
Opcodes
Lexical Analysis of human readable
code into Lexicons that can form
syntax...
echo "Hello World!";
T_ECHO
T_STRING
Parsing of Lexicons into Opcodes
Validation of language syntax and use...
foreach_variable:
variable { zend_check_writable_variable(&$1); $$ = $1; }
| '&' variable { zend_check_writable_variable(&$2); $$ = $2; $$.u.EA.type |= ZEND_PARSED_REFERENCE_VARIABLE; };
for_statement:
statement | ':' inner_statement_list T_ENDFOR ';';
foreach_statement:
statement | ':' inner_statement_list T_ENDFOREACH ';';
declare_statement:
statement | ':' inner_statement_list T_ENDDECLARE ';';
Operation Code instructs the direction
of the PHP virtual machine. The interpreter
version of assembly code for CPUs.
<?php
$output = "Hello World!"
echo $output;
return;
<?php
$output = "Hello World"
if ($_GET['exclaim']) {
$output .= '!';
} else {
$output .= ".";
}
echo $output;
ASSIGN !0 ‘Hello+World%21’
ECHO !0
RETURN 1
ZEND_HANDLE_EXCEPTION
ASSIGN !0, ‘HELLO+WORLD’
FETCH_R GLOBAL $1, ‘_GET’
FETCH_DIM_R $2, $1, ‘exclaim’
JMPZ $2, ->6
ASSIGN_CONCAT !0, ‘%21’
JMP ->7
ASSIGN_CONCAT !0, ‘.’
ECHO $0
RETURN 1
ZEND_HANDLE_EXCEPTION
Interpretation of the Opcodes by the
PHP VM. Include or eval statements
trigger nested compilation and execution cycles.
ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL,
CONST|TMP|VAR|CV, ANY)
{
zend_op *opline = EX(opline); zend_op_array
*new_op_array=NULL;
int return_value_used;
zend_free_op free_op1;
zval *inc_filename = GET_OP1_ZVAL_PTR(BP_VAR_R);
zval tmp_inc_filename;
zend_bool failure_retval=0;
if (inc_filename->type!=IS_STRING) {
tmp_inc_filename = *inc_filename;
zval_copy_ctor(&tmp_inc_filename);
convert_to_string(&tmp_inc_filename);
inc_filename = &tmp_inc_filename;
}
Normal Compilation
Lexer
Parser
APC Compilation
Source
Source File Not Cached or
Updated on Disk?
Copy Opcodes
for Execution
Opcodes
Execution
Store Opcodes
in APC Cache
APC
Installation & Configuration
http://pecl.php.net/packages/APC/
cvs -d :pserver:cvsread@cvs.php.net:/repository/ checkout pecl/apc
$ pecl install apc
Download via HTTP or CVS:
Automated PECL installation:
$ phpize && ./configure && make install
Build and Install:
- or -
; APC
apc.enable=1
apc.shm_size=400
apc.ttl=172800
apc.user_ttl=172800
apc.num_files_hint=500
apc.user_entries_hint=60000
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.file_update_protection=15
apc.write_lock=1
apc.stat=0
Begin with a simple configuration.
Increase file and user variable
hints for your application.
Verify that the shm_size setting is
enough memory for files and vars.
Install the apc.php to view cache
statistics and contents.
Opcode Cache
Filesystem stat() calls...
$ strace -e trace=file httpd -X
stat("/www/html/test.php", {st_mode=S_IFREG|0644, st_size=710, ...}) = 0
getcwd("/usr/local/apache/bin", 4095) = 41
chdir("/www/html") = 0
open("/www/html/includes/1.inc", O_RDONLY) = 7
stat("/www/html/includes/1.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/2.inc", O_RDONLY) = 8
stat("/www/html/includes/2.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/3.inc", O_RDONLY) = 9
stat("/www/html/includes/3.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/4.inc", O_RDONLY) = 10
stat("/www/html/includes/4.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/5.inc", O_RDONLY) = 11
stat("/www/html/includes/5.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/6.inc", O_RDONLY) = 12
stat("/www/html/includes/6.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/7.inc", O_RDONLY) = 13
stat("/www/html/includes/7.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/8.inc", O_RDONLY) = 14
stat("/www/html/includes/8.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/9.inc", O_RDONLY) = 19
stat("/www/html/includes/9.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
open("/www/html/includes/10.inc", O_RDONLY) = 20
stat("/www/html/includes/10.inc", {st_mode=S_IFREG|0644, st_size=16, ...}) = 0
chdir("/usr/local/apache/bin") = 0
File paths need to be resolved to
real paths (resolve symbolic links).
Files must also be stat()'d to check
for modifications not yet cached.
PHP-5.2.x, apc.stat=1
apc.stat=0
Disable the check for any updates
to files on disk.
Big performance increase.
Requires server restart or manual
insertion of files to update code.
User Variable
Cache
Binary Dumps
Ability to dump the file and variable user
caches to file, stream, or string.
Architecture specific, you can't transfer
files between different systems unless
they are identical.
Still "experimental".
Not intended as an obfuscation mechanism.
Cache Priming
To overcome this performance problem
we can prime the cache prior to accepting
requests...
apc_compile_file()
Ability to programatically insert a file
into the cache.
Accepts a single filename or an array
of filenames.
Lazy Loading
Simple API to store PHP variables
apc_store($key, $value)
$value = apc_fetch($key)
Effective cache usage
Cache is server specific, so unless
users are "sticky" to a server user
specific data is not ideal due to
size constraints.
Site-wide data is the ideal data.
Think of APC as a giant array, store
individual values rather than large arrays
of values. Currently values must be copied
from the cache to the local process, so size
matters.
Control!
Control the site behavior with APC variables.
Faster respones to enable, change, or disable
features.
Better user experience, easier to push new
features with a large number of servers.
The Future...
APC-4.0
A major limitation of APC is that it does not
evict least-used items like memcache.
APC-4.0 is an experimental branch that adds:
Least Frequently Used (LFU) cache eviction.
Multiple caches, with individual configurations.
extension=apc.so
apc.enabled=1
apc.enable_cli=1
apc.file_caches = "file"
apc.user_caches = "primary, lfu"
apc.segments = "200M, 500M, 500M"
apc.coredump_unmap = "1, 2"
apc.mmap_file_mask=/tmp/apc.XXXXXX
apc.optimization=0
apc.include_once_override=0
apc.enable_binfiles=0
apc.binfiles_path="/Users/shire/www.bin"
apc.file.desc = "Primary File Cache"
apc.file.segment = 0
apc.file.gc_ttl = 3600
apc.file.expunge_method = "flush"
apc.file.entries_hint = 4096
apc.file.stat = 0
apc.file.ttl=172800
apc.file.cache_by_default = 1
apc.file.slam_defense = 0
apc.file.file_update_protection = 15
apc.file.max_file_size = 1Mapc.file.stat_ctime = 0
apc.file.write_lock = 1
apc.file.filters = ""
apc.file.lazy_functions=1
apc.file.lazy_classes=1
apc.primary.desc = "Primary Variable Cache"
apc.primary.segment = 1
apc.primary.gc_ttl = 3600
apc.primary.expunge_method = "lfu"
apc.primary.entries_hint = 1048576
apc.primary.ttl=0
apc.lfu.desc = "LFU Variable Cache"
apc.lfu.segment = 2
apc.lfu.gc_ttl = 3600
apc.lfu.expunge_method = "lfu"
apc.lfu.entries_hint = 1048576
apc.lfu.ttl=0
Abstract Extension API and Dependency Interface
GSoC Project
by Varuna Jayasiri
No-copy shared cache.
*under research*
Currently APC and other opcode caches must copy
opcode and other data from shared memory to a local
process when a file (opcodes) or a user variable is requested.
It may be possible to remove this cost, creating a huge cost
savings for APC and likely other opcode caches.
Renders lazy loading unecessary!
Should have results in the next month or two.
George Schlossnagle
Daniel Cowgill
Rasmus Lerdorf
Gopal Vijayaraghavan
Edin Kadribasic
Ilia Alshanetsky
Marcus Börger
Sara Golemon
Brian Shire
Performance
Locking
If you use SVN, CVS, rsync or
other similar tools to push code
*and* apc.stat=1, then you may
need to enable apc.stat_ctime=1.
This ensures that the creation
time rather than modification time
signifies file updates, as these
tools often backdate
modification times...
EOF
questions?
Primary
APC Developers:
Live in San Francisco.
Work for Facebook, Palo Alto.
and I Focus on APC & PHP stack performance.
email: shire@tekrat.com or shire@facebook.com
blog: http://tekrat.com/
Code
<?php
echo 'Hello World!';
Execution
$ strace -e trace=file httpd -X
stat("/www/html/test.php", {st_mode=S_IFREG|0644, st_size=710, ...}) = 0
getcwd("/usr/local/apache/bin", 4095) = 41
chdir("/www/html") = 0
open("/www/html/includes/1.inc", O_RDONLY) = 7
open("/www/html/includes/2.inc", O_RDONLY) = 8
open("/www/html/includes/3.inc", O_RDONLY) = 9
open("/www/html/includes/4.inc", O_RDONLY) = 10
open("/www/html/includes/5.inc", O_RDONLY) = 11
open("/www/html/includes/6.inc", O_RDONLY) = 12
open("/www/html/includes/7.inc", O_RDONLY) = 13
open("/www/html/includes/8.inc", O_RDONLY) = 14
open("/www/html/includes/9.inc", O_RDONLY) = 19
open("/www/html/includes/10.inc", O_RDONLY) = 20
chdir("/usr/local/apache/bin") = 0
PHP-5.2.x, apc.stat=0
apc_bindump_file("gzip://...")
apc_binload_file("gzip://...")
API is simple, and supports streams:
OSCON 2009
Brian Shire
APC.php...
Copying opcodes out of shared memory causes
delays attributed to include() or require() calls.
Lazy Loading is an experimental patch to delay
copy until a function or class is actually used.
This can significantly reduce include time for
large codebases.
apc_compile_file($filename);
apc_compile_file($file_array);
Any PHP value can be stored, except for resources.
Objects have to be serialized, this is a significant
performance loss, so it's not recommended.
Data center A
Data center B
Web Server 1
Web Server 2
Web Server ...
Web Server 1
Web Server 2
Web Server ...
Central update script
spawns multiple HTTP
requests to individual
servers.
Updates are made in the time
it takes for a HTTP Request.
Can be scaled by distributing to
per data center update scripts.
Internal Infrastructure
Internet/Users
User experiences
near immediate
changes in
functionality.
Normal enable/disable.
List of file and user caches
by name descriptions.
Segment sizes.
"file" cache configuration.
Which segment to use.
"primary" cache configuration.
"lfu" cache configuration.
LFU expunge method.
Develop functionality for PHP that will allow extensions to register a
set of functions as a versioned API.
This could potentially allow for APC to export it's shared memory storage
and mechanisms to other extensions.
More information and updates:
http://wiki.php.net/gsoc/2009/api
Contact info...
Brian Shire: shire@tekrat.com, http://tekrat.com
Graham Kelly: graham@php.net
Iterator, Iterator, Iterator...
$iterator = new APCIterator(APC_LFU,
'/some_key/',
APC_ITER_KEY |
APC_ITER_HITS |
APC_ITER_MTIME);
foreach($iterator as $key=>$info) { ...
}
Needed the ability to search for and browse cache
entries quickly and efficiently, up to many millions
of entries.
The only way to do this was move searching facilities
to the C extension, and browse by smaller chunks...
pecl/optimizer
Sample Code... before...
APC optimizer
Ilia's optimizer
- Ilia Alshanetsky
GSoC :: pecl/optimizer
Facebook :: pecl/optimizer2
turck MMCache
- Dmitry Stogov
History of sorts...
Overview
PHP Extension :: lives in Pecl
APC optimizes initilization and datapath
Optimizer improves execution performance!
return;
while(1) {
}
/* Future Work */
Sample Code... after optimization...
Switchtable
Similar to the extension by Stefan Esser
Many performance improvments can be made!
Cross file optimization
Crazy Crazy JIT???
Currently includes and requires make optimization hard
<http://www.suspekt.org/switchtable/>
Only one devloper, plenty of work!
graham@php.net
http://pecl.php.net/package/optimizer
http://svn.php.net/viewvc/pecl/optimizer/
APC + Optimizer
Source
Lexer
Parser
Optimize
opcodes
Optimizer
Normal Compilation
Store opcodes
in APC
Source file cached
or updated on disk?
Copy opcodes
for execution
Execution
Opcodes
APC Compilation
<?php
/**
* Returns the size of an integer in PHP (actually a long in the zval)
*
* @return Integer
*/
function sizeof_int() {
$shifted = ($org = 1) << 32;
return $shifted == $org ? 32 : 64;
}
$bits = sizeof_int();
if (strtolower(substr(PHP_OS, 0, 3)) == 'win') {
echo("You are running ".PHP_OS."\n");
echo("sizeof(int) = ".$bits."\n");
} else {
echo("You are not running Windows\n");
echo(" (good for you!)\n");
echo("sizeof(int) = ".$bits."\n");
}
?>
<?php
function sizeof_int() {
return 64;
}
echo("You are not running Windows\n (good for you!)\nsizeof(int) = 64\n");
?>
+/* {{{ apc_eapi_init */
+void apc_eapi_init()
+{
+ apc_eapi api = {0};
+
+ /* Low-level SMA access */
+ api.alloc = apc_eapi_alloc;
+ api.free = apc_eapi_free;
+
+ /* Register our Extension API functions */
+ zend_eapi_register("APC", "1.0.0.0", (void *)&api, sizeof(api));
+
+} /* }}} */
High Performance APC talk for 2009