gorgonia.org/tensor@v0.9.24/array.go (about)

     1  package tensor
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"unsafe"
     8  
     9  	"github.com/pkg/errors"
    10  	"gorgonia.org/tensor/internal/storage"
    11  )
    12  
    13  // array is the underlying generic array.
    14  type array struct {
    15  	storage.Header       // the header - the Go representation (a slice)
    16  	t              Dtype // the element type
    17  }
    18  
    19  // makeArray makes an array. The memory allocation is handled by Go
    20  func makeArray(t Dtype, length int) array {
    21  	v := malloc(t, length)
    22  	hdr := storage.Header{
    23  		Raw: v,
    24  	}
    25  	return array{
    26  		Header: hdr,
    27  		t:      t,
    28  	}
    29  
    30  }
    31  
    32  // arrayFromSlice creates an array from a slice. If x is not a slice, it will panic.
    33  func arrayFromSlice(x interface{}) array {
    34  	xT := reflect.TypeOf(x)
    35  	if xT.Kind() != reflect.Slice {
    36  		panic("Expected a slice")
    37  	}
    38  	elT := xT.Elem()
    39  
    40  	return array{
    41  		Header: storage.Header{
    42  			Raw: storage.AsByteSlice(x),
    43  		},
    44  		t: Dtype{elT},
    45  	}
    46  }
    47  
    48  func (a *array) Len() int { return a.Header.TypedLen(a.t.Type) }
    49  
    50  func (a *array) Cap() int { return a.Header.TypedLen(a.t.Type) }
    51  
    52  // fromSlice populates the value from a slice
    53  func (a *array) fromSlice(x interface{}) {
    54  	xT := reflect.TypeOf(x)
    55  	if xT.Kind() != reflect.Slice {
    56  		panic("Expected a slice")
    57  	}
    58  	elT := xT.Elem()
    59  	a.Raw = storage.AsByteSlice(x)
    60  	a.t = Dtype{elT}
    61  }
    62  
    63  // fromSliceOrTensor populates the value from a slice or anything that can form an array
    64  func (a *array) fromSliceOrArrayer(x interface{}) {
    65  	if T, ok := x.(arrayer); ok {
    66  		xp := T.arrPtr()
    67  
    68  		// if the underlying array hasn't been allocated, or not enough has been allocated
    69  		if a.Header.Raw == nil {
    70  			a.Header.Raw = malloc(xp.t, xp.Len())
    71  		}
    72  
    73  		a.t = xp.t
    74  		copyArray(a, T.arrPtr())
    75  		return
    76  	}
    77  	a.fromSlice(x)
    78  }
    79  
    80  // byteSlice casts the underlying slice into a byte slice. Useful for copying and zeroing, but not much else
    81  func (a array) byteSlice() []byte { return a.Header.Raw }
    82  
    83  // sliceInto creates a slice. Instead of returning an array, which would cause a lot of reallocations, sliceInto expects a array to
    84  // already have been created. This allows repetitive actions to be done without having to have many pointless allocation
    85  func (a *array) sliceInto(i, j int, res *array) {
    86  	c := a.Cap()
    87  
    88  	if i < 0 || j < i || j > c {
    89  		panic(fmt.Sprintf("Cannot slice %v - index %d:%d is out of bounds", a, i, j))
    90  	}
    91  
    92  	s := i * int(a.t.Size())
    93  	e := j * int(a.t.Size())
    94  	c = c - i
    95  
    96  	res.Raw = a.Raw[s:e]
    97  
    98  }
    99  
   100  // slice slices an array
   101  func (a array) slice(start, end int) array {
   102  	if end > a.Len() {
   103  		panic("Index out of range")
   104  	}
   105  	if end < start {
   106  		panic("Index out of range")
   107  	}
   108  
   109  	s := start * int(a.t.Size())
   110  	e := end * int(a.t.Size())
   111  
   112  	return array{
   113  		Header: storage.Header{Raw: a.Raw[s:e]},
   114  		t:      a.t,
   115  	}
   116  }
   117  
   118  // swap swaps the elements i and j in the array
   119  func (a *array) swap(i, j int) {
   120  	if a.t == String {
   121  		ss := a.hdr().Strings()
   122  		ss[i], ss[j] = ss[j], ss[i]
   123  		return
   124  	}
   125  	if !isParameterizedKind(a.t.Kind()) {
   126  		switch a.t.Size() {
   127  		case 8:
   128  			us := a.hdr().Uint64s()
   129  			us[i], us[j] = us[j], us[i]
   130  		case 4:
   131  			us := a.hdr().Uint32s()
   132  			us[i], us[j] = us[j], us[i]
   133  		case 2:
   134  			us := a.hdr().Uint16s()
   135  			us[i], us[j] = us[j], us[i]
   136  		case 1:
   137  			us := a.hdr().Uint8s()
   138  			us[i], us[j] = us[j], us[i]
   139  		}
   140  		return
   141  	}
   142  
   143  	size := int(a.t.Size())
   144  	tmp := make([]byte, size)
   145  	bs := a.byteSlice()
   146  	is := i * size
   147  	ie := is + size
   148  	js := j * size
   149  	je := js + size
   150  	copy(tmp, bs[is:ie])
   151  	copy(bs[is:ie], bs[js:je])
   152  	copy(bs[js:je], tmp)
   153  }
   154  
   155  /* *Array is a Memory */
   156  
   157  // Uintptr returns the pointer of the first value of the slab
   158  func (a *array) Uintptr() uintptr { return uintptr(unsafe.Pointer(&a.Header.Raw[0])) }
   159  
   160  // MemSize returns how big the slice is in bytes
   161  func (a *array) MemSize() uintptr { return uintptr(len(a.Header.Raw)) }
   162  
   163  // Data returns the representation of a slice.
   164  func (a array) Data() interface{} {
   165  	// build a type of []T
   166  	shdr := reflect.SliceHeader{
   167  		Data: a.Uintptr(),
   168  		Len:  a.Len(),
   169  		Cap:  a.Cap(),
   170  	}
   171  	sliceT := reflect.SliceOf(a.t.Type)
   172  	ptr := unsafe.Pointer(&shdr)
   173  	val := reflect.Indirect(reflect.NewAt(sliceT, ptr))
   174  	return val.Interface()
   175  
   176  }
   177  
   178  // Zero zeroes out the underlying array of the *Dense tensor.
   179  func (a array) Zero() {
   180  	if a.t.Kind() == reflect.String {
   181  		ss := a.Strings()
   182  		for i := range ss {
   183  			ss[i] = ""
   184  		}
   185  		return
   186  	}
   187  	if !isParameterizedKind(a.t.Kind()) {
   188  		ba := a.byteSlice()
   189  		for i := range ba {
   190  			ba[i] = 0
   191  		}
   192  		return
   193  	}
   194  
   195  	l := a.Len()
   196  	for i := 0; i < l; i++ {
   197  		val := reflect.NewAt(a.t.Type, storage.ElementAt(i, unsafe.Pointer(&a.Header.Raw[0]), a.t.Size()))
   198  		val = reflect.Indirect(val)
   199  		val.Set(reflect.Zero(a.t))
   200  	}
   201  }
   202  
   203  func (a *array) hdr() *storage.Header { return &a.Header }
   204  func (a *array) rtype() reflect.Type  { return a.t.Type }
   205  
   206  /* MEMORY MOVEMENT STUFF */
   207  
   208  // malloc is standard Go allocation of a block of memory - the plus side is that Go manages the memory
   209  func malloc(t Dtype, length int) []byte {
   210  	size := int(calcMemSize(t, length))
   211  	return make([]byte, size)
   212  }
   213  
   214  // calcMemSize calulates the memory size of an array (given its size)
   215  func calcMemSize(dt Dtype, size int) int64 {
   216  	return int64(dt.Size()) * int64(size)
   217  }
   218  
   219  // copyArray copies an array.
   220  func copyArray(dst, src *array) int {
   221  	if dst.t != src.t {
   222  		panic("Cannot copy arrays of different types.")
   223  	}
   224  	return storage.Copy(dst.t.Type, &dst.Header, &src.Header)
   225  }
   226  
   227  func copyArraySliced(dst array, dstart, dend int, src array, sstart, send int) int {
   228  	if dst.t != src.t {
   229  		panic("Cannot copy arrays of different types.")
   230  	}
   231  	return storage.CopySliced(dst.t.Type, &dst.Header, dstart, dend, &src.Header, sstart, send)
   232  }
   233  
   234  // copyDense copies a DenseTensor
   235  func copyDense(dst, src DenseTensor) int {
   236  	if dst.Dtype() != src.Dtype() {
   237  		panic("Cannot dopy DenseTensors of different types")
   238  	}
   239  
   240  	if ms, ok := src.(MaskedTensor); ok && ms.IsMasked() {
   241  		if md, ok := dst.(MaskedTensor); ok {
   242  			dmask := md.Mask()
   243  			smask := ms.Mask()
   244  			if cap(dmask) < len(smask) {
   245  				dmask = make([]bool, len(smask))
   246  				copy(dmask, md.Mask())
   247  				md.SetMask(dmask)
   248  			}
   249  			copy(dmask, smask)
   250  		}
   251  	}
   252  
   253  	e := src.Engine()
   254  	if err := e.Memcpy(dst.arrPtr(), src.arrPtr()); err != nil {
   255  		panic(err)
   256  	}
   257  	return dst.len()
   258  
   259  	// return copyArray(dst.arr(), src.arr())
   260  }
   261  
   262  // copyDenseSliced copies a DenseTensor, but both are sliced
   263  func copyDenseSliced(dst DenseTensor, dstart, dend int, src DenseTensor, sstart, send int) int {
   264  	if dst.Dtype() != src.Dtype() {
   265  		panic("Cannot copy DenseTensors of different types")
   266  	}
   267  
   268  	if ms, ok := src.(MaskedTensor); ok && ms.IsMasked() {
   269  		if md, ok := dst.(MaskedTensor); ok {
   270  			dmask := md.Mask()
   271  			smask := ms.Mask()
   272  			if cap(dmask) < dend {
   273  				dmask = make([]bool, dend)
   274  				copy(dmask, md.Mask())
   275  				md.SetMask(dmask)
   276  			}
   277  			copy(dmask[dstart:dend], smask[sstart:send])
   278  		}
   279  	}
   280  	if e := src.Engine(); e != nil {
   281  		darr := dst.arr()
   282  		sarr := src.arr()
   283  		da := darr.slice(dstart, dend)
   284  		sa := sarr.slice(sstart, send)
   285  
   286  		switch e.(type) {
   287  		case NonStdEngine:
   288  			if err := e.Memcpy(&da, &sa); err != nil {
   289  				panic(err)
   290  			}
   291  		default:
   292  			// THIS IS AN OPTIMIZATION. REVISIT WHEN NEEDED.
   293  			//
   294  			// THE PURPOSE of this optimization is to make this perform better under
   295  			// default circumstances.
   296  			//
   297  			// The original code simply uses t.Engine().Memcpy(&dSlice, &tSlice).
   298  			// A variant can still be seen in the NonStdEngine case above.
   299  			//
   300  			// The `array.slice()` method has been optimized to return `array2`, which is a
   301  			// non-heap allocated type.
   302  			// a value of `array2` cannot have its address taken - e.g.
   303  			// 	var a array2
   304  			// 	doSomething(&a) // ← this cannot be done
   305  			//
   306  			// We *could* make `array2` implement Memory. But then a lot of runtime.convT2I and
   307  			// runtime.convI2T would be called. Which defeats the purpose of making things fast.
   308  			//
   309  			// So instead, we check to see if the Engine uses standard allocation methods.
   310  			// Typically this means `StdEng`.
   311  			//
   312  			// If so, we directly use storage.Copy instead of using the engine
   313  			storage.Copy(da.t.Type, &da.Header, &sa.Header)
   314  		}
   315  
   316  		return da.Len()
   317  	}
   318  	return copyArraySliced(dst.arr(), dstart, dend, src.arr(), sstart, send)
   319  }
   320  
   321  // copyDenseIter copies a DenseTensor, with iterator
   322  func copyDenseIter(dst, src DenseTensor, diter, siter Iterator) (int, error) {
   323  	if dst.Dtype() != src.Dtype() {
   324  		panic("Cannot copy Dense arrays of different types")
   325  	}
   326  
   327  	// if they all don't need iterators, and have the same data order
   328  	if !dst.RequiresIterator() && !src.RequiresIterator() && dst.DataOrder().HasSameOrder(src.DataOrder()) {
   329  		return copyDense(dst, src), nil
   330  	}
   331  
   332  	if !dst.IsNativelyAccessible() {
   333  		return 0, errors.Errorf(inaccessibleData, dst)
   334  	}
   335  	if !src.IsNativelyAccessible() {
   336  		return 0, errors.Errorf(inaccessibleData, src)
   337  	}
   338  
   339  	if diter == nil {
   340  		diter = FlatIteratorFromDense(dst)
   341  	}
   342  	if siter == nil {
   343  		siter = FlatIteratorFromDense(src)
   344  	}
   345  
   346  	// if it's a masked tensor, we copy the mask as well
   347  	if ms, ok := src.(MaskedTensor); ok && ms.IsMasked() {
   348  		if md, ok := dst.(MaskedTensor); ok {
   349  			dmask := md.Mask()
   350  			smask := ms.Mask()
   351  			if cap(dmask) < len(smask) {
   352  				dmask = make([]bool, len(smask))
   353  				copy(dmask, md.Mask())
   354  				md.SetMask(dmask)
   355  			}
   356  			copy(dmask, smask)
   357  		}
   358  	}
   359  	return storage.CopyIter(dst.rtype(), dst.hdr(), src.hdr(), diter, siter), nil
   360  }
   361  
   362  type scalarPtrCount struct {
   363  	Ptr   unsafe.Pointer
   364  	Count int
   365  }
   366  
   367  // scalarRCLock is a lock for the reference counting list.
   368  var scalarRCLock sync.Mutex
   369  
   370  // scalarRC is a bunch of reference counted pointers to scalar values
   371  var scalarRC = make(map[uintptr]*sync.Pool) // uintptr is the size, the pool stores []byte
   372  
   373  func scalarPool(size uintptr) *sync.Pool {
   374  	scalarRCLock.Lock()
   375  	pool, ok := scalarRC[size]
   376  	if !ok {
   377  		pool = &sync.Pool{
   378  			New: func() interface{} { return make([]byte, size) },
   379  		}
   380  		scalarRC[size] = pool
   381  	}
   382  	scalarRCLock.Unlock()
   383  	return pool
   384  }
   385  
   386  func allocScalar(a interface{}) []byte {
   387  	atype := reflect.TypeOf(a)
   388  	size := atype.Size()
   389  	pool := scalarPool(size)
   390  	return pool.Get().([]byte)
   391  }
   392  
   393  func freeScalar(bs []byte) {
   394  	if bs == nil {
   395  		return
   396  	}
   397  
   398  	// zero out
   399  	for i := range bs {
   400  		bs[i] = 0
   401  	}
   402  
   403  	size := uintptr(len(bs))
   404  
   405  	// put it back into pool
   406  	pool := scalarPool(size)
   407  	pool.Put(bs)
   408  }
   409  
   410  // scalarToHeader creates a Header from a scalar value
   411  func scalarToHeader(a interface{}) (hdr *storage.Header, newAlloc bool) {
   412  	var raw []byte
   413  	switch at := a.(type) {
   414  	case Memory:
   415  		raw = storage.FromMemory(at.Uintptr(), at.MemSize())
   416  	default:
   417  		raw = allocScalar(a)
   418  		newAlloc = true
   419  	}
   420  	hdr = borrowHeader()
   421  	hdr.Raw = raw
   422  	if newAlloc {
   423  		copyScalarToPrealloc(a, hdr.Raw)
   424  	}
   425  
   426  	return hdr, newAlloc
   427  }
   428  
   429  func copyScalarToPrealloc(a interface{}, bs []byte) {
   430  	xV := reflect.ValueOf(a)
   431  	xT := reflect.TypeOf(a)
   432  
   433  	p := unsafe.Pointer(&bs[0])
   434  	v := reflect.NewAt(xT, p)
   435  	reflect.Indirect(v).Set(xV)
   436  	return
   437  }