From c403d4838dc8662d62902ced3713c1d9be6309cc Mon Sep 17 00:00:00 2001 From: Fred Emmott Date: Tue, 9 Jun 2015 14:24:15 -0700 Subject: [PATCH] Add LCStore implementation that uses static arrays in PHP files Implementation written by Fred Emmott of Facebook. Quoting Fred: As well as array access being faster, the main advantage is actually that this significantly reduces the use of unserialize(), which does a lot of memcpys when making the strings. Benchmarks compared to LCStoreCDB: * HHVM (no repo-auth): ~7% improvement * HHVM (with repo-auth): ~12% improvement * PHP7: ~1% improvement My (Legoktm) brief testing noted that the generated PHP files were noticiably larger than the CDB ones: * 1.5M en.l10n.php * 932K l10n_cache-en.cdb Bug: T99740 Change-Id: Ib2c5856d40cd928cab4a79cb935b3ce08c598300 --- autoload.php | 1 + includes/DefaultSettings.php | 4 +- includes/cache/LCStoreStaticArray.php | 140 ++++++++++++++++++++++++++ includes/cache/LocalisationCache.php | 3 + 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 includes/cache/LCStoreStaticArray.php diff --git a/autoload.php b/autoload.php index a84fefd059..26d954a9d1 100644 --- a/autoload.php +++ b/autoload.php @@ -607,6 +607,7 @@ $wgAutoloadLocalClasses = array( 'LCStoreCDB' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStoreDB' => __DIR__ . '/includes/cache/LocalisationCache.php', 'LCStoreNull' => __DIR__ . '/includes/cache/LocalisationCache.php', + 'LCStoreStaticArray' => __DIR__ . '/includes/cache/LCStoreStaticArray.php', 'LangMemUsage' => __DIR__ . '/maintenance/language/langmemusage.php', 'Language' => __DIR__ . '/languages/Language.php', 'LanguageAr' => __DIR__ . '/languages/classes/LanguageAr.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 5c29ff8689..d278c59539 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -2297,11 +2297,13 @@ $wgAdaptiveMessageCache = false; * Localisation cache configuration. Associative array with keys: * class: The class to use. May be overridden by extensions. * - * store: The location to store cache data. May be 'files', 'db' or + * store: The location to store cache data. May be 'files', 'array', 'db' or * 'detect'. If set to "files", data will be in CDB files. If set * to "db", data will be stored to the database. If set to * "detect", files will be used if $wgCacheDirectory is set, * otherwise the database will be used. + * "array" is an experimental option that uses PHP files that + * store static arrays. * * storeClass: The class name for the underlying storage. If set to a class * name, it overrides the "store" setting. diff --git a/includes/cache/LCStoreStaticArray.php b/includes/cache/LCStoreStaticArray.php new file mode 100644 index 0000000000..862ed67fa4 --- /dev/null +++ b/includes/cache/LCStoreStaticArray.php @@ -0,0 +1,140 @@ +directory = $conf['directory']; + } else { + $this->directory = $wgCacheDirectory; + } + } + + public function startWrite( $code ) { + $this->currentLang = $code; + $this->fname = $this->directory. '/' . $code . '.l10n.php'; + $this->data[$code] = array(); + if ( file_exists( $this->fname ) ) { + $this->data[$code] = require $this->fname; + } + } + + public function set( $key, $value ) { + $this->data[$this->currentLang][$key] = self::encode( $value ); + } + + /** + * Encodes a value into an array format + * + * @param mixed $value + * @return array + * @throws RuntimeException + */ + public static function encode( $value ) { + if ( is_scalar( $value ) || $value === null ) { + // [V]alue + return array( 'v', $value ); + } + if ( is_object( $value ) ) { + // [S]erialized + return array( 's', serialize( $value ) ); + } + if ( is_array( $value ) ) { + // [A]rray + return array( 'a', array_map( function ( $v ) { + return LCStoreStaticArray::encode( $v ); + }, $data ) ); + } + + throw new RuntimeException( 'Cannot encode ' . var_export( $value, true ) ); + } + + /** + * Decode something that was encoded with encode + * + * @param array $encoded + * @return array|mixed + * @throws RuntimeException + */ + public static function decode( array $encoded ) { + $type = $encoded[0]; + $data = $encoded[1]; + + switch ( $type ) { + case 'v': + return $data; + case 's': + return unserialize( $data ); + case 'a': + return array_map( function ( $v ) { + return LCStoreStaticArray::decode( $v ); + }, $data ); + default: + throw new RuntimeException( + 'Unable to decode ' . var_export( $encoded, true ) ); + } + } + + public function finishWrite() { + file_put_contents( + $this->fname, + "data[$this->currentLang], true ) . ';' + ); + $this->currentLang = null; + $this->fname = null; + } + + public function get( $code, $key ) { + if ( !array_key_exists( $code, $this->data ) ) { + $fname = $this->directory. '/' . $code . '.l10n.php'; + if ( !file_exists( $fname ) ) { + return null; + } + $this->data[$code] = require $fname; + } + $data = $this->data[$code]; + if ( array_key_exists( $key, $data ) ) { + return self::decode( $data[$key] ); + } + return null; + } +} diff --git a/includes/cache/LocalisationCache.php b/includes/cache/LocalisationCache.php index dc5a2eb6bc..febde7a79c 100644 --- a/includes/cache/LocalisationCache.php +++ b/includes/cache/LocalisationCache.php @@ -204,6 +204,9 @@ class LocalisationCache { case 'db': $storeClass = 'LCStoreDB'; break; + case 'array': + $storeClass = 'LCStoreStaticArray'; + break; case 'detect': $storeClass = $wgCacheDirectory ? 'LCStoreCDB' : 'LCStoreDB'; break; -- 2.20.1