-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Description
Background
Forgive me if I don't do this right, its actually my first post on github, let alone a feature development proposal. I primarily develop in Fortran 95-2008 with python for pre/post processing of data and glue codes. I have been experimenting with Go for the past three months and think the language can have some powerful extensions beyond server side programming.
Proposal
Adding a "vect" basic type that are analogous to standard slices, but are optimized for vector math, contiguous memory, pass by reference and compile with cpu SIMD instructions or addition optimizations. In addition there could be compiler flags to customize instructions for specific OS or CPUs (eg AVX2), which then would make it easier on the developer without having to dive into assembly code.
Potential type names could be
vecfloat32 or float32vec
vecfloat64 or float64vec
vecint32 or int32vec
vecint64 or int64vec
The use of the vec types would necessitate using make to initialize the base vector that would point to the underlying array, which would be guaranteed to be in contiguous memory and once made can only point to that underlying array or nil (nothing else). Operations that break the contiguous feature and constant dimention, such as append, should be disabled.
A vector size is set at run-time, but cannot be dynamically changed once established.
For example:
x := make(float64vec, 32)
y := make(float64vec, 32)
ivec := make(int64vec, 100)
jvec := make(int64vec, 100)
Would create a x and y as float64 vector-arrays and ivec and jvec as int64 vector-arrays. If possible it might be good to make part of the basic type implicit padding for (such as shifting the 100 dim to the best cache size, but still limiting the size to 100)
Like a slice a vec would point to an underlying array, but there would be additional restrictions:
-
A vec that has not been allocated with
make()is of nil type and is not usable (like amap) -
No dimension changing; after creating with
make()cannot change len/cap -
Underlying array is always in contiguous memory
-
Vec passed by reference, loops do not create copy variables (see later example) only pointer reference
-
The life of a vec variable is nil to start and then once allocated with
make()cannot be associated with any other memory location unless set to nil first.- No automatic repointing of a non-pointer vec type
- A vec's memory is freed when it goes out of scope or set to nil.
- A vec can be set to nil and the subsequently reallocated with
make()to change its size, but it should be thought of as being a new variable (makes it simpler for name reuse).- If a vec is set to nil, then all pointers (eg
*float32vec) that point to it are set to nil and no longer associated with vec.
- If a vec is set to nil, then all pointers (eg
-
Pointers can only point to contiguous portions of allocated vectors
-
Pointer versions cannot be allocated with
make() -
Pointers may only point to
nil- a vec of the same type
- If vec is allocated, then points to contiguous memory it points to
- If vec is nil, then pointer follows vec
-if vec is allocated withmake(vec, len), then pointer will point to newly allocated memory along with vec
- Another pointer, but must be a contiguous portion of it or nil
- If
p1 = &p2and then laterp2 => &vec, then by associationp1 => &vec
- If
-
Vec pointers are automatically dereferenced when a dimension is specified.
p1[3] = 5is syntactic sugar*p1[3] = 5- Consequently:
*p1 == p[:]
-No dimension functions just like a pointer that requires an address
-
Pointers must point to an existing dimension or panic (example are
p1, p2 *vecint32, vec vecint32)
-
p1 = &p2is ok ifp2 = nilorp2 => &vec -
p1 = &p2[2:6]is ok only ifp2 !=nilandp2 = &vecand spans at least 5 elements- dereferencing is not necessary as p2 should just return the address at vec[2]
- An example:
vec := make(vecint32,10); p2 => &vec; p1 => &p2[2:6]
- Pointers may point to a subdimention of a pointer that points to a subdimention of a vec as long as the memory exists
- For example:
vec := make(vecint32, 20) // vec contains 20 elementsp2 => &vec[2:] // p2 points to the vec[2]p1 => &p2[4:8] // p1 points to the p2[4], which points to vec[6]
- For example:
Vector Operations
The advantage of a vector type is to allow vector operations (element wize operations and setting values).
For example:
x := make(float64vec, 32)
y := make(float64vec, 32)
z := make(float64vec, 32)
z = x + y
z = x - y
z = x * y
z = x / y
z = x % y
Scalar operations that use the same base type are applied to the entire vector.
For example:
x := make(float64vec, 32)
y := make(float64vec, 32)
x = 1.0 //all of x is set to 1
y = x[3] + 10 //scalar value sets entire y vector
If a vector is combined with a scalar of the same time, then the scalar is applied to the entire vector.
For example:
x := make(float64vec, 32)
y := make(float64vec, 32)
var z float64 = 15
x = 1.0 //all of x is set to 1
y = x * z // Set matching elements in y to the product of z to x
x*=z // Multiply all elements in x by z
y = x + y + z // vector addition for x and y, then add z to all elements
The advantage is that these operations may lend to faster vector processes by the compiler.
Looping and Reference Values
Looping with range should be syntactic sugar for referencing an index.
For example:
x := make(float64vec, 32)
for i,v := range x{
v = i
}
// is equivalent to:
for i := range x{
x[i] = i
}
Again the main goal of this is to take advantage of looping over contiguous memory for long vectors of numbers
Possible multi-dimension extensions
While I would not advocate this, it does open the possibility of creating an alias to a vec type that creates pointers to a vector to be referenced by multiple dimensions.
Not sure how this would be done, but one possiblity is to set one of the dimensions like how an array is declared
For example:
x := make([8]float64vec, 24)
// or
//x := make(float64vec, dim1, dim2, ...) //dimX is the size of dimension X and total size is prod(dimX)
x := make(float64vec, 3, 8) // row or column major can be discussed at a later time
This would create a vector that in contiguous memory for 32 float64, but can be referenced in groupings of 8.
Such as:
x[1,3] would be syntactic sugar for x [1*8 + 3 - 1] or simply x[12]
Under the hood it would just be a contiguous memory vector, but the multi-index would open the doors to many numerical applications and potential compiler optimizations.
Hopefully this is a useful suggestion and would open the door for GO applications in numerics as well as adding faster vector math for its current applications.