0

I just learned that inline makes my code faster when working with functions, but I wonder why don't we just inline every single function, what's the point of writing functions in the normal way why we have such a powerful keyword (I know that the compiler chooses whether to inline the function or not but it would be better to suggest inlining every function)?

plus: using Clion I am trying to inline the following function but I get an error:

#include <stdio.h>
#include <time.h>


inline double maxf(int a,int b)
{
    if (a>b)
        return a;
    return b;
}

int main() {
    clock_t begin = clock();
    double arr[999999]={0};
    double max=arr[0];
    arr[9898]=99999999;
    for (int i=1;i<999999;i++)
    {
            max=maxf(arr[i],max);
    }
    clock_t end = clock();
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%lf",time_spent);
    return 0;
}

[ 50%] Building C object CMakeFiles/untitled.dir/main.c.o
[100%] Linking C executable untitled
Undefined symbols for architecture x86_64:
  "_maxf", referenced from:
      _main in main.c.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [untitled] Error 1
make[2]: *** [CMakeFiles/untitled.dir/all] Error 2
make[1]: *** [CMakeFiles/untitled.dir/rule] Error 2
make: *** [untitled] Error 2

Any ideas of what is causing this problem?

  • 4
    If you did, it could make your program size exponentially larger. Imagine if you call a large function many times, and it's `inline`d. – Fiddling Bits Mar 24 '20 at 15:04
  • `a` and `b` should be `double`s. `double maxf(int a,int b)` – Fiddling Bits Mar 24 '20 at 15:07
  • 4
    The compiler does not *have* do make the function `inline` anyway. The keyword is an **invitation** to do so. *"6.7.4.5 Making a function an inline function suggests that calls to the function be as fast as possible. The extent to which such suggestions are effective is implementation-defined."* – Weather Vane Mar 24 '20 at 15:10
  • 2
    Inlining does not _always_ make the code faster. – 500 - Internal Server Error Mar 24 '20 at 16:17
  • regarding: `arr[9898]=99999999;` Since the array `arr[]` is an array of `double` values, the literal: `99999999` should also be a double. Suggest: `arr[9898]=99999999.0;` – user3629249 Mar 24 '20 at 17:14
  • OT: regarding: `for (int i=1;i<999999;i++)` This has the 'magic' number: `999999`. 'magic' numbers make the code more difficult to understand, debug, etc. Suggest using a `#define` or `enum` statement to give that 'magic' number a meaningful name, then using that meaningful name throughout the code – user3629249 Mar 24 '20 at 17:16
  • OT: the function name: `maxf` is a well known function, exposed via the header file: `math.h` – user3629249 Mar 24 '20 at 17:19
  • regarding: `double arr[999999]={0};` Given that way that a `double` contains a `128` offset value when the result is 0.0 suggest modifying the statement to: `double arr[999999]={0.0};` – user3629249 Mar 24 '20 at 17:28
  • regarding; `inline double maxf(int a,int b)` the parameters are `double` values, not `int` values. Suggest: `inline double maxf( double a, double b )` – user3629249 Mar 24 '20 at 17:30

2 Answers2

1

The reason that you get a linker error on that program is that your program uses the function maxf, which is in the math library, and you don't link with -lm. This only happens at certain optimisation levels.

Note that it's possible for the compiler to decide to neither inline nor call maxf because it can see that the variable max is unused, and therefore actually saving a value to it is unnecessary. Provided that the compiler knows that maxf has no visible side-effects (and it knows that because maxf is a standard library function), it can therefore eliminate the assignment to max and therefore the call to maxf. In fact, the compiler might even know what the semantics of maxf are, and avoid the call by precomputing at compile time.

In order to avoid this case in your benchmark, you should do both of the following:

  • Change the name of maxf so that it doesn't collide with a standard library function name. (Below I assume you renamed it as myMax.
  • Either declare the variable max as volatile or use it in some non-predictable way, so that the store happens.

You should also fix the prototype of your function, since it's being called with a double argument.

Now, let's suppose that you've fixed your benchmark accordingly, and you compile it. You will still receive the linker error at certain optimisation levels, because without optimisation (or in the case of Clang, at optimisation level less than 2), inlining is not performed. (In GCC at level -O1, the function is only inlined because there is just one call to it in the translation unit. Had it been called in two places, you'd need optimisation level 2 to trigger inlining, as with Clang.)

But it's clear that there is a definition of maxf (or, in the corrected code, myMax or some such). So why can't the compiler use it?

The answer has to do with the actual semantics of inline, which might help answer your first question. As we all know (and can see in the lengthy discussion above), the compiler makes a decision about inlining functions more or less independent of the programmer's suggestion in the inline specifier. (Compare this with the now-deprecated register specifier, which compilers have been ignoring for a long time.)

But inline does have an important significance. While the compiler can figure out, based on its own heuristics, code analysis and optimisation settings, whether or not it's a good idea to inline a function, it cannot know what your intentions are about use of that function in other translation units which will be linked to create the executable.

Perhaps in some other translation unit, the function myMax is called as an external function. In that case, the compiler will need to include a compilation of myMax whether or not it chose to inline all uses in this file.

On the other hand, it's possible that myMax is intended to be inlined in every translation unit in which it appears; in other words, all translation units include a definition of myMax. But that would lead to a problem, because those various definitions would collide when the translation units were linked together, generating a different linker error. (Duplicate name definition.) You could get around that by declaring the function static, but in that case you might find that various modules in your executable each had their own static definition of the function, because the compiler chose not to inline it in those modules. That could significantly increase executable size.

And that's where the inline declaration comes in. An inline declaration of a function means "this is the definition to use in cases where this function is being inlined". If the compiler sees a function declared inline without being explicitly declared extern, it does not emit a definition of the function even if it chooses to not inline it. It relies on the function being defined in some external translation unit.

So you can safely put an inline declaration in a header file. That will ensure that all inline uses of the function use the same definition, without causing any problems with duplicate names in the linker. But you still need to ensure that the function has a definition in exactly one translation unit. (This is somewhat similar to the related issue of declaring global variables used by various translation units in a program. The global variable must be declared consistently in all translation units, but defined only in one.)

In the case of inline functions, you indicate that a definition of the function should definitely be compiled by using an extern delaration. If you've declared myMax as inline in your header file, you can satisfy this requirement by putting the following in exactly one implementation file:

#include "myMax.h"
extern double myMax(double a, double b);

Note that you don't need the definition -- the one in the header will be used -- but you do need to get the prototype right.

rici
  • 234,347
  • 28
  • 237
  • 341
0

the problem is the compiler is not seeing the function: maxf() at the point where the code is calling that function.

This is due to the inline keyword.

Suggest removing that keyword from the function signature and inserting a prototype that does contain that keyword, Similar to:

inline double maxf( double a, double b);

double maxf( double a, double b)
{
    if (a>b)
        return a;
    return b;
} 
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • but why this happened? why inline causes such a problem? –  Mar 24 '20 at 17:53
  • regarding: *but why this happened? why inline causes such a problem?* I don't know the definitive answer. This is really a question for the language lawyers – user3629249 Mar 24 '20 at 18:03
  • @Daniel (and user3629249): perhaps my answer helps. (And if not, let me know and I will try to improve it.) – rici Mar 24 '20 at 20:01