andyferris
6 hours ago
Regarding autovectorization:
> The other drawback of this method is that the optimizer won’t even touch anything involving floats (f32 and f64 types). It’s not permitted to change any observable outputs of the program, and reordering float operations may alter the result due to precision loss. (There is a way to tell the compiler not to worry about precision loss, but it’s currently nightly-only).
Ah - this makes a lot of sense. I've had zero trouble getting excellent performance out of Julia using autovectorization (from LLVM) so I was wondering why this was such a "thing" in Rust. I wonder if that nightly feature is a per-crate setting or what?
queuebert
11 minutes ago
Does Rust not have the equivalent of GCC's "-ffast-math"?
Arch-TK
6 hours ago
It's not something you seem to be able to just enable globally. From what I gather this is what is being referenced:
https://doc.rust-lang.org/std/intrinsics/index.html
Specifically the *_fast intrinsics.
vlovich123
5 hours ago
Does Julia ignore the problem of floating point not being associative, commutative nor distributive?
The reason it’s a thing is from LLVM and I’m not sure you can “language design” your way out of this problem as it seems intrinsic to IEEE 754.
ChrisRackauckas
4 hours ago
No it only uses the same LLVM compiler passes and you enable certain optimizations locally via macros if you want to allow reordering in a given expression.
tomsmeding
5 hours ago
Nitpick, but IEEE float operations are commutative (when relevant and appropriate). Associative and distributive they indeed are not.
vlovich123
5 hours ago
Unless I’m having a brain fart it’s not commutative or you mean something by “relevant and appropriate” that I’m not understanding.
a+b+c != c+b+a
That’s why you need techniques like Kahan summation.
amluto
2 hours ago
I think the other replies are overcomplicating this.
+ is a binary operation, and a+b+c can’t be interpreted without knowing whether one treats + as left-associative or right-associative. Let’s assume the former: a+b+c really means (a+b)+c.
If + is commutative, you can turn (a+b)+c into (b+a)+c or c+(a+b) or (commuting twice) c+(b+a).
But that last expression is not the same thing as (c+b)+a. Getting there requires associativity, and floating point addition is not associative.
wfleming
4 hours ago
We’re in very nitpicky terminology weeds here (and I’m not the person you’re replying to), but my understanding is “commutative” is specifically about reordering operands of one binary op (4+3 == 3+4), while “associative” is about reordering a longer chain of the same operation (1+2+3 == 1+3+2).
Edit: Wikipedia actually says associativity is definitionally about changing parens[0]. Mostly amounts to the same thing for standard arithmetic operators, but it’s an interesting distinction.
nyrikki
4 hours ago
It is not a nit it is fundamental, a•b•c is associativity, specifically operator associativity.
Rounding and eventual underflow in IEEE means an expression X•Y for any algebraic operation • produces, if finite, a result (X•Y)·( 1 + ß ) + µ where |µ| cannot exceed half the smallest gap between numbers in the destination’s format, and |ß| < 2^-N , and ß·µ = 0 . ( µ ≠ 0 only when Underflow occurs.)
And yes that is a binary relation only
a•b•c is really (a•b)•c assuming left operator associativity, one of the properties that IEEE doesn't have.
wtallis
5 hours ago
"a+b+c" doesn't describe a unique evaluation order. You need some parentheses to disambiguate which changes are due to associativity vs commutativity. a+(b+c)=(c+b)+a should be true of floating point numbers, due to commutativity. a+(b+c)=(a+b)+c may fail due to the lack of associativity.
adastra22
4 hours ago
It is not, due to precision. Consider a=1.00000, b=-0.99999, and c=0.00000582618.
jcranmer
4 hours ago
No, the two evaluations will give you exactly the same result: https://play.rust-lang.org/?version=stable&mode=debug&editio...
IEEE 754 operations are nonassociative, but they are commutative (at least if you ignore the effect of NaN payloads).
zygentoma
4 hours ago
You still need to specify an evaluation order …
immibis
4 hours ago
Does (1.00000+-0.99999)+0.00000582618 != 0.00000582618+(-0.99999+1.00000) ? This would disprove commutativity. But I think they're equal.
dataangel
an hour ago
For those to be equal you need both associativity and commutativity.
Commutativity says that a*b = b*a, but that's not enough to allow arbitrary reordering. When you write a*b*c depending on whether * is left or right associative that either means a*(b*c) or (a*b)*c. If those are equal we say the operation is associative. You need both to allow arbitrary reordering. If an operation is only commutative you can turn a*(b*c) into a*(c*b) or (b*c)*a but there is no way to put a in the middle.
nyrikki
4 hours ago
IEEE 754 floating-point addition and multiplication are commutative in practice, even if there are exceptions with NaNs etc..
But remember that commutative is on the operations (+,x) which are binary operations, a+b=b+a and ab=ba, you can get accumulated rounding errors on iterated forms of those binary operations.
bobmcnamara
5 hours ago
We used to tweak our scalar product simulator code to match the SIMD arithmetic order so we could hash the outputs for tests.
I wonder if it could autovec the simd-ordered code.
dzaima
4 hours ago
For vectorizing, that quote is only true for loops with dependencies between iterations, e.g. summing a list of numbers (..that's basically the only case where this really matters).
For loops without such dependencies Rust should autovectorize just fine as with any other element type.
galangalalgol
an hour ago
You just create f32x4 types, the wide crate does this. Then it autovectorizes just fine. But it still isn't the best idea if you are comparing values. We had a defect due to this recently.
the__alchemist
a minute ago
I suspect I am misunderstanding. If you create an f32x4 type, aren't you manually vectorizing? Auto-vectoring is magic SIMD use the compiler does in some cases. (But usually doesn't...)