PHP Benchmarking Framework

Overview

Athletic

Athletic is a benchmarking framework. It allows developers to benchmark their code without littering microtime() calls everywhere.

Athletic was inspired by the annotation format that PHPUnit uses. Benchmark tests extend the AthleticEvent class and are annotated with specific docblock parameters. The benchmark suite is then run with the Athletic command-line tool.

Branch Unit Tests Coverage
Latest Stable Version Build Status Coverage Status

WARNING: No longer maintained

Athletic is not currently being maintained, so it may have bugs or other problems. I would recommend using PhpBench, which was inspired by Athletic. It is far more capable, and actively maintained

Why Benchmark?

Because fast code is good! While premature optimization is certainly evil, optimization is always an important component of software development. And sometimes you just really need to see if one solution to a problem is faster than an alternative.

Why Use Athletic?

Because it makes benchmarking easy! Athletic is built around annotations. Simply create a benchmarking class and annotate a few methods:

/**
 * @iterations 1000
 */
public function fastIndexingAlgo()
{
    $this->fast->index($this->data);
}

Without Athletic, you have to litter your code with microtime() calls and build timing metrics yourself. Or you end up building a benchmark harness remarkably similar to Athletic (but probably with less syntactic sugar...because who builds throw-away code to read test annotations and fancy output?)

Why can't I use xDebug?

xDebug is an excellent profiling tool, but it is not a benchmarking tool. xdebug (and by extension, cachegrind) will show you what is fast/slow inside your method, and is indispensable for actually optimizing your code. But it is not useful for running 1000 iterations of a particular function and determining average execution time.

Quick Installation via Composer

You can easily install Athletic through Composer in two steps:

# Install Composer
curl -sS https://getcomposer.org/installer | php

# Add Athletic as a dev dependency
php composer.phar require athletic/athletic:~0.1 --dev

You can find out more on how to install Composer, configure autoloading, and other best-practices for defining dependencies at getcomposer.org.

Usage

To begin using Athletic, you must create an Event. This is the analog of a PHPUnit Test Case. An Event will benchmark one or more functions related to your code, compile the results and output them to the command line.

Here is a sample Event:

<?php

namespace Vendor\Package\Benchmarks\Indexing;

use Vendor\Package\Indexing;
use Athletic\AthleticEvent;

class IndexingEvent extends AthleticEvent
{
    private $fast;
    private $slow;
    private $data;

    public function setUp()
    {
        $this->fast = new Indexing\FastIndexer();
        $this->slow = new Indexing\SlowIndexer();
        $this->data = array('field' => 'value');
    }


    /**
     * @iterations 1000
     */
    public function fastIndexingAlgo()
    {
        $this->fast->index($this->data);
    }


    /**
     * @iterations 1000
     */
    public function slowIndexingAlgo()
    {
        $this->slow->index($this->data);
    }

}

Let's look at how this works.

<?php

namespace Vendor\Package\Benchmarks\Indexing;

use Vendor\Package\Indexing;
use Athletic\AthleticEvent;

First, we have a PHP file that is included in your project's repo, just like unit tests. In this example, the Event is saved under the Vendor\Package\Benchmarks\Indexing namespace. It uses classes from your project located at Vendor\Package\Indexing. It also uses a class from the Athletic framework.

class IndexingEvent extends AthleticEvent
{

Next, we declare an indexing class that extends \Athletic\AthleticEvent. This is important because it tells Athletic that this class should be benchmarked. AthleticEvent is an abstract class that provides code to inspect your class and actually run the benchmarks.

    private $fast;
    private $slow;
    private $data;

    public function setUp()
    {
        $this->fast = new Indexing\FastIndexer();
        $this->slow = new Indexing\SlowIndexer();
        $this->data = array('field' => 'value');
    }

Next, we have some private variables and a setUp() method. The setUp() method is invoked once at the beginning of each benchmark iteration. This is a good place to instantiate variables that are important to the benchmark itself, populate data, build database connections, etc. In this example, we are building two "Indexing" classes and a sample piece of data. (More details about setup and tear down are further down in this document)

    /**
     * @iterations 1000
     */
    public function fastIndexingAlgo()
    {
        $this->fast->index($this->data);
    }


