How Expensive Is Rtti

RTTI Overhead in C++

Enabling RTTI typically brings only a small overhead. The usual implementation carries a pointer to the type information structure in the vtable of an object. Since the vtable must be constructed anyway, the extra time is small - it's like adding another virtual function to the class.

typeid is therefore comparable to calling a virtual function.
dynamic_cast is slower - it needs to traverse the inheritance hierarchy to do a cast. Calling dynamic_cast too frequently can be a performance bottleneck. By 'can' I mean that it usually won't …

There is a slight bloat in executable size since the typeinfo structures need to be stored somewhere. In most cases it won't be relevant.

How expensive are dynamic casts in C++?

1200 dynamic_casts per second is not likely to be a major performance problem. Are you doing one dynamic_cast per image, or a whole sequence of if statements until you find the actual type?

If you're worried about performance, the fastest ways to implement polymorphism are:

  • --- fastest ---
  • Function overloading (compile-time polymorphism only)
  • CRTP (compile-time polymorphism only)
  • Tags, switches and static casts (brittle, doesn't support multi-level inheritance, a maintenance headache so not recommended for unstable code)
  • Virtual functions
  • Visitor pattern (inverted virtual function)
  • --- almost as fast ---

In your situation, the visitor pattern is probably the best choice. It's two virtual calls instead of one, but allows you to keep the algorithm implementation separate from the image data structure.

PHP5 - What is the cost of RTTI (get class' name and etc.) in PHP5?

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

What significant exceptions are there to the zero overhead principle, if any?


Is this still a good idea, or is that coding standard outdated?

The RTTI is most definitely the most notorious violation of the zero-overhead principle, because it incurs a static cost (executable size, and initialization code) that is proportional to the number of polymorphic classes (i.e., classes with at least one virtual function), and it does not depend on how much you use it, if at all. But there is no way to really provide RTTI without some per-class overhead. That's why you can disable RTTI on most compilers if you don't need it at all, or if you want replace it with an RTTI system you have more control over (as the LLVM guys did). Nevertheless, if you have RTTI enabled, and you are not using it, the overhead is only in the form of code bloat (larger executable, larger memory space needed, larger spread of code) and loading / unloading time, and so, the run-time execution overhead is almost non-existent. But in resource-deprived environments, or for small utility programs (e.g., invoked repetitively in shell scripts), that static overhead can be too much to bear. Moreover, there aren't that many practical situations where you need a high-performance RTTI, most of the time you don't need it at all, and at other times you need it in a few special places that are usually not performance-critical in comparison to other things. LLVM is an exception here because writing a compiler involves dealing with abstract syntax trees and similar descriptive class hierarchies, which is hard to do without lots of down-casting, and since analysing these structures is the core operation that compilers do, performance of the down-casts (or other RTTI invocations) is critical. So, don't take the "don't use the RTTI" as a general rule, just know what overhead it entails, and know if it is acceptable for your application in terms of its cost / benefits.

C++ exceptions are certainly next on the list of things that could have more overhead than you would be prepared to bargain for. This is a much more controversial issue, particularly when it comes to actually characterising the overhead of exceptions overall. Evaluating the overhead of exceptions empirically is a very difficult task because it is highly dependent on usage patterns, i.e., there are different ways to use exceptions, different levels of severity (Do you use exceptions for bugs, fatal errors, exceptional conditions, or to replace every if-statement?), and there are different levels of care for error-handling (whether with or without using exceptions). And then, of course, different compilers can implement exceptions differently. The current implementations, so-called "zero-cost exceptions", is geared towards having zero run-time cost during normal execution, but that leaves quite a bit of static overhead and makes throw-to-catch execution path slower. Here is a nice overview of that. As far as exceptions being in violation of the "you only pay for what you use" principle, it is true (unless you disable them), but they are often justifiable. The basic assumption with exceptions is that you, as a programmer, intend to write robust error-handling code, and if you do handle all errors appropriately, then the overhead of exceptions will pale in comparison to the error-handling code (catch-blocks and destructors), and you will probably have a smaller and faster program than an equivalent C-style error-code implementation of the same amount of error-handling. But if you don't intend to do much error-handling (e.g., the "if anything goes wrong, just crash!" approach), then exceptions will incur significant overhead. I'm not exactly sure why LLVM banned exceptions (if I had to be blunt, I would say it's because they aren't really serious about error-handling, as far as I can see from the code I have seen from that project). So, long story short, the guideline should be "if you intend to seriously handle errors, use exceptions, otherwise, don't". But remember, this is a hotly debated topic.

Are there any other such features which violate "you only pay for what you use"?

You have named to two obvious ones, and unsurprisingly, they are the two main features that most compilers have options to disable (and are often disabled when it is appropriate to do so). There are certainly other more minor violations of the zero-overhead principles.

One notorious example is the IO-stream library (<iostream>) from the standard library. This library has often been criticized for having too much overhead for what most people need and use it for. The IO-stream library tends to pull in a lot of code, and require quite a bit of load-time initialization. And then, many of its classes like std::ostream or std::ofstream have too much run-time overhead, both for construction / destruction and for read/write performance. I think they packed a bit too many features into that library, and since most of the time, IO-streaming tasks are very simple, those features are often left unused and their overhead unjustified. But generally, I find the overhead is often acceptable, especially since most IO tasks are already very slow regardless. BTW, LLVM also bans the use of the IO-stream library, and again, that is because the target of LLVM is for writing lean and mean command-line utilities that do a lot of file IO (like a compiler or its related tools).

There might be other standard libraries that have more overhead than some might wish to have for particular situations. Library code often has to make compromises that fall somewhere that won't please everyone. I would suspect that some of the newer libraries like thread, chrono, regex, and random, provide a bit more features or robust guarantees than are necessary in many applications, and therefore, pull in some undue overhead. But then again, many applications do benefit from those features. This is the very meaning of compromise.

As for language rules that put undue overhead, there are many small issues that impose some overhead. For one, I can think of several places where the standard has to make conservative assumptions that prevent optimizations. One notable example is the inability to restrict pointer aliasing, which forces compilers to assume that any memory could be aliased by any pointer (even though, in practice, pointer aliasing is rare), limiting the opportunities for optimization. There are many similar cases where the compiler has to make the "safe" assumption, limiting optimizations. But most of these are rather small in scope and potential benefits, and they are often justified in terms of being able to guarantee correctness of the behaviour (and repeatability, robustness, portability, etc.). Also, note that in the vast majority of those cases, it doesn't really get any better in other languages, maybe marginally better in C, but that's about it. Some of those issues can also be circumvented with compiler extensions or platform-specific features, or as a last resort, with inline assembly code, that is, if you really need to optimize down to that level.

One example that is no longer valid is the problem of requiring the compiler to produce exception-handling (stack unwinding) code even for functions that will never throw. Now, that can be solved by specifying noexcept on the functions in question.

But other than those microscopic issues, I can't really think of any other major source of undue overhead in C++ (aside from RTTI and exceptions). I mean, there are things in C++ that can create overhead of different kinds, but they are all "opt-in" features (per-use) like virtual functions, virtual inheritance, multiple inheritance, templates, etc... but those mostly obey the "you only pay for what you use" principle. For an example of the rules imposed for a low-overhead subset of C++, check out Embedded C++.

Does C++ RTTI work always on all platforms?

Especially for small embedded system RTTI is not available. Another example is 8 bit AVR target.

The reason is quite simple: RTTI needs more memory for the information itself. So for small embedded systems it is typically off by default or it is simply not implemented for the target like with avr-gcc.

Normally in c++ it is guaranteed that you pay not for things you do not use. But RTTI is one example where you need more memory also if you don't use it until you switch it off for your modules which do not need RTTI.



Related Topics



Leave a reply



Submit