【问题标题】:Why Does This Perform Better?为什么这表现更好?
【发布时间】:2012-03-17 05:33:59
【问题描述】:

所以我正在尝试使用debug_backtrace 和PHP 反射在我的架构中实现面向方面的设计。该设计有效,但我决定看看它对性能的影响有多严重,因此我编写了以下分析测试。有趣的是,当AdvisableNonAdvisable 方法什么都不做时,其影响大约是使用可取方法与使用非可取方法的 5 倍,但是当我增加每种方法的复杂性时(这里通过将迭代次数增加到 30 次或更多),可取的方法开始表现得更好,并随着复杂性的增加而继续增加。

基类:

abstract class Advisable {

    private static $reflections = array();
    protected static $executions = 25;

    protected static function advise()
    {
        $backtrace = debug_backtrace();
        $method_trace = $backtrace[1];
        $object = $method_trace['object'];
        $function = $method_trace['function'];
        $args = $method_trace['args'];

        $class = get_called_class();

        // We'll introduce this later
        $before = array();
        $around = array();
        $after = array();

        $method_info = array(
            'args' => $args,
            'object' => $object,
            'class' => $class,
            'method' => $function,
            'around_queue' => $around
        );

        array_unshift($args, $method_info);
        foreach ($before as $advice)
        {
            call_user_func_array($advice, $args);
        }

        $result = self::get_advice($method_info);

        foreach ($after as $advice)
        {
            call_user_func_array($advice, $args);
        }

        return $result;
    }

    public static function get_advice($calling_info)
    {
        if ($calling_info['around_queue'])
        {
            $around = array_shift($calling_info['around_queue']);
            if ($around)
            {
                // a method exists in the queue
                return call_user_func_array($around, array_merge(array($calling_info), $calling_info['args']));
            }
        }
        $object = $calling_info['object'];
        $method = $calling_info['method'];
        $class = $calling_info['class'];

        if ($object)
        {
            return null; // THIS IS THE OFFENDING LINE
            // this is a class method
            if (isset(self::$reflections[$class][$method]))
            {
                $parent = self::$reflections[$class][$method];
            }
            else
            {
                $parent = new ReflectionMethod('_'.$class, $method);
                if (!isset(self::$reflections[$class]))
                {
                    self::$reflections[$class] = array();
                }
                self::$reflections[$class][$method] = $parent;
            }
            return $parent->invokeArgs($object, $calling_info['args']);
        }
        // this is a static method
        return call_user_func_array(get_parent_class($class).'::'.$method, $calling_info['args']);
    }
}

一个实现的类:

abstract class _A extends Advisable
{
    public function Advisable()
    {
        $doing_stuff = '';
        for ($i = 0; $i < self::$executions; $i++)
        {
            $doing_stuff .= '.';
        }
        return $doing_stuff;
    }

    public function NonAdvisable()
    {
        $doing_stuff = '';
        for ($i = 0; $i < self::$executions; $i++)
        {
            $doing_stuff .= '.';
        }
        return $doing_stuff;
    }
}

class A extends _A
{
    public function Advisable()
    {
        return self::advise();
    }
}

并分析方法:

$a = new A();
$start_time = microtime(true);
$executions = 1000000;
for ($i = 0; $i < $executions; $i++)
{
    $a->Advisable();
}
$advisable_execution_time = microtime(true) - $start_time;
$start_time = microtime(true);
for ($i = 0; $i < $executions; $i++)
{
    $a->NonAdvisable();
}
$non_advisable_execution_time = microtime(true) - $start_time;

echo 'Ratio: '.$advisable_execution_time/$non_advisable_execution_time.'<br />';
echo 'Advisable: '.$advisable_execution_time.'<br />';
echo 'Non-Advisable: '.$non_advisable_execution_time.'<br />';
echo 'Overhead: '.($advisable_execution_time - $non_advisable_execution_time);

如果我以 100 (A::executions = 100) 的复杂度运行此测试,我会得到以下结果:

Ratio: 0.289029437803
Advisable: 7.08797502518
Non-Advisable: 24.5233671665
Overhead: -17.4353921413

有什么想法吗?

【问题讨论】:

  • 使用 microtime 分析此代码是不合适的。使用像 XDebug 这样的 Profiler 可以为各个方法收集数据。然后你会看到是什么导致了什么。
  • 没关系,我想通了。我在 get_advice() 方法中留下了一些调试代码,该方法立即返回 null。复杂度为 25 的开销约为 2.5:1,我认为还不错,并且从那里开始减少。不过,我会明确地研究 XDebbug。

标签: php performance reflection aop debug-backtrace


【解决方案1】:

当您调用 A 的 Advisable 方法时,您将跳过所有迭代......您将通过一次调用继承的 advisor() 方法来覆盖它。因此,当您添加迭代时,您只是将它们添加到 NonAdvisable() 调用中。

【讨论】:

    【解决方案2】:

    method overhead 我猜应该适用于 PHP 以及 Java -“调用的实际方法是在运行时确定的”=> 阴影 Advisible 方法的开销更大

    但它会是 O(1) 而不是 Non-Advisable 的 O(n)

    【讨论】:

      【解决方案3】:

      抱歉,我刚刚在调用父方法之前在get_advice 方法中找到了return null; 行。我讨厌回答我自己的问题,但这并不值得别人搜索。

      【讨论】:

        猜你喜欢
        • 2020-09-15
        • 1970-01-01
        • 2013-10-19
        • 2019-05-21
        • 2011-06-22
        • 2016-05-09
        • 1970-01-01
        • 2021-12-29
        • 1970-01-01
        相关资源
        最近更新 更多