    /**
     * @iterations 1000
     */
    public function slowIndexingAlgo()
    {
        $this->slow->index($this->data);
    }

Finally, we get to the meat of the benchmark. Here we have two methods that are annotated with @iterations in the docblock. The @iterations annotation tells Athletic how many times to repeat the method. If a method does not have an iterations annotation, it will not be benchmarked.

That's it! Now you are ready to run the benchmark.

Running Athletic

A benchmark test is run from the command line:

$ php ./vendor/bin/athletic -p /home/ProjectDir/benchmarks/ -b /home/ProjectDir/vendor/autoload.php

The tool has a few flags that can be set:

Flag Long Form Required Description
-p --path Yes Specifies the path to the Events to benchmark. Will recursively load all files/classes that extend AthleticEvent
-b --bootstrap Sets the path to an optional bootstrap file which is included before anything else. This is often used to include an autoloader for your project.
-f --formatter User-configured formatter to use instead of DefaultFormatter
-h --help Help screen with options and their descriptions

Note: Athletic is intended to be used as a single Phar archive, but that process has not been built yet. Soon!

Output

So what does the output of a benchmark look like?

$ php ./vendor/bin/athletic -p /home/ProjectDir/benchmarks/ -b /home/ProjectDir/vendor/autoload.php

Vendor\Package\Benchmarks\Indexing\IndexingEvent
    Method Name             Iterations    Average Time      Ops/second
    ---------------------  ------------  --------------    -------------
    fastIndexingAlgo:      [1000      ] [0.0020904064178] [478.37588]
    slowIndexingAlgo:      [1000      ] [0.0048114223480] [177.59184]

The default formatter outputs the Event class name, each method name, the number of iterations, average time and operations per second. More advanced formatters will be created in the near future (CSVFormatter, database export, advanced statistics, etc).

Further Information

SetUps and TearDowns

Athletic offers several methods to setup and tear down data/variables.

Method Description
classSetUp() Invoked at the beginning of the Event before anything else has occurred
setUp() Invoked once before each iteration of the method being benchmark.
classTearDown() Invoked at the end of the event after everything else has occurred.
tearDown() Invoked after each iteration of a benchmark has completed.

There are two levels of setup and tear down to prevent "state leakage" between benchmarks. For example, an object that caches calculations will perform faster on subsequent calls to the method.

If the goal is to benchmark the initial calculation, it makes sense to place the instantiation of the object in setUp().

If the goal, however, is to benchmark the entire process (initial calculation and subsequent caching), then it makes more sense to instantiate the object in classSetUp() so that it is only built once.

Calibration

Athletic uses Reflection and variable functions to invoke the methods in your Event. Because there is some internal overhead to variable functions, Athletic performs a "calibration" step before each iteration. This step calls an empty calibration method and times how long it takes. This time is then subtracted from the iterations total time, providing a more accurate total time.

Comments
  • [Feature request] tests comparison

    [Feature request] tests comparison

    Hi!

    I've been looking into benchmarking test suites to integrate into ProxyManager (see Ocramius/ProxyManager#62) and I think athletic is the best one I've found so far in terms of code organization.

    There's a couple of things that I'd like to ask as clarification and/or new feature (if possible).

    • Does athletic profile memory usage? I couldn't find anything related to that in method results
    • Would it be possible to group method results (with something like @group some-test and @baseLine, for example) and then group results and compare them Like jeremyFreeAgent/Clutch does? (note: Clutch is un-suited for this stuff because of the introduced xhprof overhead)

    I will try integrating athletic as it is right now, and eventually try to patch together a result printer, but I hope these features can eventually be put into discussion.

    I generally LOVE how athletic keeps tests organized, but Clutch is generally better in terms of details/tests comparison. I'd actually suggest integrating Clutch itself and open an issue there to ask if xhprof could be a problem.

    opened by Ocramius 17
  • Notices being catched and stopping execution

    Notices being catched and stopping execution

