Are there any rules of thumb when โ€œvirtualโ€ are significant overhead?

My questions are mostly completely outlined in the title, however let me clarify.

Question: Maybe itโ€™s worth rephrasing how complicated / simple the virtual method is to make the mechanism a significant overhead? Are there any rules of thumb? For example. If it takes 10 minutes, it uses I / O, complex if , memory operations, etc. This is not a problem. Or, if you write virtual get_r() { return sqrt( x*x + y*y); }; virtual get_r() { return sqrt( x*x + y*y); }; and call it in a loop, you will have problems.

I hope this question is not too general, as I am looking for some general, but specific technical answers. Either it's hard / impossible to say, or virtual calls take up so many resources of time / cycles, and math takes it, I / O it.

Perhaps some technical people know some common figures to compare or do some analysis, and can share general conclusions. Embarrassingly, I do not know how to make these fantasies asm analysis = /.

I would also like to give some explanation for this, as well as my use case.

I think I saw more than a few questions when people refused to use virtual games, such as open fire in the forest during a drought, for the sake of productivity, and so many people asked them: "Are you absolutely sure that virtual overheads really are problem in your case? "

In my recent work, I encountered a problem, which, in my opinion, can be placed on both sides of the river.

Also keep in mind, I am not asking how to improve the implementation of the interface. I seem to know how to do this. I ask if it is possible to say when to do this or to choose the right bits.

Use case:

I am running some simulations. I have a class that basically provides a runtime. There is a base class and several derived classes that define several different workflows. The base collects material as general logic and assigns sources and receivers of input-output. Derivatives define specific workflows, more or less, by implementing RunEnv::run() . I think this is the right design. Now suppose that objects that are subjects of a workflow can be placed in a 2D or 3D plane. Workflows are common / interchangeable in both cases, so the objects we are working on can have a common interface, albeit very simple methods like Object::get_r() . In addition, a statistical log can be defined for the environment.

Initially, I wanted to provide some code snippets, but in the end it had 5 classes and 2-4 methods, each of which constituted a wall of code . I can send it on request, but this will extend the question to two different current sizes.

Key points: RunEnv::run() - the main loop. Usually a very long time (5 minutes-5 hours). It provides base time, calls to RunEnv::process_iteration() and RunEnv::log_stats() . All are virtual. There is a justification. I can get RunEnv , a run() redesign, for example, for different stop conditions. I can redesign process_iteration() , for example, use multithreading if I need to process a pool of objects, process them in various ways. Also, various workflows will want to record various statistics. RunEnv::log_stats() is just a call that displays the already computed interesting statistics in std::ostream . I assume that I am using virtual machines and have no real effect.

Now let's say that iteration works by calculating the distance of objects to the origin. So, we have the interface double Obj::get_r(); . Obj are an implementation for 2D and 3D cases. The getter in both cases is a simple math with 2-3 multiplications and additions.

I also experimented with different memory processing. For example. sometimes the data coordinates were stored in private variables and sometimes in a common pool, so even get_x() could be made virtual with the implementation of get_x(){return x;}; or get_x(){ return pool[my_num*dim+x_offset]; }; get_x(){ return pool[my_num*dim+x_offset]; }; . Imagine calculating something with get_r(){ sqrt(get_x()*get_x() + get_y()*get_y()) ;}; . I suspect that virtuality here will kill performance.

+7
c ++ performance
source share
4 answers

A virtual method call in C ++ on x86 gives code similar to (single inheritance):

  mov ecx,[esp+4] mov eax,[ecx] // pointer to vtable jmp [eax] 

Without a virtual, you will spare one mov instruction compared to a non-virtual member function. Thus, in the case of single inheritance, the performance loss is negligible.

If you have multiple inheritance or, even worse, virtual inheritance, virtual calls can be much more complicated. But this is more a problem of class hierarchy and architecture.

Rule of thumb:

If the method body is many times (> 100x) slower than one mov instruction, just use virtual and don't worry. Otherwise, profile bottlenecks and optimize.

Update:

For multiple / virtual inheritance cases, check this page: http://www.lrdev.com/lr/c/virtual.html

+8
source share

Are there any rules of thumb?

The best general rule for questions like this:

measure your code before optimizing

Trying to make code work well without measurement is the right way to overly complex code that is optimized in all the wrong places.

So, don't worry about the overhead of a virtual function until you get strong evidence that the problem is virtual . If you have such evidence, you can work to remove virtual in this case. You will most likely find that finding ways to speed up your calculations or avoid calculating where you donโ€™t need to lead to a significant increase in performance. But then again, don't just guess - measure first.

+8
source share

First, of course, any difference will depend on the compiler, architecture, etc. On some machines, the difference between a virtual call and a non-virtual one is unlikely to be measurable, at least one other, it (or it will be --- my experience with this machine is quite ancient) completely blows off the pipeline (there is no branch prediction for indirect transitions).

On most processors, the real cost of virtual functions is the loss of the ability to embed, followed by the loss of other optimization capabilities. In other words, the cost actually depends on the context in which the function is called.

Moreover, however: virtual functions and non-virtual functions have different semantics. Therefore, you cannot choose: if you need virtual semantics, you must use virtual; if you do not need virtual semantics, you cannot use virtual. So the question really does not come.

+3
source share

The absolute main recommendation, which, like others, has been stated, should be the profile in your particular application and environment, is to avoid virtual in tight loops.

Note: if you really need polymorphic behavior, virtual member functions are likely to be better than most alternatives. An exception may be when you have a collection of polymorphic but homogeneous types (the collection can be any of the polymorphic types, but they are all of the same type, whatever type they may be). Then you better change the polymorphic behavior outside the loop.

Taking a classic mute bad-OO example using shapes, you would be better off:

 // "fast" way struct Shape { virtual void DrawAll(Collection) = 0; }; struct Rectangle : public Shape { virtual void DrawAll(Collection collection) { for (const auto& rect : collection) do_rectangle_draw(); } }; struct Circle : public Shape { virtual void DrawAll(Collection collection) { for (const auto& circle : collection) do_circle_draw(); } }; 

The more naive version, which may be something like:

 // "slow" way struct Shape { virtual void DrawSelf() = 0; void DrawAll(Collection collection) { for (const auto& shape : collection) shape.DrawSelf(); // virtual invocation for every item in the collection! } }; 

Again, this only works with uniform types in collections. If your Collection can contain both Rectangle and Circle s, then you will need to distinguish each instance during the iteration as to which drawing method to use. A virtual function is likely to be faster than a function pointer or switch statement (but a profile is required).

The purpose of the above code was to move polymorphic behavior out of a loop. This is not always possible, but when it is, it will usually be to some extent a gain in performance. For a large number of objects (say, a particle simulator), the difference in performance can be quite noticeable.

If a function is not called many thousands of times inside a loop, you probably won't notice any noticeable difference between virtual and non-virtual functions. But consult with him to check and make sure that you think this is important.

+1
source share

All Articles