0

C++ allows aggressive optimization with arithmetic math expressions for the standard data types (builtin integer and floating point types). In these cases, while adhering to the C++ standard, the compiler can precompute literal constants, reorder operations, even change the operations completely, etc. (and in some cases even deviate from the standard compliance, as what happens with the -Ofast optimization level in some compilers, for example).

But now let's suppose you write your custom classes library for scalars and you implement the arithmetic operators for them, and even your own user-defined literals for constants definition.

Does the C++ specification provide some mechanism for achieving in the operators of your own classes the same optimization chances as for the builtin integer and floating point types?

Imagine for example you have the following:

#include <cstdint>

class MyFP16
{
private:
    std::uint16_t m_val;

public:

    MyFP16();
    [...other constructors here...]
    ~MyFP16();

    // Arithmetic operators
    friend MyFP16 operator+(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator-(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator*(const MyFP16 &c1, const MyFP16 &c2);
    friend MyFP16 operator/(const MyFP16 &c1, const MyFP16 &c2);

    [...rest of arithmetic operators...]

    // Other logic needed
    [...]
};

Can I define this class in a way that all operators have exactly the same semantics as in the float builtin type, so that all the arithmetic expressions optimizations that can be used for float can be used also for my class, taking advantage of reordering operations, commutativity/associativity, transforming some operations into others, precomputing constants results, etc...? How?

Thanks a lot!

cesss
  • 852
  • 1
  • 6
  • 15
  • you could try to see what the compiler does. Without your code it is unclear how others should do that for you – 463035818_is_not_an_ai Apr 12 '20 at 16:54
  • 3
    maybe you expect some general answer, but there are no general answers when it comes to compiler optimizations. The only way to know is to write the code, compile it and look at the output of the compiler. In case you dont know it, this is an awesome tool to do exactly that: https://godbolt.org/ – 463035818_is_not_an_ai Apr 12 '20 at 16:55
  • btw concerning your last sentence, you should take a look at `constexpr` functions. It seems like that is what you are looking for – 463035818_is_not_an_ai Apr 12 '20 at 17:01
  • I have added some certain examples for the kind of classes I have on mind. In other words, I'm thinking on scalar values, as you can see. No matrices nor vectors, just scalars. Can the compiler manage my scalars with the same optimizations as the builtin types? – cesss Apr 12 '20 at 17:43
  • your examples are still too vague. As already mentioned, details do matter. You need some code and see what the compiler does to know what the compiler does to a particular piece of code – 463035818_is_not_an_ai Apr 12 '20 at 17:46
  • @idclev463035818 Too vague? I'm precisely asking before writing the classes because arithmetic expression optimization is an important topic for me, so I need to know how to achieve it *before* writing the classes. Anyway, if you really want code, take a look at the 16 bit fp half library: https://sourceforge.net/p/half/code/HEAD/tree/trunk/include/half.hpp (disclaimer: no, I'm not the half library author, but it's exactly the same kind of class I'm thinking in) – cesss Apr 12 '20 at 17:52
  • 1
    well if you have code you can compile it and see if the compiler does apply the optimizations you expect. – 463035818_is_not_an_ai Apr 12 '20 at 17:55
  • @walnut But the compiler does know that `+` is commutative for the builtin integer and fp types, so... can you tell the compiler that `+` is commutative for your classes? How? – cesss Apr 12 '20 at 17:59
  • 1
    @cesss No, you cannot tell the compiler about mathematical properties of your operators, except maybe if your compiler supports such specific attributes. I don't think they usually do. – walnut Apr 12 '20 at 18:00
  • I'm voting to close this question as off-topic because it is about compilers, not C++. – 2785528 Apr 12 '20 at 18:40
  • 1
    @2785528 Please argue how on earth C++ operator semantics belong to compilers and not to C++. This question is not about compilers, but about defining operators with the same semantics as builtin types so that these semantics can be used for optimization. I'll edit the question to remove the "compiler" word. – cesss Apr 12 '20 at 18:53
  • Thanks a lot for reopening it! I have reworded the question, making it clearer I'm interested in how to achieve this from the C++ language rather than being interested on the compilers part. – cesss Apr 13 '20 at 11:29

1 Answers1

1

There is nothing special about operator overloads or user-defined literals.

If you want to give the compiler the best opportunity to optimize for speed, possibly paying with longer compilation times and in some situations larger binary sizes, then you should follow the usual rules that apply to all functions.

Write small functions that would benefit from inlining as inline functions in the header file, so that the compiler can always decide to inline if appropriate.

If a small function is inlined, then you are back to expressions which operate on the primitive types and the compiler can use the same optimization methods that you have already mentioned.

If the compiler does not decide to inline a call, then it will probably make no optimizations based on mathematical properties of the operators, because it cannot assume that an operator overload (or any function) is e.g. commutative or associative, except maybe in very simple cases (which would probably be inlined anyway).

Also make sure that you mark every function that you are allowed to mark constexpr as such, so that it becomes easier to do compile-time evaluation and write code in a constexpr-friendly way (i.e. use only literal types).

If you are not happy enough with optimizations that the compiler does, then a common method is to use the expression templates method to collect expression trees at compile-time and do your own transformations on them.

walnut
  • 21,629
  • 4
  • 23
  • 59
  • Yes, inlining would be a must-have optimization in the examples I wrote, but it's independent from arithmetic optimizations. From your last comment below my question, I'm assuming that the answer is that it's not possible to optimize arithmetic expressions unless you use builtin C++ types. If affirmative, this is a big bummer... – cesss Apr 12 '20 at 18:05
  • I didn't know about expression templates. They look like a complex subject at first glance, but I'll study them in more depth. Anyway, they seem to be independent from the compiler optimization level... the transformations will be done on the expressions no matter the optimization level. It's very unfortunate that you cannot tell the compiler to treat a class with the same operator semantics as a builtin type :-( – cesss Apr 12 '20 at 18:17
  • I have reworded the original question, in case it matters. The question is the same, but I hope it's now better explained. – cesss Apr 13 '20 at 11:30