-
Notifications
You must be signed in to change notification settings - Fork 17.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: Go 2: component-wise operators for fixed size arrays #65216
Comments
Please fill out https://github.com/golang/proposal/blob/master/go2-language-changes.md when proposing language changes |
Experienced, have been working with Go since 2014.
C, Lua, Python, Javascript
I believe this would make Go easier to learn, often developers like to learn a language by creating graphics, or solving mathematial equations. For example, in order to animate something on a screen, or in a 3D space, Go code typically uses special types with defined methods or functions to represent this. The names of these methods aren't usually consistent, there may be methods defined on the types themselves, or top level functions. There isn't any idiomatic standard here.
I suspect this form of method calling may be confusing, intimidating or off-putting for somebody learning the language, who expects a standard way to perform basic operations. ie.
@ianlancetaylor briefly touched on the idea when addressing #35307
There are also a few proposals about SIMD and operator overloading, this proposal differs by being much simpler, yet addressing the underlying needs.
Graphics, Science and Maching Learning all involve vector operations, this proposal helps developers working in these fields use Go to more effectively represent their work.
yes.
This proposal enables operators to operate on two differently typed values, which introduces complexity. Component-wise division requires a preceding check for division by zero, when checking for large arrays (as the divisor) this can be a significant runtime cost.
An implementation may start with a simple loop, that applies an operator for each element in the array(s). Later implementations may leverage SIMD instructions, or if the array is large enough, multiple threads. ( other questions are addressed in the proposal) |
Your proposal implies that |
@rittneje no that was in error, I have resolved the example. |
Out of curiosity, what would be the challenges in supporting flexible arrays / slices too ? One thing came to my mind is lack of a proper error propagation mechanism when the length of operands don't match. |
I'm not in the SIMD world, but don't people want the dot product and cross product fairly regularly, not just a pairwise operator? |
ISTM, the point here is to hint to the compiler what CPU instruction to use, so an explicit length is required. If you have a slice, you can autoconvert it to an array in recent versions of Go with |
If this proposal was accepted and operations on arrays that are dependent on their element types became a normal part of the language, it could open the door to built-in functions to handle those more specialized cases, too, perhaps. |
If performance is the only consideration goal here it is easy for an experienced compiler engineer to teach the compiler to understand: a = [...]T{
a[0] + b[0],
a[1] + b[1],
a[2] + b[2],
a[3] + b[3],
} should be done in SIMD land. Which does not involve any new language concept (you would hide this in a pretty inlined function if this actually compiled to SIMD instructions). From purely a language standpoint I find My example above is self descriptive, a new programmer might not understand why the code is written that way but they can read out what is actually happening. The fact each element operates individually on the matching index is weird if you don't already know SIMD is a thing. The kind of code this would incentivize people to write also would look bad: // assume there is a constant named vectorSize which varies based on build tags and architectures
func AddPairwise(dst []int, x []int, y []int) {
_ = x[:len(dst)]
_ = y[:len(dst)]
var i uint
for ; i < len(dst)-len(dst)%vectorSize; i += vectorSize {
*(*[vectorSize]int)(dst[i:]) = [vectorSize]int(x[i:]) + [vectorSize]int(y[i:])
}
for ; i < len(dst); i++ {
dst[i] = x[i] + y[i]
}
} needless to say this is tricky code and easy to get wrong. This is a close to a best case for SIMD, real world use-cases get much worst fast. |
I don't see as much of a practical benefit to this, with fixed size arrays, you are most likely attemping to clearly represent an inline vector equation, most of the time with arrays of length 2, 3 or 4. With slices, I imagine you're more likely to be working with very large datasets, they could be different sizes and using a function/method to perform these 'heavier' operations makes sense. Do you have any real-world use-cases or fields, where you think this proposal would also make sense for slices?
Are you suggesting there is a widespread need to write such functions to add large slices together efficiently?
This is the standard way to add and subtract vectors. I mean you could certainly restrict this proposal to only work on arrays with numerical elements and limit the operators to Note that languages designed to run on a GPU also do component-wise operations on their vector types. I'm not sure Python is a good example, as it supports operator overloading, so it's not entirely clear what an operator will do in the first place! |
I no longer think that this is a good approach for SIMD. The problem is that we can only use hardware support if the arrays in question are exactly the right size with exactly the right types. The effect is that for any given program whether you get hardware SIMD or not depends on the target (and on the compiler's capabilities). Few people want to write code that may or may not get hardware acceleration. Most people want to write code that has predictable performance. This issue doesn't support that. As a language change separate from hardware support, it is unconvincing. Arrays are not widely used in Go, and array operations don't always break down naturally into the forms provided here (for example, this doesn't support dot product, as mentioned earlier). Therefore, this is a likely decline. Leaving open for four weeks for final comments. |
No change in consensus. |
Could be an indication that arrays are missing something? Looking at the newer proposal specifically about adding a SIMD package. The syntax there is incredibly unpleasant. As demonstrated in the proposal, Go packages that simply serve to wrap arithmetic operations always run into a problem of poor readability. It's a big reason why having complex types is so convenient and perhaps a factor as to why @robpike opened #19623 I'm not sure why all the responses to my proposal here have been hyperfocused on SIMD, with little to no discussion about the language change. I didn't open this proposal because I needed SIMD.
Dot products are typically represented as functions, as they are not a basic arithmetic operation. |
Proposal Details
Consider an array
[n]T
whereop
is an operator supported by typeT
. The following operations are proposed:[n]T op T
, which applies an operator to each element in the array, producing a resulting array.[n]T op [n]T
, which applies an operator to the corresponding element in both arrays, producing a resulting array.Here's an example where
n
is3
,T
isfloat64
andop
is+
:Such operations may be optimised to run in parallel, by using CPU features, such as SIMD.
This proposal aims to address any ongoing requests for "SIMD intrinsics" or "operator overloading" and enable vector math to be more readable.
The text was updated successfully, but these errors were encountered: