4 ms·
We now have return std::reduce(x.begin(), x.end()); Which is a little cleaner and is even faster (compiler is free to do the additions in any order). h
by a_t48 2y ago
We now have
return std::reduce(x.begin(), x.end());
Which is a little cleaner and is even faster (compiler is free to do the additions in any order). https://en.cppreference.com/w/cpp/algorithm/reduce https://en.cppreference.com/w/cpp/algorithm/reduce - it looks like even the `accumulate` example can be made simpler with `std::plus`. I prefer the `reduce` option for a number of reasons, but understand why someone might not.
- autoexecbat 2y agoI think most people would be thinking "hmm now I need to read what 'reduce' mean in this context"
- a_t48 2y agoFor at least the first time, yeah.
- CuriousCosmic 2y agoAt least in my experience in the C++ world there is a general expectation that you learn the "standard algorithms" and prefer them over the alternatives unless you have a specific reason not to. For example: outside the C++ ecosystem, most people probably would stare blank faced if they saw `std::rotate` in a codebase but it's basically a meme at this point in the C++ space. Ex: https://www.youtube.com/watch?v=UZmeDQL4LaE https://www.youtube.com/watch?v=UZmeDQL4LaE
- j1elo 2y agoLook at that link: 2,4,6) ... These overloads participate in overload resolution only if std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> is true. std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true. std::is_execution_policy_v? std::decay_t? std::remove_cvref_t? Really, this madness with C++ needs to stop. People who work on newer versions of the language might be very conservative with the language syntax itself, but they clearly decided having carte blanche to adding an infinite amount of stuff to the standard library. Could someone please force them to stop, somehow? The whole rest of the industry would be thankful.
- lupire 2y agoThat's terrible! The main operation, addition, is completely hidden magic! But the completely trivial calls to .begin() and .end() are explicit! Why would I want to add by default?
- psychoslave 2y agoMaybe the idea is that you overload your favored default as a + operator? Even more baffling certainly, but at this point I wouldn’t even be surprised this was considered a compelling argument in the mind of whoever settled this API choice. :D
- constantcrying 2y agoWhat is hidden? It's a reduce on + starting with 0, hardly anything subtle.
- jcelerier 2y ago> The main operation, addition, is completely hidden magic the main operation is reduction which is fairly basic computer science and taught in any non-BS curriculum - https://en.wikipedia.org/wiki/Reduction_operator https://en.wikipedia.org/wiki/Reduction_operator (or https://en.wikipedia.org/wiki/Fold_(higher-order_function) https://en.wikipedia.org/wiki/Fold_(higher-order_function) which is the standard way to introduce this concept ; check in particular the table showcasing all the implementations in various languages). The standard reduction for a set of numbers to a number that is likely going to be the very first example in your textbook is addition.
- Dylan16807 2y agoThe reduction is explicit. That's not what the complaint is about. And it's more about implementation than the actual result. The actual operation is addition. I don't care if it's the first example, that doesn't mean you should hide it. Imagine if print did hello world by default.
- o11c 2y agostd::ranges::fold_left (since C++23) can take a range directly instead of a separated pair of iterators, and requires the explicit add.
- floxy 2y ago>the `accumulate` example can be made simpler with `std::plus` Accumulate, surprisingly enough, accumulates by default: return accumulate(x.begin(), x.end(), 0);
- MathMonkeyMan 2y agoJust make sure you're accumulating integers and not doubles!
- floxy 2y ago#include<cstdio> #include<numeric> #include<vector> using namespace std; template <class T, template<class> class Cont> T sum(Cont<T> x) { return accumulate(x.begin(), x.end(), (T)0); } int main(int argc, char *argv[]) { vector<double> ds({1.1, 2.2, 3.3, 4.4}); vector<float> fs({1.1f, 2.2f, 3.3f, 4.4f}); vector<int> is({1, 2, 3, 4}); vector<unsigned long long> ulls({1ul, 2ul, 3ul, 4ul}); printf("doubles: %f\n",sum(ds)); printf("floats: %f\n",sum(fs)); printf("ints: %d\n",sum(is)); printf("ulls: %llu\n",sum(ulls)); return 0; }
- a_t48 2y agoObviously, you have to use std::accumulate(v.cbegin(), v.cend(), decltype(*v.cbegin()){}); :) :) :)
- vlovich123 2y ago> and is even faster (compiler is free to do the additions in any order) Is that actually true? I'm not even sure how hypothetically removing ordering requirements would help you extract performance, let alone any compilers that could do anything with that today. Unless the standard library were to auto-parallelize the reduction, but I doubt they'd do that because the overhead of starting threads would be quite costly for anything but the absolute largest ranges since C++ doesn't have a thread pool sitting idly for you (not to mention that the docs for the function don't mention any thread safety requirements for the BinaryOp and Init which would be required for any such optimization).
- im3w1l 2y agoI think relaxing the ordering requirement let's you use simd something like this (semi-pseudo code) (a, b, c, d) = (0, 0, 0, 0); for(int i = 0; i < n; i += 4) { (a, b, c, d) += (arr[i], arr[i+1], arr[i+2], arr[i+3]); } return a + b + c + d;
- mjcohen 2y agoWhat if n is not a multiple of 4? There is a trick I can't recall at the moment for handling this with a case statement on n mod 4 at the end to wrap this up.
- jbboehr 2y agoI assume you're thinking of: https://en.wikipedia.org/wiki/Duff%27s_device https://en.wikipedia.org/wiki/Duff%27s_device
- vlovich123 2y agoAre you sure that the compiler doesn’t autovectorize a simple loop into this form anyway? This is kind of the defacto scenario for that pass.
- im3w1l 2y agoThis transformation is only legal if order of summation doesn't matter. So the compiler can do it for ints, but not floats.