Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance with value semantics

Tags:

c++

c++11

move

I am very concerned about performance and readability of the code and I get most of my ideas from Chandler Carruth from Google. I would like to apply the following rules for C++ for clean code without loosing performance at all.

  • Pass all Builtin types as values
  • Pass all objects that you don't want to mutate by const reference
  • Pass all objects that your function needs to consume by value
  • Ban everything else. When in corner cases, pass a pointer.

That way, functions don't have side effects. That's a must for code readability and makes C++ kind of functionnal. Now comes performance. What can you do if you want to write a function that adds 1 to every element of a std::vector? Here is my solution.

std::vector<int> add_one(std::vector<int> v) {
    for (std::size_t k = 0; k < v.size(); ++k) {
        v[k] += 1;
    }
    return v;
}

...
v = add_one(std::move(v));
...

I find this very elegant and makes only 2 moves. Here are my questions:

  • Is it legal C++11 ?
  • Do you think of any drawback with this design?
  • Couldn't a compiler automatically transform v = f(v) into this? A kind of copy elision.

PS: People ask me why I don't like to pass by reference. I have 2 arguments:

1 - It's does not make explicit at the call site which argument might get mutated.

2 - It sometimes kill performance. References and pointers are hell for compiler because of aliasing. Let's take the following code

std::array<double, 2> fval(const std::array<double, 2>& v) {
    std::array<double, 2> ans;
    ans[0] = cos(v[0] + v[1]);
    ans[1] = sin(v[0] + v[1]);
    return ans;
}

The same code that takes ans as a reference is 2 times slower:

std::array<double, 2> fref(const std::array<double, 2>& v,
        std::array<double, 2>& ans) {
    ans[0] = cos(v[0] + v[1]);
    ans[1] = sin(v[0] + v[1]);
}

Pointer aliasing prevents the compiler from computing sin and cos with a single machine instruction (Gcc does not currently do that optimization, but icpc makes the optimization with value semantics).

like image 978
InsideLoop Avatar asked Apr 28 '15 19:04

InsideLoop


People also ask

What is value type semantics?

In computer science, having value semantics (also value-type semantics or copy-by-value semantics) means for an object that only its value counts, not its identity.

What is value semantics C++?

In C++, value semantics is the default, which means that when you pass an instance of a class or struct, it behaves in the same way as passing an int , float , or any other fundamental type. To use reference semantics, we need to explicitly use references or pointers.

What is value semantics Swift?

The Swift language ecosystem is built around the concept of value types for data structures. This provides significant benefits when developers reason about the behavior of their code, particularly in the multi-threaded environments required for high-performance software.

What are reference semantics?

Reference semantics (objects) — reference semantics: Behavior where variables actually. store the address of an object in memory. — When one variable is assigned to another, the object is. not copied; both variables refer to the same object.


1 Answers

It appears to be legal C++11 to me. Drawbacks may possibly be opinion-based so I won't address that, but as for your third point, the compiler could only do that transformation if it can prove that the function doesn't also alias v in any way. As such compiler writers may not have elected to implement such an optimization for the simple cases they can analyze and leave that burden (no-aliasing) on the programmer.

However also consider: When the function resists being written in a clear version with obvious performance characteristics perhaps you're writing the wrong function. Instead write an algorithm that operates on a range like the standard library and the problem goes away:

template <typename Iterator>
void add_one(Iterator first, Iterator last)
{
    for(; first != last; ++first)
    {
        (*first) += 1;
    }
}
like image 142
Mark B Avatar answered Oct 02 '22 06:10

Mark B