🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++ tutorials

Started by
93 comments, last by taby 1 year, 9 months ago

Oberon_Command said:
That matches my expectations as well - I expect most of the perf loss to be related to the allocations.

Sure, but I was still suprised to see pretty much a 0% overhead on call (I expected at least a bit, due to having to go through the type-erasure => though maybe this part is just heavily optimized at the cost of memory/allocation speed in the implementation that I tested). Also, I was suprised by the amount of overhead due to the deallocation caused by the captureless variant. Sure, it has to allocate/deallocate the type-erasure container, but I expected it to be a lot smarter about how it would handle a simple member-to-function pointer with no capture. Would be interesting to see how other implementations fair.

taby said:
Using the Release build, the numbers are close, but not quite the same: Function pointer method took 1.42315 seconds Function<> method took 1.97452 seconds

Which compiler did you test this on? Would also be interested in seeing to code to compare it to what I've done previously.

Advertisement

Reminds me on my example of replacing std::function with a template type on page 2.

std::function overhead really was expensive, the latter showed no performance loss for the example vs. inlining the whole iterator helper function (with Clang at least).

I do not understand the reasons in detail, but some of those 3 function ‘callbacks’ are called very often.
However, during that whole process i do not expect object deallocations going on. The 3 objects (made from lambdas) should live until everything is done i guess.

JoeJ said:
Reminds me on my example of replacing std::function with a template type on page 2. std::function overhead really was expensive, the latter showed no performance loss for the example (with Clang at least).

std::function should never be able to outperform a function-pointer/templated solution. The inverse can obviously be true - I'm kind of intrigued now, I might revisit the example I built last time, now that I understand assembly a bit more and am better able to see if the compiler might have just been big-brain and optimized the std::function away entirely for me, even if I expected that it couldn't ?

Juliean said:
I might revisit the example I built last time, now that I understand assembly a bit more and am better able to see if the compiler might have just been big-brain and optimized the std::function away entirely for me

Yeah, i guess it's worth the effort.
For me std::function is something i really would like to use often, but can't because it's not free.
Compilers make a big difference too. Clang usually gives me 20-30% better perf. overall, but sometimes VS seems buggy with it. At least it's nowadays easy to switch between MSVC and Clang.

JoeJ said:
Yeah, i guess it's worth the effort. For me std::function is something i really would like to use often, but can't because it's not free. Compilers make a big difference too. Clang usually gives me 20-30% better perf. overall, but sometimes VS seems buggy with it. At least it's nowadays easy to switch between MSVC and Clang.

FWIW I made my own variant of std::function, where the main difference is that it cannot be copied - thus it can be much more optimized. Essentially its like a member-to-function pointer with an additional pointer to a deleter, so that one, in regards to calls, is guaranteed to be free (*at least for a case where member-to-function could be used; obviously a solo static-function pointer might still be cheaper under certain circumstances). That is btw not an invention by myself; there are event standard-papers for c++ suggestion a non-copyable std::function-variant, as this solve many of the issues that current std::function has.

Regarding switch between MSVC and Clang - personally I didn't have much sucess with that. Are you talking about selecting clang as compiler inside Visual Studio? I'm kind of annoyed at MSVC for breaking their /latest every other revision, so I was trying clang for Visual Studio once, but it didn't manage to compile any of the Window SDK-headers, and I quit after that.

Juliean said:
Are you talking about selecting clang as compiler inside Visual Studio? I'm kind of annoyed at MSVC for breaking their /latest every other revision, so I was trying clang for Visual Studio once, but it didn't manage to compile any of the Window SDK-headers, and I quit after that.

Yes. I'm only switching the platform toolset in project options (after installing VS Clang additions):

That's all. But it's a new feature, maybe since VS 2019. Before that it was pretty complicated to get to work, and i never tried.

I use Clang only for release builds so far. Not sure if debugging works properly, but not needed.
The problems i sometimes have with Clang are random crashes, which look like caused from memory corruptions. But because i got them in multiple project, i guess it's some VS bug.
(I also still install VS offline, which might cause some issues on my side. Reinstalling VS completely seems to affect those issues.)

Juliean said:

taby said:
Using the Release build, the numbers are close, but not quite the same: Function pointer method took 1.42315 seconds Function<>

Which compiler did you test this on? Would also be interested in seeing to code to compare it to what I've done previously.

Sorry, I am using Visual Studio 2022. I'm using Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31630 for x64

The code is at https://github.com/sjhalayka/sample_cpp_code/blob/main/03_advanced/s19_function_pointers.cpp

P.S. My old Julia 4D 2 program had a menu full of equations to choose from. 42 altogether, including a custom equation where the user can type in an equation. So, it was found that using an if-else statement was slower than using a function pointer. https://github.com/sjhalayka/Julia4d2​ (requires D3D9 and MFC for compiling).

taby said:
Sorry, I am using Visual Studio 2022. I'm using Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31630 for x64 The code is at https://github.com/sjhalayka/sample_cpp_code/blob/main/03_advanced/s19_function_pointers.cpp

Sorry to say, but that test is literally worthless.

	f2 = &B::proceed_d;

	start_time = high_resolution_clock::now();

	for (size_t i = 0; i < iterations; i++)
		(*f2)(param); // Call function

Any compiler not written by a brain-dead monkey will be able to resolve “f2 == &B::proceed_d” and replace+inline that call, instead of going through the function-pointer. Which in turn will mean that the entire for-loop will be eliminated, since nothing happens inside proceed_d. See https://godbolt.org/z/vc1E54EWY​ - no code is generated for the lines (here with gcc + 03). The same for the function<>-object.

If you want to actually measure those differences, you need to make sure the compiler is unable to fully resolve the value of the function-pointer. This can be done be initializing f2 with a global function, that is set to “noinline”, or you do the entire test inside a no-inline function where the pointer is passed in. Otherwise, your results are unindicative at best, and entirely misleading at worst.

You're missing the point. In the event where it does matter, a 42-long if-else statement is generally slower than a function pointer. Like I said, look at the source for Julia 4D 2 ( https://github.com/sjhalayka/Julia4d2/blob/aac5a2fd485802cf78c232bbd474c1338e20f53f/quaternion_julia_set.cpp#L409 )​ ​. It proves my point. Plus, it says right there in the tutorial sample code ( https://github.com/sjhalayka/sample_cpp_code/blob/main/03_advanced/s19_function_pointers.cpp ) that you just need to add more cases, and eventually, the function pointer will pay off.

This topic is closed to new replies.

Advertisement