2012-03-16 6 views
2

私は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を複雑で、このテストを実行する場合: :実行= 100)、私は次のようになる:

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

アイデア?

+2

このコードのプロファイリングにmicrotimeを使用することは不適切です。個々のメソッドのデータを収集できるXDebugのようなプロファイラを使用します。それで何が何を引き起こしているのかが分かります。 – Gordon

+0

気にしない、私はそれを理解した。すぐにnullを返すget_advice()メソッドにデバッグコードを残しました。 25の複雑さのためのオーバーヘッドは約2.5:1であり、私は悪くないと思うし、それはそこから減少する。私は確かにXDebbugを調べます。 – Godwin

答えて

0

親メソッドが呼び出される前に、get_adviceメソッドで行return null;が見つかりました。私は自分自身の質問に答えるのが嫌いですが、それを探している人の価値はありません。

0

AのAdvisableメソッドを呼び出すときに、すべての繰り返しをスキップしています...継承されたadvise()メソッドを1回呼び出すだけで上書きされます。したがって、反復を追加する場合は、それらをNonAdvisable()呼び出しに追加するだけです。

0

method overheadは、PHPにだけでなく、Javaへの適用すべきである私は推測 - 「と呼ばれる実際のメソッドが実行時に決定された」影のAdvisible方法について=>オーバーヘッド

大きいですが、それはO(だろう1)の代わりにNon-AdvisableのO(n)

関連する問題