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.
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:
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).
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.
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.
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.
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With