Excimer
![]() Release status: beta |
|
---|---|
MediaWiki | |
License | Apache License 2.0 |
Download | GitHub: |
Issues | Open tasks · Report a bug |
Excimer is a PHP 7.1+ extension that provides an interrupting timer and a low-overhead sampling profiler.
Installation[edit]
Debian[edit]
apt-get install php-excimer
See packages.debian.org and packages.ubuntu.com
PECL[edit]
pecl install excimer
See our PECL entry.
Source[edit]
Download the source code into an appropriate directory from git:
git clone https://gerrit.wikimedia.org/r/mediawiki/php/excimer.git
Or download a snapshot and unpack.
Build[edit]
Excimer requires Linux.
Below excimer/
is the directory that excimer Git repository was cloned to.
cd excimer/
phpize && ./configure && make && sudo make install
Then add extension=excimer.so
to the PHP configuration in an appropriate place. For example, in modern Debian-derived distributions you'd add a file to /etc/php/$version/mods-available
(where $version
is the version of PHP for which you compiled Excimer) and use the phpenmod
command to enable it.
Background[edit]
We found that just enabling Tideways XHProf on a server with PHP 7.2 caused a performance degradation of about 4%. We believe this is due to it installing a zend_execute_ex
hook, which intercepts all function calls in PHP.[1] Even though this hook does nothing by default, just having it causes PHP's VM to omit certain optimisations. The hook can only be installed globally, and it is not possible to enable it just for a particular request.
Excimer does not install any zend_execute_ex
hook. This means it cannot count function calls, but makes it suitable for production-wide installation.
Reference documentation[edit]
For now, please see the function stubs in the extension source.
Examples[edit]
Use a one-shot timer to limit the execution time of a function[edit]
$timer = new ExcimerTimer;
$timer->setInterval( 10 /* seconds */ );
$timer->setCallback( function () {
throw new Exception( "The allowed time has been exceeded" );
} );
$timer->start();
do_expensive_thing();
When the timer expires, Excimer sets a flag which is then checked by the PHP VM every time a function returns, and on every loop iteration. For CPU-intensive tasks, this is usually often enough. However, it will not interrupt long-running C functions such as MySQL connection attempts.
When the exception is thrown, the backtrace will show the callback being called from the interrupted code.
Note that the $timer
variable must be kept alive, for example by keeping it in scope. If the timer object is destroyed, the timer will stop and the callback will not be called.
Use a periodic timer to gather time-series memory usage statistics[edit]
$timer = new ExcimerTimer;
$timer->setPeriod( 0.1 );
$startTime = microtime( true );
$timer->setCallback( function () use ( $startTime ) {
$usage = sprintf( "%.2f", memory_get_usage() / 1048576 );
$interval = microtime( true ) - $startTime;
$date = sprintf( "%.2f", $interval );
file_put_contents(
'/tmp/php-memory-usage',
"$date\t$usage\n",
FILE_APPEND );
} );
$timer->start();
...
The process will be interrupted once every 100ms, and the current memory usage logged to a file.
Profile a process and produce a flame graph[edit]
$profiler = new ExcimerProfiler;
$profiler->setPeriod( 0.01 );
$profiler->setEventType( EXCIMER_CPU );
$profiler->start();
register_shutdown_function( function () use ( $profiler ) {
$profiler->stop();
$pipe = popen( "/usr/local/bin/flamegraph.pl > /tmp/profile.svg", "w" );
fwrite( $pipe, $profiler->getLog()->formatCollapsed() );
} );
In this example, FlameGraph is presumed to be installed at /usr/local/bin/flamegraph.pl
. An SVG file is written to /tmp/profile.svg
. An example of the typical output can be seen at https://tstarling.com/stuff/excimer-flamegraph.svg .
Profile a process and produce a function table[edit]
$profiler = new ExcimerProfiler;
$profiler->setPeriod( 0.01 );
$profiler->setEventType( EXCIMER_CPU );
$profiler->start();
register_shutdown_function( function () use ( $profiler ) {
$profiler->stop();
$report = sprintf( "%-79s %14s %14s\n", 'Function', 'Self', 'Inclusive' );
foreach ( $profiler->getLog()->aggregateByFunction() as $id => $info ) {
$report .= sprintf( "%-79s %14d %14d\n", $id, $info['self'], $info['inclusive'] );
}
file_put_contents( '/tmp/aggregated.txt', $report );
} );
In this example, event counts are aggregated by function name, formatted in a fixed-width table, and written to a file. An example of typical output can be seen at https://tstarling.com/stuff/excimer-aggregated.txt .
Maintainer notes[edit]
For information about how to update the PECL release, see how to PECL.
Further reading[edit]
- Profiling PHP in production at scale (Tech blog, December 2020)
- Arc Lamp, generate flame graphs for PHP in production using Excimer as sampling profiler.
- Browse source code (GitHub mirror)
References[edit]
- ↑ Comment on "Excimer: new profiler for PHP", Tim Starling (2018), phabricator.wikimedia.org.