Const function substitution in Clang
I am currently working on a synthesizer for the iPad. An important thing here is that the sound rendering callback must be as fast as possible so that you can reduce the audio buffer size and thus get a lower latency.
This means that, if you want to do anything more than a stupid non-bandlimited monosynth with horrible sound quality (such as the currently trending free Core Synth, but there are many others), you will have to spend a lot of time optimizing the renderer. This is especially important when you want to build a polyphonic synthesizer, where rendering time scales linear with the amount of concurrently playing notes.
Recently I discovered that Clang optimizes differently depending on whether you use single or double precision floating point numbers. The result of this is something you would not expect. Today I want to focus on a single aspect which might slow a lot of things down. Suppose you have this piece of code:
double y = exp(123.456);
Any sane programmer would convert this to machine code by evaluating the exponential function during compilation and writing down a single load-constant operation in the compiled program. In other words, the compiler would treat the exponential function as a const function. That is a function that produces an output value that only depends on the input, meaning that there are no side effects and every invocation of the function with a constant input value will always result in the same output value.
However, it turns out that Clang can only optimize away some of these functions when they are called with constant parameters. So I’ve made a little table which tells you what functions you can safely use with constants. The functions that have a “no” will be compiled to a normal function call even when the input is a constant, so they should be replaced with precomputed constants if possible.
So here’s the table. Measurements were done using the Apple LLVM 3.0 compiler which is found in the XCode 4.2 beta.
| Function | float | double |
|---|---|---|
| sin | yes | yes |
| cos | yes | yes |
| tan | no | yes |
| asin | no | no |
| acos | no | no |
| atan | no | yes |
| atan2 | no | yes |
| sinh | no | yes |
| cosh | no | yes |
| tanh | no | yes |
| exp | no | yes |
| exp2 | no | no |
| expm1 | no | no |
| log | no | yes |
| log2 | no | no |
| log10 | no | yes |
| log1p | no | no |
| fmod | no | yes |
| fabs | yes | yes |
| sqrt | yes | yes |
| pow | no | no |
| ceil | no | yes |
| floor | no | yes |
| rint | no | no |
| round | no | no |
| trunc | no | no |
When you write your own functions you can give Clang a hint that this function is a const function and has no side effects. This helps Clang to optimize away instances which are called with constants. In order to help Clang you have to modify your function declaration to be as follows:
int __attribute__ ((const)) myfunction(int arg) {
return 42 + arg;
}
This trick originates from GCC, so more documentation about function attributes can be found in the GCC documentation.