Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have "constexpr and runtime" alias

Tags:

Constexpr can be awsome and useful for compilation optimisation. For example...

strlen(char*) 

Can be precompiled using....

constexpr inline size_t strlen_constexpr(char* baseChar) {     return (             ( baseChar[0] == 0 )             ?(// if {               0               )// }             :(// else {               strlen_constexpr( baseChar+1 ) + 1                )// }             ); } 

Which gives it a runtime cost of "0" when optimised... But is more than 10+x slower on runtime

// Test results ran on a 2010 macbook air --------- strlen --------- Time took for 100,000 runs:1054us. Avg Time took for 1 run: 0.01054us. --------- strlen_constexpr --------- Time took for 100,000 runs:19098us. Avg Time took for 1 run: 0.19098us. 

Are there any existing macro / template hack where a single unified function can be used instead. ie.

constexpr size_t strlen_smart(char* baseChar) {     #if constexpr     ... constexpr function     #else its runtime     ... runtime function } 

Or some overloading hack that would allow the following

constexpr size_t strlen_smart(char* baseChar) {     ... constexpr function }  inline size_t strlen_smart(char* baseChar) {     ... runtime function } 

Note: This question applies to the concept in general. Of having 2 separate functions for runtime and constexpr instead of the example functions given.

Disclaimer: Setting the compiler to -O3 (optimization level) is more than enough to fix 99.9% of static char optimizations making all the examples above "pointless". But that's beside the point of this question, as it applies to other "examples", and not just strlen.

like image 839
PicoCreator Avatar asked Jan 03 '14 10:01

PicoCreator


People also ask

Is constexpr a runtime?

Constant expression, constexpr , code in C++ aims to move non-changing repetitive computations at runtime to compile time. For example, you can write a function that calculates π² at compile time, so, whenever you run the program, π² value is already there. constexpr functions are also allowed to be called at runtime.

Should I use constexpr everywhere?

Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.

Does constexpr improve performance?

Understanding constexpr Specifier in C++ constexpr is a feature added in C++ 11. The main idea is a performance improvement of programs by doing computations at compile time rather than run time.

Is constexpr always evaluated at compile time?

constexpr functions will be evaluated at compile time when all its arguments are constant expressions and the result is used in a constant expression as well.


1 Answers

I don't know any generic way, but I know two specific cases where it is possible.

Specific case of some compilers

Also gcc, and clang which copies all features of gcc, have a built-in function __builtin_constant_p. I am not sure whether gcc will correctly see argument to inline function as constant, but I fear you'd have to use it from a macro:

#define strlen_smart(s) \     (__builtin_constant_p(s) && __builtin_constant_p(*s) ? \         strlen_constexpr(s) : \         strlen(s)) 

Might be of use. Note that I am testing both s and *s for constexpr, because pointer to static buffer is a compile time constant while it's length is not.

Bonus: Specific case of literals (not an actual answer)

For the specific cast of strlen you can use the fact that string literals are not of type const char * but of type const char[N] that implicitly converts to const char *. But it also converts to const char (&)[N] as well while const char * does not.

So you can define:

template <size_t N> constexpr size_t strlen_smart(const char (&array)[N]) 

(plus obviously strlen_smart on const char * forwards to strlen)

I've sometimes used function with this type of argument even in C++98 with definition corresponding to (I didn't try to overload strlen itself, but the overloads were so I could avoid calling it):

template <size_t N> size_t strlen_smart(const char (&)[N]) { return N - 1; } 

This has the problem that for

char buffer[10] = { 0 };  strlen_smart(buffer); 

should say 0, but that optimized variant just says 9. The functions don't make sense to be called on buffers like that so I didn't care.

like image 139
Jan Hudec Avatar answered Oct 04 '22 06:10

Jan Hudec