    Right now, the error handler registered by athletic stops execution on warnings/notices (which in my case don't depend on my codebase). This should probably be removed.

    For a reference, see Ocramius/GeneratedHydrator#14 and related travis builds

    opened by Ocramius 3
  • Add box.json, support for generating PHAR file

    Add box.json, support for generating PHAR file

    This PR adds a box.json file, that can be used with kherge/Box to generate a phar file for Athletic.

    Instructions:

    1. Install kherge/Box
    2. Assuming box.phar is somewhere in your $PATH, run:
    $ cd athletic/
    $ box.phar build
    
    1. athletic.phar will be generated and placed in the Athletic root dir. This file was also added to .gitignore

    Cheers!

    opened by filp 3
  • Simpler installation instructions

    Simpler installation instructions

    Composer is now very mainstream, the installation instructions can be made shorter.

    There's also now a feature so that Composer automatically selects the appropriate version, that means a simpler command.

    opened by mnapoli 2
  • Fix output formatting

    Fix output formatting

    The output of the default formatter is slightly off, as even the expected data in tests already show. I made sure that the columns are resized according to the largest value. I also changed the alignment of the number of iterations so that the tens are stacked in one row of the output.

    bug enhancement 
    opened by UlrichEckhardt 2
  • Specify single event via command line

    Specify single event via command line

    This change allows providing the path to a single event:

    php ./vendor/bin/athletic -p /home/ProjectDir/benchmarks/MyEvent.php
    

    This is especially useful when optimizing a specific part of the code, as it avoids to execute all events in a directory.

    opened by Matthimatiker 2
  • `setUp` is only called before an entire chunk of iterations

    `setUp` is only called before an entire chunk of iterations

    I've just found out that setUp is called only before the iterations start. Here's the pseudo-code of the currently implemented logic:

    $this->setUp();
    
    for($i = 0; $i < $iterations; $i += 1) {
        $this->benchmarkMethod();
    }
    

    This is the correct logic in my opinion:

    for($i = 0; $i < $iterations; $i += 1) {
        $this->setUp();
        $this->benchmarkMethod();
    }
    
    opened by Ocramius 2
  • Add error handling facilities

    Add error handling facilities

    This PR introduces some base work for error handling facilities within Athletic, with basic support for pretty-printing errors using Commando\Command::error (default behavior). It's a bit of an opinionated PR, and may very well not fall within your plans for Athletic, so feel free to refuse or request changes at will, I will completely understand :smile:

    opened by filp 2
  • AthleticEvent::pause() and ::resume()

    AthleticEvent::pause() and ::resume()

    Benchmarks can be paused within the benchmarking methods by calling $this->pause(). Execution time of code following a pause call will not affect the benchmark result. Calling $this->resume() will resume the benchmark.

    opened by DanweDE 1
  • Avoid division by zero in MethodResults

    Avoid division by zero in MethodResults

    This change avoids a division by zero in the MethodResults class.

    The problem occurs when the duration of an event is zero, which might be the case if the iterations run really fast and/or the clock is not precise enough. I noticed this behavior sometimes during execution of the \Athletic\AthleticEventTest::testCorrectRunsCount() test.

    opened by Matthimatiker 1
  • Accept Custom Formatter Classes

    Accept Custom Formatter Classes

    This pull request allows one to specify the formatter class, which is used by Athletic, via command line.

    If a class name is specified as formatter argument, then this class is used to format the benchmark results:

    $ php ./vendor/bin/athletic --formatter "My\Custom\FormatterClass" --path /home/ProjectDir/benchmarks/ --bootstrap /home/ProjectDir/vendor/autoload.php 
    

    Classes that are used as formatter must implement Athletic\Formatters\FormatterInterface and provide a constructor without parameters.

    The previous behavior of the formatter argument is preserved, which means that it is still possible to pass short names for built-in formatters.

    opened by Matthimatiker 0
  • RFC - Improving Pull Request Process via automated PHPCS checks

    RFC - Improving Pull Request Process via automated PHPCS checks

    I run http://stickler-ci.com which is a service aimed at improving code quality by simplifying code review by automating code style feedback in pull requests. As a fellow open source maintainer I found I was spending a significant amount of time giving feedback to contributors on how code should be formatted, and thought there had to be a better way.

    Sticker-CI is my attempt at building a better way. By making code style errors as pull request comments and a build status it is easy for both maintainers and contributors to know when a pull request matches a project's style.

    I wanted to know if you were interested in trying out stickler-ci. If you do, I can submit a pull request with the configuration file, but a maintainer will need to enable webhooks by logging into https://stickler-ci.com and enabling the webhook.

    opened by markstory 0
  • Refactored to support dynamic/multiple benchmarks

    Refactored to support dynamic/multiple benchmarks

    Today I refactored some of this package to support:

    • multiple benchmarking classes, such as one for time and one for memory.
    • custom benchmarking classes. This lets you benchmark almost anything, from network latency to database IOPS.
    • show/hide specific columns from the result output.

    This required the refactor of these files:

    • AthleticEvent
    • DefaultFormatter
    • MethodResults
    • A new directory: Benchmarkers
      • BenchmarkerInterface
      • TimeBenchmarker
      • MemoryBenchmarker

    See my commits here: https://github.com/oytuntez/athletic/commits/master.

    I didn't issue a PR as couldn't know how to organize the changes into tags/branches.

    opened by oytuntez 0
  • Setup instructions are incomplete

    Setup instructions are incomplete

    Hi! I'm trying to evaluate athletic and I'm having issues getting even a simple example to run. The "sample event" from the readme is obviously not intended to be used as-is, so I

    • created a folder
    • composer init
    • composer require polyfractal/athletic
    • created a simple file SleepEvent.php with a class SleepEvent deriving from AthleticEvent

    I'm trying to run using "vendor/bin/athletic -p ./SleepingEvent.php", which fails with "ERROR: Class \SleepingEvent does not exist". I've tried various other combinations, but to no avail. The class name in the error message changes slightly, but that's all. I'm using PHP 5.6, in case that matters.

    opened by UlrichEckhardt 4
  • [POC} Added iterations and dataprovider

    [POC} Added iterations and dataprovider

    This is a quick and dirty PR that:

    • implements data providers
    • allows multiple iteration speciications
    • forces to prefix the method with perform.

    If it is interesting I could break it up into separate PRs and write it properly...

    For example:

        public function provideFullTreeTraversal()
        {
            return array(
                array(10, 100),
                array(20, 200),
            );
        }
    
        /**
         * @dataProvider provideFullTreeTraversal
         * @iterations 1
         * @iterations 10
         */
        public function performFullTreeTraversal($from, $to)
        {
            $rootNode = $this->getSession()->getRootNode();
            $this->iterateNode($rootNode);
        }
    

    Produces:

    PHPCR\Benchmark\Suites\Reading\TraversalEvent
        Method Name                        Iterations   Average Time     Ops/second
        ---------------------------------  ------------ ---------------- -------------
        FullTreeTraversal(from=10,to=100): [1         ] [0.0515341758728] [19.40460]
        FullTreeTraversal(from=10,to=100): [10        ] [0.0038705348969] [258.36222]
        FullTreeTraversal(from=20,to=200): [1         ] [0.0037219524384] [268.67619]
        FullTreeTraversal(from=20,to=200): [10        ] [0.0038553476334] [259.37998]
    
    opened by dantleech 4
  • Runtime Restriction of Benchmark Methods

    Runtime Restriction of Benchmark Methods

    This change introduces a new (optional) annotation @maxRuntime, which allows one to restrict the runtime of a benchmark method to a specified number of seconds.

    This is useful, if a benchmark is executed on different hardware or if a generic event is used to benchmark several implementations with really different performance characteristics. In these cases, you might want to stop a benchmark early instead of waiting for hours until the results of a slow system are available.

    In the following example, the benchmark method slowIndexingAlgo() is stopped once 10000 iterations are reached or if the runtime of 5 minutes (300 seconds) is exceeded:

        /**
         * @iterations 10000
         * @maxRuntime 300
         */
        public function slowIndexingAlgo()
        {
            $this->slow->index($this->data);
        }
    
    opened by Matthimatiker 3
Owner
Zachary Tong
Zachary Tong
PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly frontend application for the raw metrics stream measured by PHP Depend.

PHPMD PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly

PHP Mess Detector 2.1k Jan 8, 2023
A PHP parser written in PHP

PHP Parser This is a PHP 5.2 to PHP 8.0 parser written in PHP. Its purpose is to simplify static code analysis and manipulation. Documentation for ver

Nikita Popov 15.9k Jan 3, 2023
A PHP VM implementation in PHP

PHPPHP A PHP VM implementation written in PHP. This is a basic VM implemented in PHP using the AST generating parser developed by @nikic To see what's

Anthony Ferrara 801 Dec 25, 2022
PHP Architecture Tester - Easy to use architectural testing tool for PHP :heavy_check_mark:

Easy to use architecture testing tool for PHP Introduction ?? PHP Architecture Tester is a static analysis tool to verify architectural requirements.

Carlos A Sastre 765 Dec 30, 2022
Provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths

sebastian/environment This component provides functionality that helps writing PHP code that has runtime-specific (PHP / HHVM) execution paths. Instal

Sebastian Bergmann 6.5k Jan 3, 2023
Search PHP source code for function & method calls, variables, and more from PHP.

Searching PHP source code made easy Search PHP source code for function & method calls, variable assignments, classes and more directly from PHP. Inst

Permafrost Software 22 Nov 24, 2022
A full-scale PHP sandbox class that utilizes PHP-Parser to prevent sandboxed code from running unsafe code

A full-scale PHP 7.4+ sandbox class that utilizes PHP-Parser to prevent sandboxed code from running unsafe code. It also utilizes FunctionParser to di

Corveda 192 Dec 10, 2022
A tool to automatically fix PHP Coding Standards issues

PHP Coding Standards Fixer The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards; whether you want to follow PHP codi

null 11.6k Jan 3, 2023
PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.

About PHP_CodeSniffer is a set of two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS files to detect violations of a define

Squiz Labs 9.9k Jan 4, 2023
PHP Static Analysis Tool - discover bugs in your code without running it!

PHPStan - PHP Static Analysis Tool PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even b

PHPStan 11.6k Dec 30, 2022
Phan is a static analyzer for PHP. Phan prefers to avoid false-positives and attempts to prove incorrectness rather than correctness.

Phan is a static analyzer for PHP that prefers to minimize false-positives. Phan attempts to prove incorrectness rather than correctness. Phan looks f

null 5.4k Jan 7, 2023
A PHP code-quality tool

GrumPHP Sick and tired of defending code quality over and over again? GrumPHP will do it for you! This composer plugin will register some git hooks in

PHPro 3.9k Jan 1, 2023
Beautiful and understandable static analysis tool for PHP

PhpMetrics PhpMetrics provides metrics about PHP project and classes, with beautiful and readable HTML report. Documentation | Twitter | Contributing

PhpMetrics 2.3k Dec 22, 2022
A tool for quickly measuring the size of a PHP project.

PHPLOC phploc is a tool for quickly measuring the size and analyzing the structure of a PHP project. Installation This tool is distributed as a PHP Ar

Sebastian Bergmann 2.3k Jan 4, 2023
Copy/Paste Detector (CPD) for PHP code.

PHP Copy/Paste Detector (PHPCPD) phpcpd is a Copy/Paste Detector (CPD) for PHP code. Installation This tool is distributed as a PHP Archive (PHAR): $

Sebastian Bergmann 2.2k Jan 1, 2023
:crystal_ball: Better Reflection is a reflection API that aims to improve and provide more features than PHP's built-in reflection API.

Better Reflection Better Reflection is a reflection API that aims to improve and provide more features than PHP's built-in reflection API. Why is it b

Roave, LLC 1.1k Dec 15, 2022
A command line refactoring tool for PHP

PHP Refactoring Browser Note: This software is under development and in alpha state. Refactorings do not contain all necessary pre-conditions and migh

QafooLabs 562 Dec 30, 2022
Micro PHP benchmark library

Ubench Ubench is a PHP micro library for benchmark Installation Note: If you are looking for this package for laravel application then install it from

Jeremy Perret 554 Nov 25, 2022
Analyze PHP code with one command

PHPQA Analyze PHP code with one command. Requirements PHP >= 5.4.0 xsl extension for HTML reports Why? Every analyzer has different arguments and opti

edgedesign/phpqa 542 Dec 24, 2022