Skip to main content
GameDev.net gamedev.net
🔒 Locked

Good practices: power and log functions

Started by heron3d Oct 22, 2011 at 7:12 PM 14 replies 2.4k views
Original Post
heron3d
heron3d
Its been suggested to me more than once to avoid the powf function because it is an expensive process. Does the same go for the log function? Why is it an expensive process for the processor?

Nikos
heron3d
heron3d

The processor doesn't have native functions for pow and log so it ends up calculating it with some sort of Taylor approximation which is rather expensive.


so is the log() to be avoided just as much as the pow()?
SiCrane
SiCrane
Meh. Write what's clearest and most natural and only rewrite things if a profiler tells you something's a bottleneck.
alvaro
alvaro
What SiCrane says. Also, the idea that powf is slow probably comes from times when compilers did something very naive with it. These days, a compiler will probably issue the same code for powf(x,2) as for x*x.
markr
markr
Optimisation needs to include the optimisation of the programmer's time. If log or pow makes most sense to write, then write it until the profiler tells you not do.
21st Century Moose
21st Century Moose
Basically what everyone else said. Don't try to second-guess the compiler; it will only end in tears.
Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls. 
heron3d
heron3d
thanks for all the responses. I will approach it that way. Will make my life so much easier:)
iMalc
iMalc

What SiCrane says. Also, the idea that powf is slow probably comes from times when compilers did something very naive with it. These days, a compiler will probably issue the same code for powf(x,2) as for x*x.

Actually they very probably don't. We went through this recently on a different forum.
To back that statement I just tried it in VS2008 Express with full optimisations on in release build and with code written such that it cannot be optimised out. There was a 14x speed difference between using powf(x, 2) and x*x.

That way of thinking works for a lot of things, but the math library routines are not one of them. Avoid using overly flexible functions when the simpler one will do. E.g. use sqrt(x) instead of pow(x, 0.5) because the former is specific and the later has to cater for the general case which means extra work.

The bottom line is, avoid log, pow, or other math library routines etc only where there is a clear, simple, and obvious alternative that doesn't require as much generality as those methods provide.
alvaro
alvaro

[quote name='alvaro' timestamp='1319313688' post='4875421']
What SiCrane says. Also, the idea that powf is slow probably comes from times when compilers did something very naive with it. These days, a compiler will probably issue the same code for powf(x,2) as for x*x.

Actually they very probably don't. We went through this recently on a different forum.
To back that statement I just tried it in VS2008 Express with full optimisations on in release build and with code written such that it cannot be optimised out. There was a 14x speed difference between using powf(x, 2) and x*x.[/quote]

I checked shortly after I posted that comment, just to make sure that I am not spreading false information. gcc 4.6.1 seems to be smarter about this.

In C:
#include <math.h>

float f(float x) {
return powf(x, 2);
}

The assembly produced is:
.file "kk.c"
.text
.p2align 4,,15
.globl f
.type f, @function
f:
.LFB3:
.cfi_startproc
mulss %xmm0, %xmm0 // Hard to optimize beyond this
ret
.cfi_endproc
.LFE3:
.size f, .-f
.ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1"
.section .note.GNU-stack,"",@progbits
Hodgman
Hodgman
To back that statement I just tried it in VS2008 Express with full optimisations on in release build and with code written such that it cannot be optimised out.
Out of interest, did that include using the fast floating point model and SSE instruction sets?
taz0010
taz0010
VS2010, SSE2, FP:Fast

float x;
cin >> x;
cout << pow(x,2);

012D1014 movss xmm0,dword ptr [x]
012D1019 movss xmm1,dword ptr [__real@3f800000 (12D2104h)]
012D1021 mov eax,2
012D1026 test al,1
012D1028 je main+2Eh (12D102Eh)
012D102A mulss xmm1,xmm0
012D102E shr eax,1
012D1030 je main+38h (12D1038h)
012D1032 mulss xmm0,xmm0
012D1036 jmp main+26h (12D1026h)

It doesn't optimise out to x * x, however it is applying a very useful optimisation which becomes apparent for higher integral powers of x.

float x;
cin >> x;
cout << pow(x,241);

00AF1014 movss xmm0,dword ptr [x]
00AF1019 movss xmm1,dword ptr [__real@3f800000 (0AF2104h)]
00AF1021 mov eax,0F1h
00AF1026 test al,1
00AF1028 je main+2Eh (0AF102Eh)
00AF102A mulss xmm1,xmm0
00AF102E shr eax,1
00AF1030 je main+38h (0AF1038h)
00AF1032 mulss xmm0,xmm0
00AF1036 jmp main+26h (0AF1026h)

I'm not entirely sure how this works actually, but I think it's expanding x^n out to x*x*x*x... and then finding an optimum grouping. E.g. x^20 can be calculated quickly as:
x1 = x*x
x2 = x1*x1
x3 = x2 * x2
x4 = x3 * x3
result = x4 * x2


The branching incurs a performance cost, however the method is utilised even when the power is not a constant.
alvaro
alvaro
VS2010 seems to be using exponentiation by squaring. My gcc 4.6.1 (the C compiler) seems to only know the trick for powf(x,2), and it makes a function call for all other cases. g++ 4.6.1 (the C++ compiler) optimizes any constant positive integer power. The resulting code is very amusing. For std::pow(x,241):
movaps %xmm0, %xmm1
mulss %xmm0, %xmm1 // 1 + 1 = 2
mulss %xmm0, %xmm1 // 1 + 2 = 3
movaps %xmm1, %xmm2
mulss %xmm1, %xmm2 // 3 + 3 = 6
mulss %xmm2, %xmm1 // 6 + 3 = 9
mulss %xmm2, %xmm1 // 6 + 9 = 15
mulss %xmm1, %xmm1 // 15 + 15 = 30
mulss %xmm1, %xmm1 // 30 + 30 = 60
mulss %xmm1, %xmm1 // 60 + 60 = 120
mulss %xmm1, %xmm1 // 120 + 120 = 240
mulss %xmm0, %xmm1 // 1 + 240 = 241
movaps %xmm1, %xmm0
ret
iMalc
iMalc

[quote name='iMalc' timestamp='1319396655' post='4876035']To back that statement I just tried it in VS2008 Express with full optimisations on in release build and with code written such that it cannot be optimised out.
Out of interest, did that include using the fast floating point model and SSE instruction sets?
[/quote]I don't think it did, but I've deleted that now so never mind.

I would point out though that it isn't necessarily practical to change the floating point model when working on an existing codebase because you may needlessly introduce a bug somewhere.
Of course not everything is even about release build with full optimisations either. It's kinda nice when your debug version isn't 20 times slower than release as it makes for a much nicer debugging experience when the speed difference is minimal.

That's great to see that some compilers are smarter about this. Regardless, it's not going to change that I'm an advocate of just using x*x instead.
taz0010
taz0010
The debug version of MSVC's STL is so inefficient that my debug builds are closer to 100x slower than release. I once stepped through the assembly and counted 5 critical sections just to create an iterator. I know people keep saying not to write your own implementation of the STL, but debugging is getting to be a serious problem.

rip-off
rip-off
There are additional preprocessor flags you can turn off, even in debug mode. Look into HAS_ITERATOR_DEBUGGING and _SECURE_SCL. That said, your comment seems a little off topic.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.