Skip to content
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

Closed
Splizard opened this issue Jan 22, 2024 · 13 comments
Closed

proposal: Go 2: component-wise operators for fixed size arrays #65216

Splizard opened this issue Jan 22, 2024 · 13 comments
Labels
LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Milestone

Comments

@Splizard
Copy link

Splizard commented Jan 22, 2024

Proposal Details

Consider an array [n]T where op is an operator supported by type T. The following operations are proposed:

  1. [n]T op T, which applies an operator to each element in the array, producing a resulting array.
  2. [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 is 3, T is float64 and op is +:

var a = [3]float64{1, 2, 3}
var b = [3]float64{3, 2, 1}

fmt.Println(a + b) // [4 4 4]

a += 1 

fmt.Println(a) // [2 3 4]

fmt.Println(-a) // [-2 -3 -4]

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.

@gopherbot gopherbot added this to the Proposal milestone Jan 22, 2024
@Splizard Splizard changed the title proposal: component-wise arithmetic operators for fixed size arrays proposal: component-wise operators for fixed size arrays Jan 22, 2024
@seankhliao
Copy link
Member

Please fill out https://github.com/golang/proposal/blob/master/go2-language-changes.md when proposing language changes

@seankhliao seankhliao added LanguageChange Suggested changes to the Go language v2 An incompatible library change labels Jan 22, 2024
@Splizard
Copy link
Author

Splizard commented Jan 22, 2024

Would you consider yourself a novice, intermediate, or experienced Go programmer?

Experienced, have been working with Go since 2014.

What other languages do you have experience with?

C, Lua, Python, Javascript

Would this change make Go easier or harder to learn, and why?

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.

position = position.Add(direction.ScaledBy(speed*time)) // methods
position = vectors.Sum(position, vectors.MultiplyScalar(direction, speed*time)) // package 'vectors'

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.

position = position + (direction*speed*time)

Has this idea, or one like it, been proposed before?

@ianlancetaylor briefly touched on the idea when addressing #35307

It might be simpler to just permit some binary operations on slice and array types. That would make it easier for the compiler to vectorize those operations when possible. Though it would have other drawbacks, as the time required for the operation would depend on the length of the slice.
If the desire is to be sure that operations use vector instructions, then I don't think this is the right approach. We need something simpler.

There are also a few proposals about SIMD and operator overloading, this proposal differs by being much simpler, yet addressing the underlying needs.

Who does this proposal help, and why?

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.

Is this change backward compatible?

yes.

What is the cost of this proposal?

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.

Can you describe a possible implementation?

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)

@rittneje
Copy link

Your proposal implies that a + b modifies a, which is different than any other use of + today. Is that intentional?

@Splizard
Copy link
Author

Splizard commented Jan 23, 2024

@rittneje no that was in error, I have resolved the example.

@seankhliao seankhliao changed the title proposal: component-wise operators for fixed size arrays proposal: Go 2: component-wise operators for fixed size arrays Jan 23, 2024
@psnilesh
Copy link

psnilesh commented Jan 23, 2024

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.

@earthboundkid
Copy link
Contributor

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?

@earthboundkid
Copy link
Contributor

earthboundkid commented Jan 23, 2024

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.

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 [N]T(slice) or (&[N]T)(slice).

@DeedleFake
Copy link

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?

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.

@Jorropo
Copy link
Member

Jorropo commented Jan 24, 2024

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).
The hard parts around SIMD is everything else around it which are not made easier by this proposal.


From purely a language standpoint I find a + b confusing, having learned programing with python among others a younger me would have definitely assumed this returned the concatenation of a and b as a new array which has both lengths added.
Go already hints as that because + on strings concatenate them.

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.
Given it does not make implementing SIMD in the compiler any easier than currently and I think it would be confusing for new programmers I don't think this is a good idea.


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.
Lots of that is manually writing SIMD code looks bad and is very verbose.

@Splizard
Copy link
Author

Splizard commented Jan 24, 2024

Out of curiosity, what would be the challenges in supporting flexible arrays / slices too ?

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?

The kind of code this would incentivize people to write also would look bad:

Are you suggesting there is a widespread need to write such functions to add large slices together efficiently?

The fact each element operates individually on the matching index is weird if you don't already know SIMD is a thing.

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 +, - and reserve * and / for permitting scalar operations. It's just that then it feels like a bit of a collection of special cases, with special rules for specific arrays, rather than a general case.

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!

@ianlancetaylor
Copy link
Contributor

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.

@ianlancetaylor
Copy link
Contributor

No change in consensus.

@Splizard
Copy link
Author

Arrays are not widely used in Go

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.

this doesn't support dot product, as mentioned earlier

Dot products are typically represented as functions, as they are not a basic arithmetic operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

9 participants