├── .State ├── .gitignore ├── bors.toml ├── .travis.yml ├── composer.json ├── Exception.php ├── Dtrace ├── Stat.d └── Execution.d ├── CHANGELOG.md ├── Test └── Unit │ └── Mark.php ├── README.md ├── Mark.php ├── Bench.php └── Documentation ├── En └── Index.xyl └── Fr └── Index.xyl /.State: -------------------------------------------------------------------------------- 1 | finalized 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /composer.lock 3 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push" 3 | ] 4 | timeout_sec = 1800 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | branches: 4 | only: 5 | - staging 6 | - trying 7 | - master 8 | 9 | matrix: 10 | include: 11 | - php: 5.5 12 | - php: 5.6 13 | - php: 7.0 14 | - php: 7.1 15 | env: 16 | - ENABLE_XDEBUG=true 17 | - php: 7.1 18 | env: 19 | - ENABLE_DEVTOOLS=true 20 | - php: nightly 21 | allow_failures: 22 | - php: nightly 23 | fast_finish: true 24 | 25 | os: 26 | - linux 27 | 28 | notifications: 29 | irc: "chat.freenode.net#hoaproject" 30 | 31 | sudo: false 32 | 33 | env: 34 | global: 35 | - secure: "AAAAB3NzaC1yc2EAAAADAQABAAAAgQCP/MRQTkDEQdlnhiVbW5dl3dSPBI8KO0EkCLRJ8mEOJ6gm9VH0yy2IyiBuGa+Oyj+cbdKkASN4B/nMvPS+POG9Qd+z9aSmgYZd1ZwVbmu1r0ag53qhQAiodLudzBpjS3RA0MJyX3IJu7HdMNo8qhx0M9WF+vGkcOAYqbsifakO8Q==" 36 | 37 | cache: 38 | directories: 39 | - vendor/ 40 | 41 | before_script: 42 | - export PATH="$PATH:$HOME/.composer/vendor/bin" 43 | - if [[ ! $ENABLE_XDEBUG ]]; then 44 | phpenv config-rm xdebug.ini || echo "ext-xdebug is not available, cannot remove it."; 45 | fi 46 | 47 | script: 48 | - composer install 49 | - vendor/bin/hoa test:run 50 | - if [[ $ENABLE_DEVTOOLS ]]; then 51 | composer global require friendsofphp/php-cs-fixer; 52 | vendor/bin/hoa devtools:cs --diff --dry-run .; 53 | fi 54 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "hoa/bench", 3 | "description": "The Hoa\\Bench library.", 4 | "type" : "library", 5 | "keywords" : ["library", "bench", "performance", "time", "dtrace", 6 | "trace", "analyze"], 7 | "homepage" : "https://hoa-project.net/", 8 | "license" : "BSD-3-Clause", 9 | "authors" : [ 10 | { 11 | "name" : "Ivan Enderlin", 12 | "email": "ivan.enderlin@hoa-project.net" 13 | }, 14 | { 15 | "name" : "Hoa community", 16 | "homepage": "https://hoa-project.net/" 17 | } 18 | ], 19 | "support": { 20 | "email" : "support@hoa-project.net", 21 | "irc" : "irc://chat.freenode.net/hoaproject", 22 | "forum" : "https://users.hoa-project.net/", 23 | "docs" : "https://central.hoa-project.net/Documentation/Library/Bench", 24 | "source": "https://central.hoa-project.net/Resource/Library/Bench" 25 | }, 26 | "require": { 27 | "hoa/consistency": "~1.0", 28 | "hoa/exception" : "~1.0", 29 | "hoa/iterator" : "~2.0" 30 | }, 31 | "require-dev": { 32 | "hoa/test" : "~2.0" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Hoa\\Bench\\": "." 37 | } 38 | }, 39 | "extra": { 40 | "branch-alias": { 41 | "dev-master": "3.x-dev" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Exception.php: -------------------------------------------------------------------------------- 1 | i = 0; 53 | } 54 | 55 | php*:::execute-entry 56 | { 57 | execute_start = timestamp; 58 | } 59 | 60 | php*:::function-entry 61 | /copyinstr(arg0) != ""/ 62 | { 63 | calls[self->i].name = strjoin( 64 | copyinstr(arg3), 65 | strjoin(copyinstr(arg4), copyinstr(arg0)) 66 | ); 67 | calls[self->i].time = timestamp; 68 | calls[self->i].depth = self->i; 69 | 70 | self->i++; 71 | } 72 | 73 | php*:::function-return 74 | /copyinstr(arg0) != ""/ 75 | { 76 | self->i--; 77 | 78 | @c[calls[self->i].name] = count(); 79 | @a[calls[self->i].name] = quantize( 80 | (timestamp - calls[self->i].time) / 1000 81 | ); 82 | } 83 | 84 | php*:::execute-return 85 | { 86 | execute_stop = timestamp; 87 | } 88 | 89 | END 90 | { 91 | printf("Count calls:\n"); 92 | printa(" • %-80s%@u\n", @c); 93 | 94 | printf("\nExecution distribution (values are in nanoseconds):\n"); 95 | printa(@a); 96 | 97 | printf("Total execution time: %dms", (execute_stop - execute_start) / 1000); 98 | 99 | exit(0); 100 | } 101 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 3.17.01.11 2 | 3 | * Quality: Happy new year! (Alexis von Glasow, 2017-01-09T21:37:32+01:00) 4 | 5 | # 3.16.10.24 6 | 7 | * Documentation: New `README.md` file. (Ivan Enderlin, 2016-10-17T20:34:40+02:00) 8 | * Documentation: Update `support` properties. (Ivan Enderlin, 2016-10-05T15:43:12+02:00) 9 | 10 | # 3.16.03.15 11 | 12 | * Update copyright. (Ivan Enderlin, 2016-01-17T14:12:35+01:00) 13 | * README: Simplify an example. (Ivan Enderlin, 2016-01-17T14:02:47+01:00) 14 | 15 | # 3.16.01.11 16 | 17 | * Quality: Drop PHP5.4. (Ivan Enderlin, 2016-01-11T09:15:26+01:00) 18 | * Quality: Run devtools:cs. (Ivan Enderlin, 2016-01-09T08:56:45+01:00) 19 | * Documentation: Use `hoa protocol:resolve`. (Ivan Enderlin, 2016-01-09T08:37:41+01:00) 20 | * Core: Remove `Hoa\Core`. (Ivan Enderlin, 2016-01-09T07:58:29+01:00) 21 | * Consistency: Use `Hoa\Consistency`. (Ivan Enderlin, 2015-12-08T10:51:08+01:00) 22 | * Exception: Use `Hoa\Exception`. (Ivan Enderlin, 2015-11-20T07:07:21+01:00) 23 | 24 | # 2.15.10.21 25 | 26 | * Documentation: Fix typo. (Raphaël Emourgeon, 2015-09-14T21:15:43+02:00) 27 | * Documentation: Introduce `pause` and `resume` methods. (Raphaël Emourgeon, 2015-09-14T19:09:10+02:00) 28 | * Documentation: Describe the `pause` & `resume` methods. (Ivan Enderlin, 2015-09-14T08:21:30+02:00) 29 | 30 | # 2.15.08.13 31 | 32 | * Update API documentation and re-order methods. (Ivan Enderlin, 2015-08-12T09:03:30+02:00) 33 | * The `resume` method is now static. (Ivan Enderlin, 2015-08-12T09:02:22+02:00) 34 | * Introduce the `pause` and `resume` methods. (Aymeric GERLIER, 2015-08-03T17:23:57+02:00) 35 | * Add a `.gitignore` file. (Stéphane HULARD, 2015-08-03T11:20:04+02:00) 36 | 37 | # 2.15.05.29 38 | 39 | * Fix English typography. (Ivan Enderlin, 2015-05-12T08:46:01+02:00) 40 | * Fix CS. (Ivan Enderlin, 2015-04-20T10:10:01+02:00) 41 | * Move the CHANGELOG to PSR-1 and PSR-2. (Ivan Enderlin, 2015-04-20T09:34:20+02:00) 42 | * Move documentation to PSR-1 and PSR-2. (Ivan Enderlin, 2015-04-20T09:31:12+02:00) 43 | * Move to PSR-1 and PSR-2. (Ivan Enderlin, 2015-04-20T09:23:09+02:00) 44 | 45 | # 2.15.04.13 46 | 47 | * Forget to translate a header text. (Ivan Enderlin, 2015-04-09T15:40:56+02:00) 48 | 49 | # 2.15.02.17 50 | 51 | * Add the CHANGELOG.md file. (Ivan Enderlin, 2015-02-17T09:20:07+01:00) 52 | * Happy new year! (Ivan Enderlin, 2015-01-05T14:18:01+01:00) 53 | 54 | # 2.14.12.10 55 | 56 | * Move to PSR-4. (Ivan Enderlin, 2014-12-09T13:34:12+01:00) 57 | 58 | # 2.14.11.21 59 | 60 | * Fix semi-colon missing (Alexis von Glasow, 2014-11-20T09:15:25+01:00) 61 | 62 | # 2.14.11.09 63 | 64 | * Use `hoa/iterator` ~1.0. (Ivan Enderlin, 2014-11-09T10:56:19+01:00) 65 | * Use `Hoa\Iterator`. (Ivan Enderlin, 2014-10-05T11:07:46+02:00) 66 | * Remove `from`/`import` and update to PHP5.4. (Ivan Enderlin, 2014-10-05T11:00:14+02:00) 67 | * Add links around `hoa://` in the documentation. (Ivan Enderlin, 2014-09-26T10:32:00+02:00) 68 | 69 | # 2.14.09.23 70 | 71 | * Add `branch-alias`. (Stéphane PY, 2014-09-23T11:56:19+02:00) 72 | 73 | # 2.14.09.17 74 | 75 | * Drop PHP5.3. (Ivan Enderlin, 2014-09-17T17:38:03+02:00) 76 | * Add the installation section. (Ivan Enderlin, 2014-09-17T17:37:54+02:00) 77 | 78 | (first snapshot) 79 | -------------------------------------------------------------------------------- /Test/Unit/Mark.php: -------------------------------------------------------------------------------- 1 | object( 56 | $mark = new CUT($id = uniqid()) 57 | )->string($mark->getId())->isEqualTo($id) 58 | ->boolean($mark->isRunning())->isFalse() 59 | ->boolean($mark->isPause())->isFalse() 60 | ->assert('Start mark') 61 | ->when($SUT = $mark->start()) 62 | ->object($SUT)->isInstanceOf('Hoa\Bench\Mark') 63 | ->boolean($mark->isRunning())->isTrue() 64 | ->boolean($mark->isPause())->isFalse() 65 | ->assert('Pause mark') 66 | ->when($SUT = $mark->pause()) 67 | ->object($SUT)->isInstanceOf('Hoa\Bench\Mark') 68 | ->boolean($mark->isRunning())->isTrue() 69 | ->boolean($mark->isPause())->isTrue() 70 | ->assert('Restart mark after pause') 71 | ->when($SUT = $mark->start()) 72 | ->object($SUT)->isInstanceOf('Hoa\Bench\Mark') 73 | ->boolean($mark->isRunning())->isTrue() 74 | ->boolean($mark->isPause())->isFalse() 75 | ->assert('Stop mark') 76 | ->when($SUT = $mark->stop()) 77 | ->object($SUT)->isInstanceOf('Hoa\Bench\Mark') 78 | ->boolean($mark->isRunning())->isFalse() 79 | ->boolean($mark->isPause())->isFalse() 80 | ; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Dtrace/Execution.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env dtrace -s 2 | 3 | /** 4 | * Hoa 5 | * 6 | * 7 | * @license 8 | * 9 | * New BSD License 10 | * 11 | * Copyright © 2007-2017, Hoa community. All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are met: 15 | * * Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * * Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * * Neither the name of the Hoa nor the names of its contributors may be 21 | * used to endorse or promote products derived from this software without 22 | * specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 | * POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | #pragma D option quiet 38 | 39 | BEGIN 40 | { 41 | } 42 | 43 | php*:::request-startup 44 | { 45 | printf("[38;5;0;48;5;226mRequest start[0m\n"); 46 | self->up = 0; 47 | self->depth = 0; 48 | } 49 | 50 | php*:::function-entry 51 | /self->up == 0/ 52 | { 53 | self->time_last = timestamp; 54 | } 55 | 56 | php*:::function-entry 57 | { 58 | self->depth += 2; 59 | printf( 60 | "[38;5;143m%6dms [38;5;94m%*s [38;5;74m%s[38;5;220m%s[38;5;106m%s[38;5;94m()\t\t\t[38;5;240m%s/[38;5;143m%s[38;5;240m:[38;5;143m%03d[0m\n", 61 | (timestamp - self->time_last) / 1000, 62 | self->depth, "➜", 63 | copyinstr(arg3), 64 | copyinstr(arg4), 65 | copyinstr(arg0), 66 | dirname(copyinstr(arg1)), 67 | basename(copyinstr(arg1)), 68 | arg2 69 | ); 70 | self->up = 1; 71 | self->time_last = timestamp; 72 | } 73 | 74 | php*:::exception-thrown 75 | /arg0 != NULL/ 76 | { 77 | printf( 78 | "[38;5;166m● %*s %s has been thrown[0m\n", 79 | self->depth, "", 80 | copyinstr(arg0) 81 | ); 82 | } 83 | 84 | php*:::exception-caught 85 | /arg0 != NULL/ 86 | { 87 | printf( 88 | "[38;5;106m✔ %*s %s has been caught[0m\n", 89 | self->depth, "", 90 | copyinstr(arg0) 91 | ); 92 | } 93 | 94 | php*:::error 95 | { 96 | printf( 97 | "[38;5;196m✖ %*s %s \t[38;5;240m%s/[38;5;143m%s[38;5;240m:[38;5;143m%03d[0m\n", 98 | self->depth, "", 99 | copyinstr(arg0), 100 | dirname(copyinstr(arg1)), 101 | basename(copyinstr(arg1)), 102 | arg2 103 | ); 104 | } 105 | 106 | php*:::function-return 107 | { 108 | printf( 109 | "[38;5;143m%6dms [38;5;94m%*s [38;5;74m%s[38;5;220m%s[38;5;106m%s[38;5;94m()[0m\n", 110 | (timestamp - self->time_last) / 1000, 111 | self->depth, "←", 112 | copyinstr(arg3), 113 | copyinstr(arg4), 114 | copyinstr(arg0) 115 | ); 116 | self->depth -= 2; 117 | } 118 | 119 | php*:::request-shutdown 120 | { 121 | printf("[38;5;0;48;5;226mRequest end[0m"); 122 | self->up = 0; 123 | } 124 | 125 | END 126 | { 127 | exit(0); 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
14 | Hoa is a modular, extensible and
15 | structured set of PHP libraries.
16 | Moreover, Hoa aims at being a bridge between industrial and research worlds.
17 |
Analyzing performances of algorithms or programs is a very
7 | common task, to which Hoa\Bench answers.
Hoa\Bench provides two ways to analyze an
16 | application: with PHP itself, or with
17 | DTrace, thanks to program written in
18 | the
19 | hoa://Library/Bench/Dtrace/
20 | directory.
The Hoa\Bench library has a very intuitive use: we only need
25 | to place mark in the program and that's all. Each time a mark
26 | is executed, it collects informations we will exploit.
There is two classes to know: Hoa\Bench\Bench which allows to
28 | group a set of marks, and Hoa\Bench\Mark which represents a
29 | single mark.
The most common usage of Hoa\Bench\Bench is to place some
34 | marks and print their statistics. Thus:
$bench = new Hoa\Bench\Bench();
36 |
37 | // Start two marks: “one” and “two”.
38 | $bench->one->start();
39 | $bench->two->start();
40 |
41 | usleep(50000);
42 |
43 | // Stop the mark “two” and start the mark “three”.
44 | $bench->two->stop();
45 | $bench->three->start();
46 |
47 | usleep(25000);
48 |
49 | // Stop all marks.
50 | $bench->three->stop();
51 | $bench->one->stop();
52 |
53 | // Print statistics.
54 | echo $bench;
55 |
56 | /**
57 | * Will output:
58 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 77ms, 100.0%
59 | * one |||||||||||||||||||||||||||||||||||||||||||||||||||| 77ms, 99.8%
60 | * two |||||||||||||||||||||||||||||||||| 51ms, 65.9%
61 | * three |||||||||||||||||| 26ms, 33.9%
62 | */
63 | Note, to declare a mark we simply call an attribute on the class
64 | Hoa\Bench\Bench, which will return a mark (or create if it does
65 | not exist). Once we have a mark, we can start it by calling the
66 | Hoa\Bench\Mark::start method. To stop it, we call the
67 | Hoa\Bench\Mark::stop method. Finally, printing the
68 | Hoa\Bench\Bench object will print the statistics of marks: their
69 | name, a little graph, their execution time and a percentage according to all
70 | marks.
Marks can be in three different states:
75 |Hoa\Bench\Mark::start method,Hoa\Bench\Mark::pause method, only if
78 | it was previously started,Hoa\Bench\Bench::stop method, only
80 | if it was previously started.If we start and stop a mark, and then start it again, it will be reset.
83 | Notwithstanding, it exists the Hoa\Bench\Mark::reset method that
84 | will have the same behavior while forcing the reset whatever the state of the
85 | mark. Thus:
$bench = new Hoa\Bench\Bench();
87 | $mark = $bench->aMark;
88 | $mark->start(); // = 0ms
89 | usleep(10000);
90 | $mark->pause(); // = 10ms
91 | usleep(10000);
92 | $mark->start(); // = 10ms
93 | usleep(10000);
94 | $mark->stop(); // = 20ms
95 | echo $bench;
96 |
97 | /**
98 | * Will output:
99 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 33ms, 100.0%
100 | * aMark |||||||||||||||||||||||||||||||||| 22ms, 66.3%
101 | */
102 |
103 | $mark->reset();
104 | $mark->start(); // = 0ms
105 | usleep(10000);
106 | $mark->stop(); // = 10ms
107 | echo $bench;
108 |
109 | /**
110 | * Will output:
111 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 45ms, 100.0%
112 | * aMark ||||||||||||| 11ms, 24.8%
113 | */
114 | We can also pause all marks and get which marks were started with the
115 | Hoa\Bench\Bench::pause method. The
116 | Hoa\Bench\Bench::resume method allows to restart a set of marks.
Other operations are available, such as:
118 |Hoa\Bench\Mark::diff
120 | method,Hoa\Bench\Mark::compareTo method,Hoa\Bench\Mark::isStarted method,Hoa\Bench\Mark::isPause method.We said that an object Hoa\Bench\Bench allows to group several
129 | marks together. We have seen how to declare a mark, but we can also check if a
130 | mark exists or remove it with PHP native constructions:
$bench = new Hoa\Bench\Bench();
132 |
133 | // Create a mark.
134 | $bench->foo;
135 |
136 | // Is “foo” exists?
137 | if (isset($bench->foo)) {
138 | // Then, delete it, for example.
139 | unset($bench->foo);
140 | }
141 | Obviously, we can remove all marks thanks to the
142 | Hoa\Bench\Bench::unsetAll method. In addition, the
143 | Hoa\Bench\Bench class behaves like an iterator on all marks,
144 | thus:
foreach ($bench as $id => $mark) {
146 | echo $id, ' => ', ($mark->isStarted() ? 'started' : 'stopped'), "\n";
147 | }
148 |
149 | /**
150 | * Could output:
151 | * one => stopped
152 | * two => started
153 | * three => started
154 | */
155 | We can also count the number of marks:
156 |var_dump(count($bench));
157 |
158 | /**
159 | * Will output:
160 | * int(3)
161 | */
162 | Finally, we can get statistics thanks to the
163 | Hoa\Bench\Bench::getStatistic or
164 | Hoa\Bench\Bench::getLongest methods, respectively to get
165 | statistics in an array corresponding to the output previously seen and to get
166 | the longest mark in term of execution time.
The Hoa\Bench\Bench class allows to filter
171 | marks during the computation of results (example with the
172 | Hoa\Bench\Bench::getStatistic or
173 | Hoa\Bench\Bench::__toString to draw the graph). A filter is a
174 | callable (for example a function) which receives three arguments: the
175 | identifier of the mark, its execution time
176 | in milliseconds and a percentage (according to the longest mark). A filter is
177 | also a predicate, it means that its result is a boolean: true to
178 | accept the mark, false to reject it.
For example, if we have a lot of marks and we would like to filter the 180 | “noise”, it means marks with a low execution time (let say, lower than 5%), we 181 | can use the following filter:
182 |$bench->filter(function ($id, $time, $pourcent) {
183 | return $pourcent > 5;
184 | });
185 | If we name our marks with a specific classification, we can filter
186 | according to the name of identifiers. For example, we would like all the marks
187 | that contain the sub-word _critical:
$bench->filter(function ($id, $time, $pourcent ) {
189 | return 0 !== preg_match('#_critical#', $id);
190 | });
191 | Obviously, we can add many filters.
192 |Notice that the Hoa\Bench\Bench::getStatistic method allows to
193 | not consider filters thanks to its sole argument
194 | $considerFilters, which is set to true by default.
195 | To get all the result, we have to set it to false.
The Hoa\Bench library also provides DTrace programs in the
200 | hoa://Library/Bench/Dtrace/
201 | directory. DTrace allows to generate traces in order to
202 | detect issues in realtime in a production environment. It is integrated into
203 | the kernel level and applicative level, which ensures excellent
204 | performances. DTrace uses
205 | the D language. We also recommend
206 | reading the DTraceBook. Attention, DTrace
207 | is not available on all platforms.
PHP is able to use DTrace if it has been compiled with the
209 | --enable-dtrace option (see
210 | DTraces probes for PHP).
We will start by analyzing the execution of the following
212 | Dtrace.php file:
<?php
214 |
215 | function f() { g(); h(); }
216 | function g() { h(); }
217 | function h() { }
218 |
219 | f();
220 | This file is very simple but it will be our test to execute the first
221 | DTrace program, namely
222 | hoa://Library/Bench/Dtrace/Execution.d.
223 | Attention, we have to execute DTrace as a super-user; thus:
$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Execution.d`
225 | $ sudo $exed -c "php Dtrace.php"
226 | Request start
227 | 2ms ➜ f() …/Dtrace.php:007
228 | 37ms ➜ g() …/Dtrace.php:003
229 | 26ms ➜ h() …/Dtrace.php:004
230 | 28ms ← h()
231 | 37ms ← g()
232 | 44ms ➜ h() …/Dtrace.php:003
233 | 25ms ← h()
234 | 30ms ← f()
235 | Request end
236 | Let's see what happens if we add errors and exceptions in
237 | Dtrace.php:
<?php
239 |
240 | function f() { ++$i; g(); h(); }
241 | function g() { h(); }
242 | function h() { throw new Exception('foo'); }
243 |
244 | try { f(); }
245 | catch (\Exception $e) { }
246 |
247 | And now, reexecute the DTrace program:
248 |$ sudo $exed -c "php Dtrace.php"
249 | Request start
250 | 2ms ➜ f() …/Dtrace.php:007
251 | ✖ Undefined variable: i …/Dtrace.php:003
252 | 37ms ➜ g() …/Dtrace.php:003
253 | 26ms ➜ h() …/Dtrace.php:004
254 | ● Exception has been thrown
255 | 28ms ← h()
256 | 37ms ← g()
257 | 44ms ➜ h() …/Dtrace.php:003
258 | 25ms ← h()
259 | 30ms ← f()
260 | ✔ Exception has been caught
261 | Request end
262 | The second DTrace program is
263 | hoa://Library/Bench/Dtrace/Stat.d
264 | and allows to collect some statistics on an execution such as the list of all
265 | called functions and methods with their respective invocations number and the
266 | distribution of the execution time. For example, if we try with our first
267 | version of Dtrace.php, we will have:
$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Stat.d`
269 | $ sudo $exed -c "php Dtrace.php"
270 | Count calls:
271 | • h 2
272 | • g 1
273 | • f 1
274 |
275 | Execution distribution (values are in nanoseconds):
276 |
277 | f
278 | value ------------- Distribution ------------- count
279 | 256 | 0
280 | 512 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
281 | 1024 | 0
282 |
283 | h
284 | value ------------- Distribution ------------- count
285 | 4 | 0
286 | 8 |@@@@@@@@@@@@@@@@@@@@ 1
287 | 16 | 0
288 | 32 | 0
289 | 64 | 0
290 | 128 | 0
291 | 256 |@@@@@@@@@@@@@@@@@@@@ 1
292 | 512 | 0
293 |
294 | g
295 | value ------------- Distribution ------------- count
296 | 128 | 0
297 | 256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
298 | 512 | 0
299 |
300 | Total execution time: 108ms
301 | This DTrace program is very useful to know what are the most invoked functions or 302 | methods in your program and to also know their average execution time. This 303 | program is a true and quick answer to the problem of detecting critical 304 | sections regarding performances!
305 | 306 |Hoa\Bench allows to analyze the execution of PHP programs
309 | thanks to PHP itself or DTrace. These tools are useful to improve
310 | performances by allowing to detect bottlenecks, too numerous
311 | calls, slow executions etc., in order to fix them. In short, all you need to
312 | get quality.
Analyser les performances de ses
7 | algorithmes ou de ses programmes est une tâche courante, à laquelle
8 | Hoa\Bench permet de répondre.
Hoa\Bench propose deux manières d'analyser
17 | son application : avec PHP lui-même, ou alors avec
18 | DTrace, grâce à des programmes présents
19 | dans le dossier
20 | hoa://Library/Bench/Dtrace/.
La bibliothèque Hoa\Bench fonctionne de manière très
25 | intuitive : il suffit de placer des marques dans notre
26 | programme et c'est tout. À chaque fois qu'une marque est exécutée, elle
27 | récolte des informations que nous pouvons exploiter par la suite.
Il y a deux classes à considérer : Hoa\Bench\Bench qui permet
29 | de regroupe un ensemble de marques, et Hoa\Bench\Mark qui
30 | représente une seule marque.
L'utilisation la plus courante de Hoa\Bench\Bench sera la
35 | suivante : placer quelques marques et afficher leurs statistiques. Ainsi :
$bench = new Hoa\Bench\Bench();
37 |
38 | // Start two marks: “one” and “two”.
39 | $bench->one->start();
40 | $bench->two->start();
41 |
42 | usleep(50000);
43 |
44 | // Stop the mark “two” and start the mark “three”.
45 | $bench->two->stop();
46 | $bench->three->start();
47 |
48 | usleep(25000);
49 |
50 | // Stop all marks.
51 | $bench->three->stop();
52 | $bench->one->stop();
53 |
54 | // Print statistics.
55 | echo $bench;
56 |
57 | /**
58 | * Will output:
59 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 77ms, 100.0%
60 | * one |||||||||||||||||||||||||||||||||||||||||||||||||||| 77ms, 99.8%
61 | * two |||||||||||||||||||||||||||||||||| 51ms, 65.9%
62 | * three |||||||||||||||||| 26ms, 33.9%
63 | */
64 | Nous remarquons que pour déclarer une marque, nous faisons simplement appel
65 | à un attribut sur la classe Hoa\Bench\Bench ce qui va retourner
66 | une marque (ou la créer avant si elle n'existe pas). Une fois que nous avons
67 | notre marque, nous pouvons la démarrer avec la méthode
68 | Hoa\Bench\Mark::start. Pour arrêter les marques, nous appelons la
69 | méthode Hoa\Bench\Mark::stop. Enfin, afficher l'objet
70 | Hoa\Bench\Bench va afficher les statistiques des marques : leur
71 | nom, un petit graphique, leur temps d'exécution et le pourcentage de ce temps
72 | par rapport aux autres marques.
Les marques peuvent être dans trois états différents :
78 |Hoa\Bench\Mark::start ;Hoa\Bench\Mark::pause,
81 | uniquement si elle était en marche précédemment ;Hoa\Bench\Mark::stop,
83 | uniquement si elle est en marche.Si nous démarrons puis arrêtons une marque, pour ensuite la démarrer à
86 | nouveau, elle sera remise à zéro. Toutefois, il existe la méthode
87 | Hoa\Bench\Mark::reset qui aura le même effet mais en forçant une
88 | remise à zéro quelque soit l'état de la marque. Ainsi :
$bench = new Hoa\Bench\Bench();
90 | $mark = $bench->aMark;
91 | $mark->start(); // = 0ms
92 | usleep(10000);
93 | $mark->pause(); // = 10ms
94 | usleep(10000);
95 | $mark->start(); // = 10ms
96 | usleep(10000);
97 | $mark->stop(); // = 20ms
98 | echo $bench;
99 |
100 | /**
101 | * Will output:
102 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 33ms, 100.0%
103 | * aMark |||||||||||||||||||||||||||||||||| 22ms, 66.3%
104 | */
105 |
106 | $mark->reset();
107 | $mark->start(); // = 0ms
108 | usleep(10000);
109 | $mark->stop(); // = 10ms
110 | echo $bench;
111 |
112 | /**
113 | * Will output:
114 | * __global__ |||||||||||||||||||||||||||||||||||||||||||||||||||| 45ms, 100.0%
115 | * aMark ||||||||||||| 11ms, 24.8%
116 | */
117 | Nous pouvons également mettre en pause toutes les marques et récupérer
118 | quelles marques étaient en cours d'exécution à ce moment grâce à la méthode
119 | Hoa\Bench\Bench::pause. La méthode
120 | Hoa\Bench\Bench::resume permet de redémarrer un ensemble de
121 | marques.
D'autres opérations sont possibles, comme :
123 |Hoa\Bench\Mark::diff ;Hoa\Bench\Mark::compareTo ;Hoa\Bench\Mark::isStarted ;Hoa\Bench\Mark::isPause.Nous avons précisé qu'un objet Hoa\Bench\Bench permettait de
134 | regrouper plusieurs marques ensemble. Nous avons vu comment déclarer une
135 | marque, mais nous avons d'autres opérations comme savoir si une marque existe
136 | ou alors la supprimer avec les constructions de PHP correspondantes :
$bench = new Hoa\Bench\Bench();
138 |
139 | // Create a mark.
140 | $bench->foo;
141 |
142 | // Is “foo” exists?
143 | if (isset($bench->foo)) {
144 | // Then, delete it, for example.
145 | unset($bench->foo);
146 | }
147 | Bien entendu, nous pouvons supprimer toutes les marques grâce à la méthode
148 | Hoa\Bench\Bench::unsetAll. De plus, la classe
149 | Hoa\Bench\Bench se comporte comme un itérateur sur toutes les
150 | marques, ainsi :
foreach ($bench as $id => $mark) {
152 | echo $id, ' => ', ($mark->isStarted() ? 'started' : 'stopped'), "\n";
153 | }
154 |
155 | /**
156 | * Could output:
157 | * one => stopped
158 | * two => started
159 | * three => started
160 | */
161 | Nous pouvons également compter le nombre de marques de la manière 162 | suivante :
163 |var_dump(count($bench));
164 |
165 | /**
166 | * Will output:
167 | * int(3)
168 | */
169 | Enfin, nous pouvons obtenir des statistiques grâce aux méthodes
170 | Hoa\Bench\Bench::getStatistic et
171 | Hoa\Bench\Bench::getLongest, respectivement pour obtenir les
172 | statistiques sous forme de tableau correspondant au graphique vu précédemment
173 | et pour obtenir la marque qui a la plus longue exécution.
La classe Hoa\Bench\Bench permet de filtrer
178 | les marques lors de calculs de résultats (par example avec la méthode
179 | Hoa\Bench\Bench::getStatistic ou
180 | Hoa\Bench\Bench::__toString pour dessiner le graphique). Un
181 | filtre est un callable (par exemple une fonction) qui
182 | reçoit trois arguments : l'identifiant de la marque, sa
183 | durée d'exécution en millisecondes et en pourcentage (par
184 | rapport à la marque la plus longue). Un filtre est également un prédicat,
185 | c'est à dire que son résultat est un booléen : true pour accepter
186 | la marque, false pour la rejeter.
Par exemple, si nous avons beaucoup de marques et que nous voulons filtrer 188 | le « bruit », c'est à dire les marques avec des petits temps d'exécutions 189 | (disons inférieur à 5%), nous pouvons utiliser le filtre suivant :
190 |$bench->filter(function ($id, $time, $pourcent) {
191 | return $pourcent > 5;
192 | });
193 | Si nous nommons nos marques avec une nomenclature particulière, nous
194 | pouvons filtrer à partir de leurs identifiants. Par exemple, nous voulons
195 | toutes les marques comportant le sous-mot _critical :
$bench->filter(function ($id, $time, $pourcent) {
197 | return 0 !== preg_match('#_critical#', $id);
198 | });
199 | Nous pouvons bien sûr ajouter plusieurs filtres.
200 |Notons que la méthode Hoa\Bench\Bench::getStatistic permet de
201 | ne pas considérer les filtres grâce à son seul argument
202 | $considerFilters, par défaut à true. Il suffit de le
203 | passer à false pour avoir tous les résultats.
La bibliothèque Hoa\Bench fournit également des programmes
208 | DTrace dans le dossier
209 | hoa://Library/Bench/Dtrace/. DTrace
210 | permet de générer des traces afin de détecter des problèmes
211 | en temps réel dans un environnement de production. Il est intégré au niveau du
212 | noyau et au niveau applicatif, ce qui lui assure d'excellentes
213 | performances. DTrace utilise
214 | le langage D. Nous conseillons
215 | également la lecture de DTraceBook.
216 | Attention, DTrace n'est pas disponible sur toutes les plateformes.
PHP peut utiliser DTrace s'il a été compilé avec l'option
218 | --enable-dtrace (voir
219 | DTraces probes for
220 | PHP).
Nous allons commencer par analyser l'exécution du fichier
222 | Dtrace.php suivant :
<?php
224 |
225 | function f() { g(); h(); }
226 | function g() { h(); }
227 | function h() { }
228 |
229 | f();
230 | Ce fichier est très simple mais il va nous servir de test pour exécuter le
231 | premier programme DTrace, à savoir
232 | hoa://Library/Bench/Dtrace/Execution.d.
233 | Attention, il faut exécuter DTrace en étant super-utilisateur ; ainsi :
$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Execution.d`
235 | $ sudo $exed -c "php Dtrace.php"
236 | Request start
237 | 2ms ➜ f() …/Dtrace.php:007
238 | 37ms ➜ g() …/Dtrace.php:003
239 | 26ms ➜ h() …/Dtrace.php:004
240 | 28ms ← h()
241 | 37ms ← g()
242 | 44ms ➜ h() …/Dtrace.php:003
243 | 25ms ← h()
244 | 30ms ← f()
245 | Request end
246 | Voyons si nous ajoutons des erreurs et des exceptions dans notre fichier
247 | Dtrace.php :
<?php
249 |
250 | function f() { ++$i; g(); h(); }
251 | function g() { h(); }
252 | function h() { throw new Exception('foo'); }
253 |
254 | try { f(); }
255 | catch (\Exception $e) { }
256 |
257 | Et réexécutons notre programme DTrace :
258 |$ sudo $exed -c "php Dtrace.php"
259 | Request start
260 | 2ms ➜ f() …/Dtrace.php:007
261 | ✖ Undefined variable: i …/Dtrace.php:003
262 | 37ms ➜ g() …/Dtrace.php:003
263 | 26ms ➜ h() …/Dtrace.php:004
264 | ● Exception has been thrown
265 | 28ms ← h()
266 | 37ms ← g()
267 | 44ms ➜ h() …/Dtrace.php:003
268 | 25ms ← h()
269 | 30ms ← f()
270 | ✔ Exception has been caught
271 | Request end
272 | Le second programme DTrace est
273 | hoa://Library/Bench/Dtrace/Stat.d
274 | et permet de collecter quelques statistiques sur l'exécution comme la liste de
275 | toutes les fonctions et méthodes appelées avec leur nombre d'appels et la
276 | distribution du temps d'exécution. Par exemple, si nous essayons sur notre
277 | première version de Dtrace.php, nous allons avoir :
$ exed=`hoa protocol:resolve hoa://Library/Bench/Dtrace/Stat.d`
279 | $ sudo $exed -c "php Dtrace.php"
280 | Count calls:
281 | • h 2
282 | • g 1
283 | • f 1
284 |
285 | Execution distribution (values are in nanoseconds):
286 |
287 | f
288 | value ------------- Distribution ------------- count
289 | 256 | 0
290 | 512 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
291 | 1024 | 0
292 |
293 | h
294 | value ------------- Distribution ------------- count
295 | 4 | 0
296 | 8 |@@@@@@@@@@@@@@@@@@@@ 1
297 | 16 | 0
298 | 32 | 0
299 | 64 | 0
300 | 128 | 0
301 | 256 |@@@@@@@@@@@@@@@@@@@@ 1
302 | 512 | 0
303 |
304 | g
305 | value ------------- Distribution ------------- count
306 | 128 | 0
307 | 256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
308 | 512 | 0
309 |
310 | Total execution time: 108ms
311 | Ce programme DTrace est très utile pour connaître quelles sont les 312 | fonctions ou méthodes les plus appelées dans votre programme et de connaître 313 | également leur temps d'exécution moyen. Autant dire que pour détecter les 314 | sections critiques d'un point de vue performance, ce programme apporte une 315 | véritable réponse et rapidement !
316 | 317 |Hoa\Bench permet d'analyser l'exécution de programmes PHP à
320 | l'aide de PHP lui-même ou de DTrace. Ces outils sont utiles pour améliorer les
321 | performances en permettant de détecter des goulots
322 | d'étranglements, des appels trop nombreux, des ralentissements etc., afin de
323 | les corriger. Bref, tout ce qu'il faut pour obtenir des programmes de
324 | qualité.