Skip to content

Latest commit

 

History

History
511 lines (329 loc) · 13 KB

File metadata and controls

511 lines (329 loc) · 13 KB

Annotributes

Configure your benchmarks with Annotations or, if you have PHP 8, Attributes. Note that some of these settings (e.g. :ref:`configuration_runner_revs`, :ref:`configuration_runner_iterations`) can be set globaly in the :doc:`configuration`.

When testing units of code where microsecond accuracy is important, it is necessary to increase the number of revolutions performed by the benchmark runner. The term "revolutions" (invented here) refers to the number of times the benchmark is executed consecutively within a single time measurement.

We can arrive at a more accurate measurement by determining the mean time from multiple revolutions (i.e. time / revolutions) than we could with a single revolution. In other words, more revolutions means more precision.

Revolutions can be specified using the @Revs annotation:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,revs,benchTime

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,revs,benchTime

You may also specify an array:

/**
 * @Revs({1, 8, 64, 4096})
 */
class HashBench
{
    // ...
}

Revolutions can also be overridden from the :ref:`command line <overriding_iterations_and_revolutions>`.

Iterations specify how many samples should be taken - i.e. how many times we run the :ref:`revolutions <metadata_revolutions>` and capture time and memory information (for example).

By looking at the separate time measurement of each iteration we can determine how stable the readings are. The less the measurements differ from each other, the more stable the benchmark.

Note

In a perfect environment the readings would all be exactly the same - but such an environment is unlikely to exist

Iterations can be specified using the @Iterations annotation:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,iterations,benchTime

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,iterations,benchTime

As with :ref:`revolutions <metadata_revolutions>`, you may also specify an array.

Iterations can also be overridden from the :ref:`command line <overriding_iterations_and_revolutions>`.

You can instruct PHPBench to continuously run the iterations until the deviation of each iteration fits within a given margin of error by using the --retry-threshold. See :ref:`metadata_retry_threshold` for more information.

Method hooks

Any number of methods can be executed both before and after each benchmark subject using the @BeforeMethods and @AfterMethods annotations. Before methods are useful for bootstrapping your environment:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,beforeMethods,afterMethods,benchTime

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,beforeMethods,afterMethods,benchTime

Multiple before and after methods can be specified.

Note

If before and after methods are used when the @ParamProviders annotations are used, then they will also be passed the parameters.

Class Hooks

Sometimes you will want to perform actions which establish an external state. For example, creating or populating a database, creating files, etc.

This can be achieved by creating static methods within your benchmark class and adding the @BeforeClassMethods and @AfterClassMethods:

These methods will be executed by the runner once per benchmark class.

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,beforeClassMethods,afterClassMethods,benchTime

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,beforeClassMethods,afterClassMethods,benchTime

Note

These methods are static and are executed in a process that is separate from that from which your iterations will be executed. Therefore state will not be carried over to your iterations!.

Parameter sets can be provided to benchmark subjects:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,paramProviders

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,paramProviders

The benchMd5 subject will now be benchmarked with each parameter set.

The param provider can return a set of parameters using any iterable. For example the above could also be returned as an array:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,paramIterable

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,paramIterable

Warning

It should be noted that Generators are consumed completely before the subject is executed. If you have a very large data set, it will be read completely into memory.

Multiple parameter providers can be used, in which case the data sets will be combined into a cartesian product - all possible combinations of the parameters will be generated:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,paramMultiple

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,paramMultiple

Will result in the following parameter benchmark scenarios:

// #0
['string' => 'Hello World!', 'algorithm' => 'md5'];

// #1
['string' => 'Goodbye Cruel World!', 'algorithm' => 'md5'[;

// #2
['string' => 'Hello World!', 'algorithm' => 'sha1'];

// #3
['string' => 'Goodbye Cruel World!', 'algorithm' => 'sha1'];

You can assign benchmark subjects to groups using the @Groups annotation.

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,groups

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,groups

The group can then be targeted using the command line interface.

You can skip subjects by using the @Skip annotation:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,skip

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,skip

Sometimes it may be necessary to pause between iterations in order to let the system recover. Use the @Sleep annotation, specifying the number of microseconds required:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,sleep

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,sleep

The above example will pause (sleep) for 1 millisecond after each iteration.

Note

This can be overridden using the --sleep option from the CLI.

Specify output time units using the @OutputTimeUnit annotation (precision is optional):

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,outputTimeUnit

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,outputTimeUnit

The following time units are available:

  • microseconds
  • milliseconds
  • seconds
  • minutes
  • hours
  • days

The output mode determines how the measurements are presented, either time or throughput. time mode is the default and shows the average execution time of a single :ref:`revolution <metadata_revolutions>`. throughput shows how many operations are executed within a single time unit:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,outputMode

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,outputMode

PHPBench will then render all measurements for benchTimeItself similar to 363,874.536ops/s.

Use the @Warmup annotation to execute any number of revolutions before actually measuring the revolutions time.

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,warmup

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,warmup

As with :ref:`revolutions <metadata_revolutions>`, you may also specify an array.

Use the @Timeout annotation to specify the maximum number of seconds before an iteration timesout and fails. The following example will fail after 0.1 seconds:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,timeout

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,timeout

You can annotate your benchmarks with assertions which will cause PHPBench to report failures and exit with a non-zero exit code if they fail.

For example, assert that the :ref:`KDE mode<expr_func_mode>` is less than 200 microseconds:

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,assert

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,assert

You can also specify assertions from the command line:

$ phpbench run --assert='mode(variant.time.avg) < 10 hours'

See :doc:`guides/assertions` for more information.

Override how the variant results are formatted in the progress output.

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,benchTime,format

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,benchTime,format

You can also specify assertions from the command line:

$ phpbench run --format='"This is my time: " ~ mode(variant.time.avg)'

See :doc:`expression` for details on using the expressio language.

Set the retry threshold (the deviation beyond which a sample will be considered invalid and retried).

Use to create more stable sets of iterations.

.. tabs::

    .. tab:: Annotations

        .. codeimport:: ../examples/Annotations/AnnotatedBench.php
          :language: php
          :sections: all,retrythreshold,benchTime

    .. tab:: Attributes

        .. codeimport:: ../examples/Attributes/AttributedBench.php
          :language: php
          :sections: all,retrythreshold,benchTime

Good values are generally 10 or less, the above threshold is 20 because the examples are executed in the continuous integration environment and may cause delays.