I agree that this needs to be standardized for vector-ey crates to be able to talk to each other seamlessly. Otherwise we'll end up with a rerun of the C++ strings fiasco, with char* and wchar_t* and std::string and std::wstring and BSTR and _bstr_t and CString and CComBSTR and QString and GString and...
Standardization. Optimal performance. Better Syntax.
People can and do write vector in libraries right now, usually using existing compiler support that gives them guarantees about SIMD.
AKA, its completely possible, and there are quite a number of C++ libraries that make vectors look like native types complete with long lists of global operator overloads for interaction with other base types. Generally these libraries are just thin wrappers around inline assembly or intel intrinsics for SSE/AVX and generally don't bring any form of OO syntax into the picture.
That said, even with C++ the libraries tend to fall down a bit when it comes to individual element operation/masking because the closest method is generally using array syntax for the elements which limits the operations to a single element in the vector at a time. Which means you end up with OO method calling syntax for element masking (or creative solutions with operator() )
(Also, Rust has pretty transparent support in the same manner also without OO overhead, but differs slightly because methods can and often do use pass-by-value. This is the distinction I was drawing.)
OP was specifically recommending operator overloading on a plain old struct, which can be done without declaring a class. Indeed, operator overloads can be declared as global functions in C++. The this pointer doesn't enter in to it at all in that case.
The only "downgrade" I made was saying that it is only "almost" the default in C++, versus the other two where they are completely the default.
To be clear, like C and Rust, C++ has great semantic attributes for this sort of thing:
- classes/structs that don't require allocation/pointers
- precise control over pass-by-value (for everything except the `this` pointer of methods)
- pervasive static dispatch of methods/functions (including operators, which can be considered to be method/function with an unusual name and call syntax)
The only downside, and the reason I said "almost" (which is what the C++ETF (C++ Evangelism Task Force) seems to be up in arms about), is methods are what most people reach for by default and so the `this` pointer comes into play. But as you point out, and as I implied in my original comment, this isn't required, just the default.
I've seen it used for various video/photo processing operations.
[0]: https://play.rust-lang.org/?gist=989fe1c05e3e68df89642032743...
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p019...
If you want to use vectors explicitly, you can do so using intrinsics for most vector architectures.
Assuming that's true, you want to maximise performance by using as much of the parallelism that vectors give you. So if you're dealing with [4 x int32], on a 128 bit vector ISA you would be fully utilising your vector registers, but moving to say AVX-512 you're now only using 1/4 of your potential parallelism.
Your architecture independent vector types would have to target the lowest common denominator, and completely defeat the purpose of vectorisation.
How these map to any particular processors resources is not my problem - though the three major vector extensions today all have 4-element vectors. Some support more, but that's not terribly relevant to the math I want to do. A smart compiler could pack multiple small vectors into a wide vector register just like they try to pack multiple scalars in there today.
I am not interested in vectorizing loops. I want to write code like this:
Vec3double position = {5.8,3.9,2.1};
Vec3double velocity = {1.0,0.0,0.0};
double timestep = 0.01;
position += timestep*velocity;
and so on. Yes, I also want the common use case of multiplication of vector and a scalar to be that easy.
Any paint program or graphics library (including font rendering) does a ton of this stuff. So does every 2d or 3d physics engine. Ray tracing. FEA software. CAD. The list of uses for these vector sizes is long and has nothing to do with auto-vectorizing loops. Of course there are plenty of applications where loop vectorization is valuable and I don't want to take anything away from that. I just want built-in support for these common mathematical entities in the base language.
On ARM you have specialised load and store instructions that can de-interleave into vector registers such that register a contains VecType.x and register b has VecType.y etc, but are a bit slower.
If you don't care about SIMD performance then fair enough, but if you care enough about this issue to want the compiler to generate SIMD instructions, you better be willing to change your code to be performant on your particular target because even small changes can impact whether or not it's worth vectoring vs leaving it as scalar code.
And, that is assuming that the autovectoriser is able to synthesise the desired instructions, e.g. I believe SSE2's packssdw & packusdw ("pack with signed/unsigned saturation") and pmaddwd ("multiply and add packed integers") are useful in a JPEG codec but I find it extremely unlikely that any compiler will autovectorise to them.
Who said anything about doing it safely? You can elide the bounds checks explicitly with calls to get_unchecked (or whatever) using unsafe.
> How would Rust handle SIMD differences when trying to generate specific code as you would in C?
Please be more specific. This question is so broad that it's impossible to answer. At some levels, this is the responsibility of the code generator (i.e., LLVM). At other levels, it's the responsibility of the programmer to write code that checks what the current CPU supports, and then call the correct code. Both Clang and gcc have support for the former using conditional compilation, and both Clang and gcc have support for the latter by annotating specific function definitions with specific target features. In the case of the latter, it can be UB to call those functions on CPUs that don't support those features. (Most often the worse that will happen is a SIGILL, but if you somehow muck of the ABIs between functions, then you're in for some pain.) The plan for Rust is to basically do what Clang does.
The question of safety in Rust and SIMD is a completely different story from auto-vectorization. Figuring out how to make calling arbitrary vendor intrinsics safe is an open question that we probably won't be able to solve in the immediate future, so we'll make it unsafe to call them.
And even that is all completely orthogonal to a nice platform independent SIMD API (like you might find in Javascript's support for SIMD[1]), since most of that surface area is handled by LLVM and we should be able to enable using SIMD at that level in safe Rust.
And all of that is still completely and utterly orthogonal to whether bounds checks are elided. Even with the cross platform abstractions, you still might want to write unsafe code to elide bounds checks when copying data from a slice into a vector in a tight loop.
[1] - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Bounds checks can be eliminated and code can be vectorized if the optimizer can prove it; explicit SIMD is useful for the cases where it can't.