== Background
During a parse request [1] for a popular Wikipedia article, there
are 180,949 calls to MapCacheLRU::has and 188,542 calls to
::getCurrentTime via ::getAge. (Note, getCurrentTime has more calls
than ::has, as there are also calls via ::set and ::setField).
In a one-off XHGui profile, these calls had 246ms wall-time or 258ms
cpu-time attributed to them, which is 0.25s of a 12s parse or 2%.
In a day's worth of index.php samples [2] [3], there is 1.1% cpu-time
or 1.4% wall-time attributed to MapCacheLRU, of which ~0.3% in
getCurrentTime() alone.
== Change
Most uses of MapCacheLRU are without any in-process TTL, and thus have
$maxAge at its default of INF. Avoid calling getAge and getCurrentTime
in that case.
I think long-term it might make sense to deprecate the TTL feature
in MapCacheLRU, as it is very very rarely used, and still adds
significant overhead to all uses MapCacheLRU and its set() code path.
For cases where in-process TTLs are really needed, the HashBagOStuff
class could be used instead. I can't find any complex uses of
get/setFields, let alone with per-sub field TTL, but a theoretical
or third-party use case could potentially use HashBagOStuff with
sub-fields either as their own key, or refactor to put arrays under
a single key and TTL, or re-create it in their own code base if really
needed.
== Difference
I've added a benchmark since generating random values and populating
MapCacheLRU seems non-trivial enough to be worth a re-usable bench.
Using `benchmarkLruHash.php --method get --count 1000`, before:
> MapCacheLRU::get with 500 keys
> count: 100
> rate: 28.5/s
> mean: 35.06ms
... and after:
> count: 100
> rate: 35.6/s
> mean: 28.10ms
[1] POST en.wikipedia.org /w/api.php?action=parse&title=Barack_Obama&text={{:Barack_Obama}}
[2] https://performance.wikimedia.org/arclamp/svgs/daily/2021-09-06.excimer.index.reversed.svgz
[3] https://performance.wikimedia.org/arclamp/svgs/daily/2021-09-06.excimer-wall.index.reversed.svgz
Bug: T275673
Change-Id: Id8c747f0d0a9454274c296090a2ca80e440f97bb
125 lines
3.5 KiB
PHP
125 lines
3.5 KiB
PHP
<?php
|
|
/**
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
* @file
|
|
* @ingroup Benchmark
|
|
*/
|
|
|
|
require_once __DIR__ . '/../includes/Benchmarker.php';
|
|
|
|
/**
|
|
* Maintenance script that benchmarks HashBagOStuff and MapCacheLRU.
|
|
*
|
|
* @ingroup Benchmark
|
|
*/
|
|
class BenchmarkLruHash extends Benchmarker {
|
|
protected $defaultCount = 1000;
|
|
|
|
public function __construct() {
|
|
parent::__construct();
|
|
$this->addDescription( 'Benchmarks HashBagOStuff and MapCacheLRU.' );
|
|
$this->addOption( 'method', 'One of "construct" or "set". Default: [All]', false, true );
|
|
}
|
|
|
|
public function execute() {
|
|
$exampleKeys = [];
|
|
$max = 100;
|
|
$count = 500;
|
|
while ( $count-- ) {
|
|
$exampleKeys[] = wfRandomString();
|
|
}
|
|
// 1000 keys (1...500, 500...1)
|
|
$keys = array_merge( $exampleKeys, array_reverse( $exampleKeys ) );
|
|
|
|
$method = $this->getOption( 'method' );
|
|
$benches = [];
|
|
|
|
if ( !$method || $method === 'construct' ) {
|
|
$benches['HashBagOStuff::__construct'] = [
|
|
'function' => static function () use ( $max ) {
|
|
$obj = new HashBagOStuff( [ 'maxKeys' => $max ] );
|
|
},
|
|
];
|
|
$benches['MapCacheLRU::__construct'] = [
|
|
'function' => static function () use ( $max ) {
|
|
$obj = new MapCacheLRU( $max );
|
|
},
|
|
];
|
|
}
|
|
|
|
if ( !$method || $method === 'set' ) {
|
|
$hObj = null;
|
|
$benches['HashBagOStuff::set'] = [
|
|
'setup' => static function () use ( &$hObj, $max ) {
|
|
$hObj = new HashBagOStuff( [ 'maxKeys' => $max ] );
|
|
},
|
|
'function' => static function () use ( &$hObj, $keys ) {
|
|
foreach ( $keys as $i => $key ) {
|
|
$hObj->set( $key, $i );
|
|
}
|
|
}
|
|
];
|
|
$mObj = null;
|
|
$benches['MapCacheLRU::set'] = [
|
|
'setup' => static function () use ( &$mObj, $max ) {
|
|
$mObj = new MapCacheLRU( $max );
|
|
},
|
|
'function' => static function () use ( &$mObj, $keys ) {
|
|
foreach ( $keys as $i => $key ) {
|
|
$mObj->set( $key, $i );
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
if ( !$method || $method === 'get' ) {
|
|
$hObj = null;
|
|
$benches['HashBagOStuff::get'] = [
|
|
'setup' => static function () use ( &$hObj, $max, $keys ) {
|
|
$hObj = new HashBagOStuff( [ 'maxKeys' => $max ] );
|
|
foreach ( $keys as $i => $key ) {
|
|
$hObj->set( $key, $i );
|
|
}
|
|
},
|
|
'function' => static function () use ( &$hObj, $keys ) {
|
|
foreach ( $keys as $key ) {
|
|
$hObj->get( $key );
|
|
}
|
|
}
|
|
];
|
|
$mObj = null;
|
|
$benches['MapCacheLRU::get'] = [
|
|
'setup' => static function () use ( &$mObj, $max, $keys ) {
|
|
$mObj = new MapCacheLRU( $max );
|
|
foreach ( $keys as $i => $key ) {
|
|
$mObj->set( $key, $i );
|
|
}
|
|
},
|
|
'function' => static function () use ( &$mObj, $keys ) {
|
|
foreach ( $keys as $key ) {
|
|
$mObj->get( $key );
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
$this->bench( $benches );
|
|
}
|
|
}
|
|
|
|
$maintClass = BenchmarkLruHash::class;
|
|
require_once RUN_MAINTENANCE_IF_MAIN;
|