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 }