Skip to content

Proposal: Go2: Vector Basic Type - Similar to []T but with enhancements #35307

@ScottBoyce

Description

@ScottBoyce

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:

  1. A vec that has not been allocated with make() is of nil type and is not usable (like a map)

  2. No dimension changing; after creating with make() cannot change len/cap

  3. Underlying array is always in contiguous memory

  4. Vec passed by reference, loops do not create copy variables (see later example) only pointer reference

  5. 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.
  6. Pointers can only point to contiguous portions of allocated vectors

  7. Pointer versions cannot be allocated with make()

  8. 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 with make(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 = &p2 and then later p2 => &vec, then by association p1 => &vec
  9. Vec pointers are automatically dereferenced when a dimension is specified.

    • p1[3] = 5 is syntactic sugar *p1[3] = 5
    • Consequently: *p1 == p[:]
      -No dimension functions just like a pointer that requires an address
  10. Pointers must point to an existing dimension or panic (example are p1, p2 *vecint32, vec vecint32)

  • p1 = &p2 is ok if p2 = nil or p2 => &vec

  • p1 = &p2[2:6] is ok only if p2 !=nil and p2 = &vec and 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]
  1. 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 elements
      • p2 => &vec[2:] // p2 points to the vec[2]
      • p1 => &p2[4:8] // p1 points to the p2[4], which points to vec[6]

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions