larger - PHP Phar creación lenta en una PC poderosa, cómo acelerar(cargando/leyendo ~ 3000 archivos)?
pear php (9)
¿Qué hay de usar clase en lugar de pasarle phar a func? solo un pedazo de código para entender ... o alguna vez escuché sobre el límite de memoria de php.ini u otra configuración que puede ralentizar las cosas.
class XY {
private $phar;
function addFile(SplFileInfo $file)
$root = realpath(__DIR__.DIRECTORY_SEPARATOR.''..''.DIRECTORY_SEPARATOR);
$path = strtr(str_replace($root, '''', $file->getRealPath()), ''//', ''/'');
$this->phar->addFromString($path, file_get_contents($file));
}
// other code here
}
Corrígeme si estoy equivocado, pero de esta manera, en lugar de pasar la función phar a la función, evitarás "copiar" el objeto. de esta manera es como un puntero.
Estoy intentando empacar mi aplicación web (proyecto Symfony 2) con Phar. He empaquetado satisfactoriamente Silex, un micro framework con cientos de archivos en un tiempo razonable (1-2 minutos).
El problema está en mi máquina de desarrollo (i7 4770k, 16GB, SSD Raid 0, proyecto en un disco RAM) crear el archivo es realmente lento, toma ~ 1 segundo para cada archivo . Realmente necesito encontrar una manera de acelerar las cosas.
La única iteración de lectura / carga del archivo es lenta. Estoy agregando archivos usando:
function addFile(Phar $phar, SplFileInfo $file)
{
$root = realpath(__DIR__.DIRECTORY_SEPARATOR.''..''.DIRECTORY_SEPARATOR);
$path = strtr(str_replace($root, '''', $file->getRealPath()), ''//', ''/'');
$phar->addFromString($path, file_get_contents($file));
}
$phar = new Phar(/* ... */);
$phar->startBuffering();
// ...
foreach ($files as $file) {
addFile($phar, $file);
}
// ...
$phar->setStub(/* ... */);
$phar->stopBuffering();
¿Cómo puedo acelerar la lectura / adición de archivos? Podría ser mi sistema operativo (Windows) el problema?
EDITAR : deshabilitar el almacenamiento en búfer no resolvió el problema. La misma velocidad de adición de cadenas:
// This is VERY fast (~ 1 sec to read all 3000+ files)
$strings = array();
foreach ($files as $file) {
$root = realpath(__DIR__.DIRECTORY_SEPARATOR.''..''.DIRECTORY_SEPARATOR);
$path = strtr(str_replace($root, '''', $file->getRealPath()), ''//', ''/'');
$strings[$path] = file_get_contents($file->getRealPath());
}
// This is SLOW
foreach ($strings as $local => $content) {
$phar->addFromString($local, $content);
}
EDITAR : app/build
script completa rápida y sucia (puede ayudar):
#!/usr/bin/env php
<?php
set_time_limit(0);
require __DIR__.''/../vendor/autoload.php'';
use Symfony/Component/Finder/Finder;
use Symfony/Component/Console/Input/ArgvInput;
$input = new ArgvInput();
$env = $input->getParameterOption(array(''--env'', ''-e''), ''dev'');
function addFile(Phar $phar, SplFileInfo $file)
{
$root = realpath(__DIR__.DIRECTORY_SEPARATOR.''..''.DIRECTORY_SEPARATOR);
$path = strtr(str_replace($root, '''', $file->getRealPath()), ''//', ''/'');
$phar->addFromString($path, file_get_contents($file));
}
$phar = new Phar(__DIR__ . "/../symfony.phar", 0, "symfony.phar");
$phar->startBuffering();
$envexclude = array_diff(array(''dev'', ''prod'', ''test''), array($env));
// App
$app = (new Finder())
->files()
->notPath(''/cache/'')
->notPath(''/logs/'')
->notName(''build'')
->notname(''/._(''.implode(''|'', $envexclude).'')/.yml$/'')
->in(__DIR__);
// Vendor
$vendor = (new Finder())
->files()
->ignoreVCS(true)
->name(''*.{php,twig,xlf,xsd,xml}'')
->notPath(''/tests/i'')
->notPath(''/docs/i'')
->in(__DIR__.''/../vendor'');
// Src
$src = (new Finder())
->files()
->notPath(''/tests/i'')
->in(__DIR__.''/../src'');
// Web
$web = (new Finder())
->files()
->in(__DIR__.''/../web'')
->notname(''/._(''.implode(''|'', $envexclude).'')/.php$/'');
$all = array_merge(
iterator_to_array($app),
iterator_to_array($src),
iterator_to_array($vendor),
iterator_to_array($web)
);
$c = count($all);
$i = 1;
$strings = array();
foreach ($all as $file) {
addFile($phar, $file);
echo "Done $i/$c/r/n";
$i++;
}
$stub = <<<''STUB''
Phar::webPhar(null, "/web/app_phar.php", null, array(), function ($path) {
return ''/web/app_phar.php''.$path;
});
__HALT_COMPILER();
STUB;
$phar->setStub($stub);
$phar->stopBuffering();
Intenta desactivar GC como lo hizo el compositor
gc_disable();
Intente utilizar Phar::addFile($file) lugar de Phar::addFromString(file_get_contents($file))
es decir
function addFile(Phar $phar, SplFileInfo $file)
{
$root = realpath(__DIR__.DIRECTORY_SEPARATOR.''..''.DIRECTORY_SEPARATOR);
$path = strtr(str_replace($root, '''', $file->getRealPath()), ''//', ''/'');
//$phar->addFromString($path, file_get_contents($file));
$phar->addFile($file,$path);
}
Sé que dijiste que agregar el nombre de archivo de la cadena no mejoraba el rendimiento, pero quizás una forma diferente de cargar los nombres de archivo puede mejorar el rendimiento junto con el uso del nombre de archivo de la cadena. Composer es bastante rápido, pero nunca lo cronometré. Intente cargar los archivos por grupo de tipos de archivos y agréguelos como grupos por separado.
Utiliza una clase de Symfony que puede que no desee o cambie.
use Symfony/Component/Finder/Finder;
$phar = new /Phar(/* ... */);
$phar->setSignatureAlgorithm(/Phar::SHA1);
$phar->startBuffering();
$finder = new Finder();
//add php files with filter
$finder->files()
->ignoreVCS(true)
->name(''*.php'')
->notName(''Compiler.php'')
->notName(''ClassLoader.php'')
->in(__DIR__.''/..'')
;
foreach ($finder as $file) {
$this->addFile($phar, $file);
}
$this->addFile($phar, new /SplFileInfo(/* ... */), false);
$finder = new Finder();
$finder->files()
->name(''*.json'')
->in(__DIR__ . ''/../../res'')
;
foreach ($finder as $file) {
$this->addFile($phar, $file, false);
}
$this->addFile($phar, new /SplFileInfo(/* ... */), false);
$phar->setStub($this->getStub());
$phar->stopBuffering();
Quizás pueda excluir un archivo de caché o registro utilizando los filtros de Finer si de alguna manera es un archivo grande que causa el retraso prolongado. Mire el enlace del compositor para obtener detalles completos sobre cómo se implementó.
Sugeriría cavar en las configuraciones de php.
Primera recomendación: es desactivar open_basedir si está habilitado. Como entiendo las internas de php, cuando intentas acceder a cualquier ubicación de archivo con php, es imprescindible que php compruebe si la ubicación del archivo coincide con el árbol de directorios permitido. Entonces, si hay muchos archivos, esta operación se realizará para cada archivo, y puede ralentizar significativamente el proceso. Por otro lado, si open_basedir está deshabilitado, la comprobación nunca termina.
http://www.php.net/manual/en/ini.core.php#ini.open-basedir
Segundo - es verificar realpath_cache_size y realpath_cache_ttl.
Como está escrito en php description
Determina el tamaño de la memoria caché realpath que utilizará PHP. Este valor se debe aumentar en los sistemas donde PHP abre muchos archivos, para reflejar la cantidad de operaciones de archivo realizadas.
http://www.php.net/manual/en/ini.core.php#ini.realpath-cache-size
Espero que esto te ayude a acelerar tus operaciones.
Sugiero usar Preloader para concatenar todos tus archivos en un solo archivo y luego simplemente agregar ese archivo individual al phar.
Ya sabes, me he dado cuenta de que Phar::buildFromDirectory
es bastante rápido.
$phar->buildFromDirectory(''./src/'', ''//.php$/'');
Pero necesitas escribir expresiones regulares más complicadas. Pero podría llamar a buildFromDirectory
varias veces con diferentes argumentos.
O cree una carpeta temporal y copie todos los archivos en $all
. Algo como esto
function myCopy($src, $dest)
{
@mkdir(dirname($dest), 0755, true);
copy($src, $dest);
}
foreach ($all as $file)
{
//$phar->addFile($file);
myCopy($file, ''./tmp/'' . $file);
}
$phar->buildFromDirectory(''./tmp/'');
puede crear subprocesos y reducir el tiempo total; por supuesto, Symfony debe admitir la carga simultánea. en realidad no es la mejor respuesta para su pregunta, pero puede disminuir significativamente el tiempo de carga total.
class Loader extends Thread {
public $phar;
public $file;
public $done;
public function run() {
$self->done = false;
addFile($self->phar, $self->file);
$self->done = true;
}
}
$threads = array();
foreach ($files as $file) {
$t = new Loader();
$t->phar = $phar;
$t->file = $file;
addFile($phar, $file);
$t->start();
$threads[] = $t;
}
while(true){
$finished = true;
foreach ($t as $threads) {
if ($t->done == false){
$finished = false;
sleep(1);
break;
}
}
if ($finished)
break;
}
y, crear 3000 hilos no es una buena idea. es posible que necesite crear una lógica de trabajo de hilos bien.
Phar::addFromString()
o Phar:addFromFile()
es increíblemente lento. Like @sectus dijo que Phar::buildFromDirectory()
es mucho más rápido. Pero como una alternativa fácil con pocos cambios en su código, podría usar Phar::buildFromIterator()
.
Ejemplo:
$all = $app->append($vendor)->append($src)->append($web);
$phar->buildFromIterator($all, dirname(__DIR__));
en lugar de:
$all = array_merge(
iterator_to_array($app),
iterator_to_array($src),
iterator_to_array($vendor),
iterator_to_array($web)
);
$c = count($all);
$i = 1;
$strings = array();
foreach ($all as $file) {
addFile($phar, $file);
echo "Done $i/$c/r/n";
$i++;
}
$ time app/build
real 0m4.459s
user 0m2.895s
sys 0m1.108s
Toma <5 segundos en mi máquina ubuntu bastante lenta.