Seb35 has submitted this change and it was merged.
Change subject: Added a performance test
......................................................................
Added a performance test
Code:
* Changed private properties/methods to protected
* Optimised a bit arrayMerge
Bugs:
* Fixed a bug in the declaration of a hook in MW < 1.25
Performance tests:
* Added a subdirectory tests/perfs
* Added a subclass MediaWikiFarmTestPerfs which inherits
MediaWikiFarm with some counters on some methods
* Derived index.php from www/index.php with a switch between
farm installation and classic installation to test performance
in both cases
* These web entry points are only accessible on localhost, and this
index.php must be called instead of www/index.php when testing perfs
* The main program testperfs.php is only accessible via CLI and is
responsible of calling X times the web entry point and of computing
resulting means
* To simulate the classic installation, a LocalSettings.php with exact
same parameters as the farm is created
Performance next steps:
* Given the strategy used in MediaWikiFarm for configuration (loading
global settings, then global arrays, then wfLoadSkin, then require_once)
is proven to be slower than LocalSettings.php (90 to 110µs added, out
of the 239µs added by MediaWikiFarm), the next step is to create a
LocalSettings.php in cache directory -- reusing code written here.
Change-Id: I74737330cb808d5a2e6897babe0cab94a81a886d
---
M .gitignore
M src/MediaWikiFarm.php
A tests/perfs/MediaWikiFarmTestPerfs.php
A tests/perfs/index.php
A tests/perfs/main.php
A tests/perfs/perfs.php
6 files changed, 391 insertions(+), 17 deletions(-)
Approvals:
Seb35: Verified; Looks good to me, approved
diff --git a/.gitignore b/.gitignore
index be65261..67b3c9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@
/tests/phpunit/data/config/varwikiversions.php
/tests/phpunit/data/config/deployments.php
/tests/phpunit/data/config/testdeploymentsfarmversions.php
+/tests/perfs/results
diff --git a/src/MediaWikiFarm.php b/src/MediaWikiFarm.php
index 7f2223d..7c12a71 100644
--- a/src/MediaWikiFarm.php
+++ b/src/MediaWikiFarm.php
@@ -37,25 +37,25 @@
* ---------- */
/** @var string Entry point script. */
- private $entryPoint = '';
+ protected $entryPoint = '';
/** @var string Farm code directory. */
- private $farmDir = '';
+ protected $farmDir = '';
/** @var string Farm configuration directory. */
- private $configDir = '';
+ protected $configDir = '';
/** @var string|null MediaWiki code directory, where each subdirectory
is a MediaWiki installation. */
- private $codeDir = null;
+ protected $codeDir = null;
/** @var string|false MediaWiki cache directory. */
- private $cacheDir = '/tmp/mw-cache';
+ protected $cacheDir = '/tmp/mw-cache';
/** @var array Configuration for this farm. */
- private $farmConfig = array();
+ protected $farmConfig = array();
/** @var string[] Variables related to the current request. */
- private $variables = array(
+ protected $variables = array(
'$SERVER' => '',
'$SUFFIX' => '',
'$WIKIID' => '',
@@ -64,7 +64,7 @@
);
/** @var array Configuration parameters for this wiki. */
- private $configuration = array(
+ protected $configuration = array(
'general' => array(),
'settings' => array(),
'arrays' => array(),
@@ -74,7 +74,7 @@
);
/** @var array Errors */
- private $errors = array();
+ protected $errors = array();
@@ -219,7 +219,7 @@
try {
# Initialise object
- $wgMediaWikiFarm = new self( $host,
$wgMediaWikiFarmConfigDir, $wgMediaWikiFarmCodeDir, $wgMediaWikiFarmCacheDir,
$entryPoint );
+ $wgMediaWikiFarm = new static( $host,
$wgMediaWikiFarmConfigDir, $wgMediaWikiFarmCodeDir, $wgMediaWikiFarmCacheDir,
$entryPoint );
# Check existence
$exists = $wgMediaWikiFarm->checkExistence();
@@ -395,7 +395,7 @@
$GLOBALS['wgAutoloadClasses']['MediaWikiFarm'] =
'src/MediaWikiFarm.php';
$GLOBALS['wgAutoloadClasses']['MWFConfigurationException'] =
'src/MediaWikiFarm.php';
$GLOBALS['wgMessagesDirs']['MediaWikiFarm'] = array(
'i18n' );
- $GLOBALS['wgHooks']['UnitTestsList'] = array(
'MediaWikiFarm::onUnitTestsList' );
+ $GLOBALS['wgHooks']['UnitTestsList'][] = array(
'MediaWikiFarm::onUnitTestsList' );
// @codeCoverageIgnoreEnd
}
@@ -792,7 +792,7 @@
* @param string $version The new version, should be the version found
in the 'expected version' file.
* @return void
*/
- private function updateVersion( $version ) {
+ protected function updateVersion( $version ) {
# Check a deployment file is wanted
if( !array_key_exists( '$DEPLOYMENTS', $this->variables ) )
@@ -1514,7 +1514,7 @@
* @param string|null $directory Name of the parent directory; null for
default cache directory
* @return void
*/
- private function cacheFile( $array, $filename, $directory = null ) {
+ protected function cacheFile( $array, $filename, $directory = null ) {
if( is_null( $directory ) )
$directory = $this->cacheDir;
@@ -1568,13 +1568,16 @@
*
* @return array
*/
- static function arrayMerge( /* ... */ ) {
- $out = array();
+ static function arrayMerge( $array1 /* ... */ ) {
+ $out = $array1;
+ if ( is_null( $out ) ) {
+ $out = array();
+ }
$argsCount = func_num_args();
- for ( $i = 0; $i < $argsCount; $i++ ) {
+ for ( $i = 1; $i < $argsCount; $i++ ) {
$array = func_get_arg( $i );
if ( is_null( $array ) ) {
- $array = array();
+ continue;
}
foreach ( $array as $key => $value ) {
if( array_key_exists( $key, $out ) &&
is_string( $key ) && is_array( $out[$key] ) && is_array( $value ) ) {
diff --git a/tests/perfs/MediaWikiFarmTestPerfs.php
b/tests/perfs/MediaWikiFarmTestPerfs.php
new file mode 100644
index 0000000..e89a7ba
--- /dev/null
+++ b/tests/perfs/MediaWikiFarmTestPerfs.php
@@ -0,0 +1,219 @@
+<?php
+
+if( $_SERVER['REMOTE_ADDR'] != '127.0.0.1' && $_SERVER['REMOTE_ADDR'] != '::1'
) {
+ exit;
+}
+
+class MediaWikiFarmTestPerfs extends MediaWikiFarm {
+
+ /** @var float Beginning of time count. */
+ protected static $time0 = array();
+
+ /** @var float Resulting counters. */
+ protected static $counters = array();
+
+ /** @var float Entry point (bis). */
+ protected static $entryPoint2 = '';
+
+ /** @var float Entry point (bis). */
+ protected static $profile = 0;
+
+ /**
+ * To do A/B test with and without farm, this returns 0 or 1.
+ *
+ * It should return alternatively 0 and 1, the value is stored in a
file.
+ *
+ * @param string $entryPoint The entry point we want the profile, e.g.
'index.php'.
+ * @return int The profile, either 0 or 1.
+ */
+ static function getEntryPointProfile( $entryPoint ) {
+
+ if( !is_dir( dirname( __FILE__ ) . '/results' ) ) {
+ mkdir( dirname( __FILE__ ) . '/results' );
+ }
+ if( !is_file( dirname( __FILE__ ) .
"/results/profile-$entryPoint.php" ) ) {
+ file_put_contents( dirname( __FILE__ ) .
"/results/profile-$entryPoint.php", "<?php return 0;\n" );
+ }
+
+ self::$entryPoint2 = $entryPoint;
+ self::$profile = include dirname( __FILE__ ) .
"/results/profile-$entryPoint.php";
+
+ $profile = (self::$profile+1)%2;
+ file_put_contents( dirname( __FILE__ ) .
"/results/profile-$entryPoint.php", "<?php return $profile;\n" );
+
+ return self::$profile;
+ }
+
+ /**
+ * Start the counter.
+ *
+ * @param string $name Name of the counter.
+ * @return void.
+ */
+ static function startCounter( $name ) {
+
+ self::$time0[$name] = microtime( true );
+ }
+
+ /**
+ * Stop the counter.
+ *
+ * @param string $name Name of the counter.
+ * @return void.
+ */
+ static function stopCounter( $name ) {
+
+ $time = microtime( true );
+ self::$counters[$name] = $time - self::$time0[$name];
+ }
+
+ /**
+ * Write down results and select the next profile.
+ *
+ * @return void.
+ */
+ static function writeResults() {
+
+ global $IP;
+
+ $entryPoint = self::$entryPoint2;
+
+ if( !is_file( dirname( __FILE__ ) .
"/results/measures-$entryPoint.php" ) ) {
+ file_put_contents( dirname( __FILE__ ) .
"/results/measures-$entryPoint.php", "<?php return array( 0 => array(), 1 =>
array() );\n" );
+ }
+
+ # Load existing state
+ $profile = self::$profile;
+ $measures = include dirname( __FILE__ ) .
"/results/measures-$entryPoint.php";
+
+ # Update with current measure
+ $measures[$profile][] = self::$counters;
+
+ # Write results
+ file_put_contents( dirname( __FILE__ ) .
"/results/measures-$entryPoint.php", '<?php return ' . var_export( $measures,
true ) . ";\n" );
+
+ if( !is_file( dirname( __FILE__ ) . '/results/metadata.php' )
&& $profile == 0 ) {
+ $server = $GLOBALS['wgMediaWikiFarm']->getVariable(
'$SERVER' );
+ file_put_contents( dirname( __FILE__ ) .
'/results/metadata.php', "<?php return array( 'IP' => '$IP', 'server' =>
'$server' );\n" );
+
+ $localSettings = "<?php\n";
+ $localSettings .= "\n# Start
counter\nMediaWikiFarmTestPerfs::startCounter( 'config' );\n";
+ $localSettings .= "\n# General settings\n";
+ foreach( $GLOBALS['wgMediaWikiFarm']->getConfiguration(
'settings' ) as $setting => $value ) {
+ $localSettings .= "\$$setting = " . var_export(
$value, true ) . ";\n";
+ }
+ foreach( $GLOBALS['wgMediaWikiFarm']->getConfiguration(
'arrays' ) as $setting => $value ) {
+ $localSettings .= self::writeArrayAssignment(
$value, "\$$setting" );
+ }
+ $localSettings .= "\n# Skins\n";
+ foreach( $GLOBALS['wgMediaWikiFarm']->getConfiguration(
'skins' ) as $skin => $loading ) {
+ if( $loading == 'wfLoadSkin' ) {
+ $localSettings .= "wfLoadSkin( '$skin'
);\n";
+ }
+ elseif( $loading == 'require_once' ) {
+ $localSettings .= "require_once
\"\$IP/skins/$skin/$skin.php\";\n";
+ }
+ }
+ $localSettings .= "\n# Extensions\n";
+ foreach( $GLOBALS['wgMediaWikiFarm']->getConfiguration(
'extensions' ) as $extension => $loading ) {
+ if( $loading == 'wfLoadExtension' ) {
+ $localSettings .= "wfLoadExtension(
'$extension' );\n";
+ }
+ elseif( $loading == 'require_once' ) {
+ $localSettings .= "require_once
\"\$IP/extensions/$extension/$extension.php\";\n";
+ }
+ }
+ $localSettings .= "\n# Included files\n";
+ foreach( $GLOBALS['wgMediaWikiFarm']->getConfiguration(
'execFiles' ) as $execFile ) {
+ $localSettings .= "include '$execFile';\n";
+ }
+ $localSettings .= "\n# Stop
counter\nMediaWikiFarmTestPerfs::stopCounter( 'config'
);\nMediaWikiFarmTestPerfs::writeResults();\n";
+ file_put_contents( dirname( __FILE__ ) .
'/results/LocalSettings.php', $localSettings );
+
+ #if( !is_file( $IP . '/LocalSettings.php' ) ) {
+ copy( dirname( __FILE__ ) .
'/results/LocalSettings.php', $IP . '/LocalSettings.php' );
+ #}
+ }
+ }
+
+ static function writeArrayAssignment( $array, $prefix ) {
+
+ $result = '';
+ $isList = (count( array_diff( array_keys( $array ), range( 0,
count( $array ) ) ) ) == 0);
+ foreach( $array as $key => $value ) {
+ $newkey = '[' . var_export( $key, true ) . ']';
+ if( $isList ) {
+ $result .= $prefix . '[] = ' . var_export(
$value, true ) . ";\n";
+ } elseif( is_array( $value ) ) {
+ $result .= self::writeArrayAssignment( $value,
$prefix . $newkey );
+ } else {
+ $result .= $prefix . $newkey . ' = ' .
var_export( $value, true ) . ";\n";
+ }
+ }
+
+ return $result;
+ }
+
+
+
+ /*
+ * Overloaded methods
+ * ------------------ */
+
+ /**
+ * Return the file where must be loaded the configuration from.
+ *
+ * This function returns a file which starts and stops a counter and
+ * launch the original file.
+ *
+ * @mediawikifarm-const
+ * @mediawikifarm-idempotent
+ *
+ * @return string File where is loaded the configuration.
+ */
+ function getConfigFile() {
+
+ return $this->farmDir . '/tests/perfs/main.php';
+ }
+
+ function loadMediaWikiConfig() {
+
+ MediaWikiFarmTestPerfs::startCounter( 'compilation' );
+
+ parent::loadMediaWikiConfig();
+
+ MediaWikiFarmTestPerfs::stopCounter( 'compilation' );
+ MediaWikiFarmTestPerfs::startCounter(
'loading-require_once-skins' );
+ }
+
+ function loadSkinsConfig() {
+
+ MediaWikiFarmTestPerfs::stopCounter(
'loading-require_once-skins' );
+ MediaWikiFarmTestPerfs::startCounter( 'loading-wfLoadSkins' );
+
+ parent::loadSkinsConfig();
+
+ MediaWikiFarmTestPerfs::stopCounter( 'loading-wfLoadSkins' );
+ MediaWikiFarmTestPerfs::startCounter(
'loading-require_once-extensions' );
+ }
+
+ function loadExtensionsConfig() {
+
+ MediaWikiFarmTestPerfs::stopCounter(
'loading-require_once-extensions' );
+ MediaWikiFarmTestPerfs::startCounter(
'loading-wfLoadExtensions' );
+
+ parent::loadExtensionsConfig();
+
+ MediaWikiFarmTestPerfs::stopCounter( 'loading-wfLoadExtensions'
);
+ MediaWikiFarmTestPerfs::startCounter( 'loading-execFiles' );
+ }
+
+ function getMediaWikiConfig() {
+
+ MediaWikiFarmTestPerfs::startCounter(
'loading-getMediaWikiConfig' );
+
+ parent::getMediaWikiConfig();
+
+ MediaWikiFarmTestPerfs::stopCounter(
'loading-getMediaWikiConfig' );
+ }
+}
diff --git a/tests/perfs/index.php b/tests/perfs/index.php
new file mode 100644
index 0000000..efce0e4
--- /dev/null
+++ b/tests/perfs/index.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Entry point index.php in the context of a multiversion MediaWiki farm.
+ *
+ * @author Sébastien Beyou ~ Seb35 <[email protected]>
+ * @license GPL-3.0+ GNU General Public License v3.0 ou version ultérieure
+ * @license AGPL-3.0+ GNU Affero General Public License v3.0 ou version
ultérieure
+ */
+// @codeCoverageIgnoreStart
+
+# Include library
+// @codingStandardsIgnoreStart MediaWiki.Usage.DirUsage.FunctionFound
+require_once dirname( dirname( dirname( __FILE__ ) ) ) .
'/src/MediaWikiFarm.php';
+require_once dirname( __FILE__ ) . '/MediaWikiFarmTestPerfs.php';
+// @codingStandardsIgnoreEnd
+
+if( $_SERVER['REMOTE_ADDR'] != '127.0.0.1' && $_SERVER['REMOTE_ADDR'] != '::1'
) {
+ exit;
+}
+
+switch( MediaWikiFarmTestPerfs::getEntryPointProfile( 'index.php' ) ) {
+
+ # Farm
+ case 0:
+
+ # Beginning of performance counter for the bootstrap part
+ MediaWikiFarmTestPerfs::startCounter( 'bootstrap' );
+
+ # Default MediaWikiFarm configuration
+ $wgMediaWikiFarmCodeDir = dirname( dirname( dirname( dirname(
__FILE__ ) ) ) );
+ $wgMediaWikiFarmConfigDir = '/etc/mediawiki';
+ $wgMediaWikiFarmCacheDir = '/tmp/mw-cache';
+
+ # Check the entry point is installed in a multiversion
MediaWiki farm or in the classical MediaWiki extensions directory
+ if( is_file( dirname( $wgMediaWikiFarmCodeDir ) .
'/includes/DefaultSettings.php' ) ) exit;
+
+ # Override default MediaWikiFarm configuration
+ @include_once dirname( dirname( dirname( __FILE__ ) ) ) .
'/config/MediaWikiFarmDirectories.php';
+
+ # Redirect to the requested version
+ if( MediaWikiFarmTestPerfs::load( 'index.php' ) == 200 ) {
+
+ # End of performance counter for the bootstrap part
+ MediaWikiFarmTestPerfs::stopCounter( 'bootstrap' );
+
+ require 'index.php';
+ }
+
+ break;
+
+ # Classical LocalSettings.php in a classical installation
+ case 1:
+
+ $wgMediaWikiFarmMetadata = include_once dirname( __FILE__ ) .
'/results/metadata.php';
+
+ chdir( $wgMediaWikiFarmMetadata['IP'] );
+ require 'index.php';
+}
+// @codeCoverageIgnoreEnd
diff --git a/tests/perfs/main.php b/tests/perfs/main.php
new file mode 100644
index 0000000..813c7f7
--- /dev/null
+++ b/tests/perfs/main.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Main program, creating the MediaWikiFarm object, then loading MediaWiki
configuration.
+ *
+ * @author Sébastien Beyou ~ Seb35 <[email protected]>
+ * @license GPL-3.0+ GNU General Public License v3.0 ou version ultérieure
+ * @license AGPL-3.0+ GNU Affero General Public License v3.0 ou version
ultérieure
+ */
+// @codeCoverageIgnoreStart
+
+if( $_SERVER['REMOTE_ADDR'] != '127.0.0.1' && $_SERVER['REMOTE_ADDR'] != '::1'
) {
+ exit;
+}
+
+MediaWikiFarmTestPerfs::startCounter( 'config' );
+
+require_once dirname( dirname( dirname( __FILE__ ) ) ) . '/src/main.php';
+
+MediaWikiFarmTestPerfs::stopCounter( 'loading-execFiles' );
+MediaWikiFarmTestPerfs::stopCounter( 'config' );
+MediaWikiFarmTestPerfs::writeResults();
+
+// @codeCoverageIgnoreEnd
diff --git a/tests/perfs/perfs.php b/tests/perfs/perfs.php
new file mode 100644
index 0000000..26ec182
--- /dev/null
+++ b/tests/perfs/perfs.php
@@ -0,0 +1,69 @@
+<?php
+
+if( PHP_SAPI != 'cli' && PHP_SAPI != 'phpdbg' ) {
+ exit;
+}
+
+$host = $argv[1];
+$sampleSize = count( $argv ) > 2 ? intval($argv[2]) : 100;
+$profiles = 2;
+
+if( is_file( dirname( __FILE__ ) . '/results/metadata.php' ) ) {
+ $deleted = @unlink( dirname( __FILE__ ) . '/results/metadata.php' );
+ if( !$deleted ) {
+ echo "Error: cannot delete previous measures.\n";
+ exit( 1 );
+ }
+}
+if( is_file( dirname( __FILE__ ) . '/results/LocalSettings.php' ) ) {
+ unlink( dirname( __FILE__ ) . '/results/LocalSettings.php' );
+}
+if( is_file( dirname( __FILE__ ) . '/results/measures-index.php.php' ) ) {
+ unlink( dirname( __FILE__ ) . '/results/measures-index.php.php' );
+}
+
+for( $i=0; $i<$sampleSize; $i++ ) {
+ if( $i > 0 && $i % 100 == 0 ) {
+ echo "\n";
+ }
+ for( $j=0; $j<$profiles; $j++ ) {
+ file_get_contents( $host );
+ usleep( rand( 3, 20 ) );
+ }
+ echo '.';
+}
+echo "\n";
+
+$statistics = include dirname( __FILE__ ) . '/results/measures-index.php.php';
+echo "sample size(farm) = " . count($statistics[0]) . "\n";
+echo "sample size(classical) = " . count($statistics[1]) . "\n";
+
+$mean = array();
+foreach( $statistics as $profile => $statisticsProfile ) {
+ $mean[$profile] = array();
+ foreach( $statisticsProfile as $unit ) {
+ foreach( $unit as $type => $value ) {
+ if( !array_key_exists( $type, $mean[$profile] ) ) {
+ $mean[$profile][$type] = 0;
+ }
+ $mean[$profile][$type] += $value;
+ }
+ }
+ foreach( $mean[$profile] as $type => $value ) {
+ $mean[$profile][$type] /= count( $statisticsProfile );
+ $mean[$profile][$type] *= 1000;
+ }
+}
+
+echo "bootstrap config total total config total\n";
+echo " farm farm classical farm difference difference\n";
+echo ' '.number_format($mean[0]['bootstrap'],3).' ';
+echo ' '.number_format($mean[0]['config'],3).' ';
+echo ' '.number_format($mean[1]['config'],3).' ';
+echo ' '.number_format($mean[0]['bootstrap']+$mean[0]['config'],3).' ';
+echo ' '.number_format($mean[0]['config']-$mean[1]['config'],3).' ';
+echo '
'.number_format($mean[0]['bootstrap']+$mean[0]['config']-$mean[1]['config'],3).'
';
+echo "\n\n";
+var_dump($mean);
+
+unlink( dirname( __FILE__ ) . '/results/profile-index.php.php' );
--
To view, visit https://gerrit.wikimedia.org/r/309548
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I74737330cb808d5a2e6897babe0cab94a81a886d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MediaWikiFarm
Gerrit-Branch: master
Gerrit-Owner: Seb35 <[email protected]>
Gerrit-Reviewer: Seb35 <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits