简单执行时间检查的结果与Xdebug分析结果

时间:2016-09-22 13:17:11

标签: php performance performance-testing execution-time xdebug-profiler

我有一个非常昂贵的方法removeColumns(...),此外还会被多次调用。所以我想提高它的性能。为了分析优化结果,我使用了两个工具:(1)带有Xdebug ProfilerWebgrind和(2)一个简单的执行时间测量脚本(在PHPUnit测试方法中的命令行上执行):

$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%

如您所见,优化失败。如果不经常调用该方法,性能会变得更好,但调用越多,情况就越糟糕(非线性,900%调用时性能损失最多100.000

现在让我们看看Xdebug Profiler的结果:

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;
}

1 个答案:

答案 0 :(得分: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输出吗?