Live data from Hacker News

Trust your compiler: Modern C++

categorica.io

31–40 of 47 posts

Re: Trust your compiler: Modern C++

#31
post #3

Trust the compiler - sure - but we can't change the whole program by using -ffast-math, unfortunately, so that particular one is out.

I like the Rust approach of adding operations like `algebraic_add` instead of supporting a compiler flag. This avoids undefined behaviour and keeps the complications from optimizations localized to code using these. https://doc.rust-lang.org/std/primitive.f32.html#algebraic-o... > Algebraic operators of the form a.algebraic_*(b) allow the compiler to optimize floating point operations using all the usual algebraic pr…

I appreciate the semantics and locality of that, too. When you glance at it, you understand that specific tradeoffs are happening right here, and here only, without some CLI arg changing them for the entire program. It’s kinda like unsafe, but for math.

Re: Trust your compiler: Modern C++

#32
post #9

Every time I see "use ranges and algorithms!" examples, I am baffled that apparently, I am supposed to find inline double algorithm_call(std::span xs) noexcept { return std::accumulate( xs.begin(), xs.end(), 0.0, [](double acc, double volts) { auto mv = calibrated_mv(volts); auto err = residual(mv); return weighted_square(err) + acc; }); } more readable, concise, and easier on my eyes than inline double raw_loop(std:…

You said "ranges and algorithms", but you didn't copy the third function which actually uses library.

inline double ranges_pipeline(std::span xs) noexcept { auto costs = xs | std::views::transform(calibrated_mv) | std::views::transform(residual) | std::views::transform(weighted_square);

  return std::ranges::fold_left(costs, 0.0, std::plus{});
}

It's still a bit verbose, because C++ doesn't allow universal function call syntax. It will be even more concise in other languages like D.

Re: Trust your compiler: Modern C++

#33
post #28
post #18

Earlier quoted context omitted.

> even the best compilers don’t use a third of the opcodes our modern CPUs boast of That’s not necessarily an indication of the weakness of compilers. It also could be an indication that hardware designers could leave out instructions. X86, in particular, will have lots of them for backwards compatibility reasons (extreme example: the old 80-bit x87 FP stack) There also are instructions that are expected to never get…

x87 support may not be the most obscure part of the instruction set. Ther is also hardware support for BCD math in 16 bit amd 32 bit mode. Who uses that anymore?

Unfortunately some exchanges (twse) uses packed BCD encoding.

Re: Trust your compiler: Modern C++

#34
post #9

Every time I see "use ranges and algorithms!" examples, I am baffled that apparently, I am supposed to find inline double algorithm_call(std::span xs) noexcept { return std::accumulate( xs.begin(), xs.end(), 0.0, [](double acc, double volts) { auto mv = calibrated_mv(volts); auto err = residual(mv); return weighted_square(err) + acc; }); } more readable, concise, and easier on my eyes than inline double raw_loop(std:…

You said "ranges and algorithms", but you didn't copy the third function which actually uses library. inline double ranges_pipeline(std::span xs) noexcept { auto costs = xs | std::views::transform(calibrated_mv) | std::views::transform(residual) | std::views::transform(weighted_square); return std::ranges::fold_left(costs, 0.0, std::plus {}); } It's still a bit verbose, because C++ doesn't allow universal function ca…

That version was so much more opaque that I didn't bother copying that. Again, I'm not entirely sure why people are so enamored with splitting iteration itself from the contents of one iteration step, especially since the loops are language built-ins.

Re: Trust your compiler: Modern C++

#35
post #10
post #9

Every time I see "use ranges and algorithms!" examples, I am baffled that apparently, I am supposed to find inline double algorithm_call(std::span xs) noexcept { return std::accumulate( xs.begin(), xs.end(), 0.0, [](double acc, double volts) { auto mv = calibrated_mv(volts); auto err = residual(mv); return weighted_square(err) + acc; }); } more readable, concise, and easier on my eyes than inline double raw_loop(std:…

The first form is easier to send to 32 beefy cores or 1024 small CPUs or a Beowulf cluster or a GPU or people sitting in a room.

It's been 15 years since I've last touched OpenMP, but the second form is trivially parallelizable as well. Besides, this parallelization can only ever properly work with arrays/vectors or, at the very worst, std::deque as its usually implemented (a vector of fixed-length arrays), not with e.g. linked lists or red-black trees, so why even bother with generic spans and algorithms?

Re: Trust your compiler: Modern C++

#36
post #23

Earlier quoted context omitted.

`std::accumulate` is defined to have sequential semantics, so the analysis required to make it parallel is probably not that different than starting from the loop version. I guess you could have an alternate `accumulate_associative` that uses the same interface but assumes the reduction is associative and has unspecified evaluation order?

C++ has std::reduce for that, which is std::accumulate except it's defined to operate without any specific ordering.

And now you should probably also stop and consider whether adding elements one-by-one as opposed to recursively adding together sums of smaller subarrays has better or worse numerical behaviour in regards to e.g. rounding and stability.

Re: Trust your compiler: Modern C++

#37
post #30

Don't trust your compiler. Your code is only fast if you're lucky. https://tiki.li/blog/lucky_code.html

I agree you can't trust your compiler, but you can control its behavior more reliably with __builtin_expect_with_probability

https://github.com/protocolbuffers/protobuf/commit/9f29f02a3...

Re: Trust your compiler: Modern C++

#38
post #10
post #9

Every time I see "use ranges and algorithms!" examples, I am baffled that apparently, I am supposed to find inline double algorithm_call(std::span xs) noexcept { return std::accumulate( xs.begin(), xs.end(), 0.0, [](double acc, double volts) { auto mv = calibrated_mv(volts); auto err = residual(mv); return weighted_square(err) + acc; }); } more readable, concise, and easier on my eyes than inline double raw_loop(std:…

The first form is easier to send to 32 beefy cores or 1024 small CPUs or a Beowulf cluster or a GPU or people sitting in a room.

For compilation?
Post reply on HN