0

我有一個非常昂貴的方法removeColumns(...),此外還有多次調用。所以我想提高它的表現。爲了分析的優化結果我使用兩個工具:(1)Xdebug ProfilerWebgrind和(2)一個簡單的執行時間測量腳本(即一個PHPUnit的測試方法中的命令行上執行):簡單執行時間檢查的結果與Xdebug性能分析結果相矛盾

$timeStart = microtime(true); 
for ($i=0 ; $i < 1000000; $i++) { 
    // code to measure 
    $this->...->removeColumns($testArray, $columnNames, $isWhitelist); 
} 
$timeStop = microtime(true); 
$resultTime = $timeStop - $timeStart; 
$cycleTime = $resultTime/$i; 
echo number_format($cycleTime, 10, ',', '') . ' sec/run'; 
die(PHP_EOL . '###' . PHP_EOL); 

但現在我正在看結果 - 而且我看到,兩者的結果是完全相反的。

執行時間測量腳本的結果是:

variant  sec/run (x69)  sec/run (x1000)  sec/run (x10000) sec/run (x100000) 
1   0,0000121144  0,0000102139  0,0000092316  0,0000089004 
2   0,0000115650  0,0000112779  0,0000098540  0,0000098941 
3   0,0000228260  0,0000240171  0,0000250236  0,0000800230 

difference ms (1-2)  0,0000005494 -0,0000010640 -0,0000006224 -0,0000009937 
yield % (1-2)   4,54%   -10,42%   -6,74%   -11,16% 
difference ms (1-3)  -0,0000107116 -0,0000138032 -0,0000157920 -0,0000711226 
yield % (1-3)   -88,42%   -135,14%  -171,06%  -799,09% 

正如你所看到的,優化的失敗。當該方法不是經常被調用時,性能會變得更好,但呼叫越多,情況就越糟糕(非線性,在調用100.000時性能損失高達900%)。

現在,讓我們看到了Xdebug的探查的結果:

variant XDP-filename XDP-filesize Calls Total Self (ms) Total Inclusive (ms) 
1  1474536556  445,678 KB  69  77325   77403 
2  1474537523  402,208 KB  69  1267   1270 
3  1474539908  402,963 KB  69  2443   2455 

difference ms (1-2)        76058   76133 
yield % (1-2)         98,36%   98,36% 
difference ms (1-3)        74882   74948 
yield % (1-3)         96,84%   96,83% 

所以這裏的改良變(23)的性能/似乎顯著更好,比variant 1的性能。

這裏有什麼問題,以及如何解決它以獲得足夠的性能測試結果?


所有方法的三個變種,我優化:

變體1

public function removeColumns(array $table, array $columnNames, bool $isWhitelist = false) 
{ 
    foreach ($table as $rowKey => $row) { 
     if (is_array($row)) { 
      foreach ($row as $fieldName => $fieldValue) { 
       $remove = $isWhitelist 
        ? ! in_array($fieldName, $columnNames) 
        : in_array($fieldName, $columnNames) 
       ; 
       if ($remove) { 
        unset($table[$rowKey][$fieldName]); 
       } 
      } 
     } 
    } 
    return $table; 
} 

變種2

public function removeColumns(array $table, array $columnNames, bool $isWhitelist = false) 
{ 
    $tableKeys = array_keys($table); 
    $firstRowKey = $tableKeys[0]; 
    $firstRow = $table[$firstRowKey]; 
    $allColumnNames = array_keys($firstRow); 
    $resultColumns = []; 
    foreach ($allColumnNames as $columnName) { 
     $remain = $isWhitelist 
      ? in_array($columnName, $columnNames) 
      : ! in_array($columnName, $columnNames) 
     ; 
     if($remain) { 
      $resultColumns[$columnName] = array_column($table, $columnName); 
     } 
    } 
    $index = 0; 
    $resultTable = []; 
    foreach ($resultColumns as $resultColumnName => $resultColumn) { 
     foreach ($tableKeys as $index => $tableKey) { 
      $resultTable[$tableKey][$resultColumnName] = $resultColumn[$index]; 
     } 
    } 
    return $resultTable; 
} 

變種3

public function removeColumns(array $table, array $columnNames, bool $isWhitelist = false) 
{ 
    $tableKeys = array_keys($table); 
    $firstRowKey = $tableKeys[0]; 
    $firstRow = $table[$firstRowKey]; 
    $allColumnNames = array_keys($firstRow); 
    $columns = []; 
    $i = 0; 
    $arrayMapInputVarNames = []; 
    foreach ($allColumnNames as $columnName) { 
     $remain = 
      ($isWhitelist && in_array($columnName, $columnNames)) || 
      (! $isWhitelist && ! in_array($columnName, $columnNames)) 
     ; 
     if($remain) { 
      $varName = 'column' . $i++; 
      $$varName = $columns[$columnName] = array_column($table, $columnName); 
      $arrayMapInputVarNames[] = '$' . $varName; 
     } 
    } 
    $arrayMapInputString = implode(', ', $arrayMapInputVarNames); 
    eval('$rows = array_map(null, ' . $arrayMapInputString . ');'); 
    foreach ($rows as $index => $row) { 
     $rows[$index] = array_combine(array_keys($columns), array_values($row)); 
    } 
    $table = array_combine(array_keys($table), $rows); 
    return $table; 
} 

回答

0

這是你的功能打算做什麼?

<?php 
$table=array(
    'col1'=>'val1', 
    'col2'=>'val2', 
    'col3'=>'val3', 
    'col4'=>'val4' 
); 

$columnNames=array(
    'col2','col3' 
); 

function removeColumns($table, $columnNames, $isWhitelist = false) { 
    if ($isWhitelist) return array_intersect_key($table,array_flip($columnNames)); 
    return array_diff_key($table,array_flip($columnNames)); 
} 

print 'blacklist:'.var_export(removeColumns($table,$columnNames,false),1).PHP_EOL; 
print 'whitelist:'.var_export(removeColumns($table,$columnNames,true),1).PHP_EOL; 

輸出:

blacklist:array (
    'col1' => 'val1', 
    'col4' => 'val4', 
) 
whitelist:array (
    'col2' => 'val2', 
    'col3' => 'val3', 
) 

但我沒有測得的性能。你能上傳代碼的xdebug輸出嗎?