views:

192

answers:

2

I notice that some PHP frameworks, such as Kohana and CodeIgniter would look at the class name of a class to perform auto-loading. Traditionally, RTTI is expensive for C++; how expensive it is for PHP5, in comparison to:

  1. A direct message call
  2. Looking up a key in an associative array
  3. Doing a message call via variables ( $class = 'foobar'; $method = 'foo'; $class->$method() )

Any general advice are welcomed too!

A: 

The reason RTTI is expensive in C++ is because you need to have the extra step of look-up (vtable for example). Take function call as an example. It is as a result much slower than moving the program counter register to a memory address that hosts the function code.

I haven't seen the implementation of PHP engine, but I suspect as an interpreting language, this "overhead" is either a must (given it doesn't compile the program, it needs to figure out where to call the function anyway), or is minimal compared with all the other overheads of an interpreting language.

That said, the best way to investigate is to do a simple experiment and profile the result. You can have two programs, one use RTTI to make function calls, and the other use direct calls, and compare the results from the two.

rxin
+2  A: 

In general, if you are using PHP, performance should not be your biggest worry, write good looking code first (ie. readable and maintainable, self documenting etc.) then profile and optimize as needed. If you begin by worrying about speed, PHP is probably not the way to go.

But to answer your question... get_class has pretty decent performance, i think it's pretty well optimized inside the zend engine. trying to call a non-existing function and dealing with the error is much more expensive. (it is a fatal error to call a non existent function, you do not get to catch it unless you write a bunch of glue code in your base object)

Here's a bit of benchmarking to show some of the different methods of determining the ability to run a method.

benchmark.php:

<?php

class MyClass {
    public function Hello() {
     return 'Hello, World!';
    }
}

function test_get_class( $instance ) {
    $t = get_class( $instance );
}

function test_is_callable( $instance ) {
    $t = is_callable( $instance, 'Hello' );
}

function test_method_exists( $instance ) {
    $t = method_exists( $instance, 'Hello' );
}

function test_just_call( $instance ) {
    $result = $instance->Hello();
}

function benchmark($iterations, $function, $args=null) {
    $start = microtime(true);
    for( $i = 0; $i < $iterations; $i ++ ) {
     call_user_func_Array( $function, $args );
    }
    return microtime(true)-$start;
}

$instance = new MyClass();

printf( "get_class:     %s\n", number_format(benchmark( 100000, 'test_get_class', array( $instance ) ), 5) );
printf( "is_callable:   %s\n", number_format(benchmark( 100000, 'test_is_callable', array( $instance ) ), 5) );
printf( "method_exists: %s\n", number_format(benchmark( 100000, 'test_method_exists', array( $instance ) ), 5) );
printf( "just_call:     %s\n", number_format(benchmark( 100000, 'test_just_call', array( $instance ) ), 5) );

?>

results:

get_class:     0.78946
is_callable:   0.87505
method_exists: 0.83352
just_call:     0.85176
Kris