gorgonia.org/gorgonia@v0.9.17/noextern.go (about)

     1  // +build !cuda
     2  
     3  package gorgonia
     4  
     5  import "gorgonia.org/tensor"
     6  
     7  // CUDA indicates if this build is using CUDA
     8  const CUDA = false
     9  
    10  var _ tensor.Engine = ExternMetadata{}
    11  
    12  // ExternMetadata is used to hold metadata about external execution devices.
    13  // In this build, it's an empty struct because the default build doesn't use external devices to execute the graph on
    14  type ExternMetadata struct {
    15  	tensor.Engine
    16  	b             batchedBLAS
    17  	workAvailable chan bool
    18  	syncChan      chan struct{}
    19  }
    20  
    21  func (m *ExternMetadata) init() error {
    22  	m.syncChan = make(chan struct{})
    23  	if m.b != nil {
    24  		m.workAvailable = make(chan bool)
    25  		go m.collectBLASWork()
    26  	}
    27  	return nil
    28  }
    29  
    30  // initFail is a no-op
    31  func (m *ExternMetadata) initFail() {}
    32  
    33  // HasFunc will always return false in this build
    34  func (m ExternMetadata) HasFunc(name string) bool { return false }
    35  
    36  // WorkAvailable returns a channel of empty struct, which is used to signal to the VM when there is work available. The VM will then call the DoWork method.
    37  func (m *ExternMetadata) WorkAvailable() <-chan bool {
    38  	if m.b != nil {
    39  		return m.workAvailable
    40  	}
    41  
    42  	return nil
    43  }
    44  
    45  // Sync returns the sync channel
    46  func (m *ExternMetadata) Sync() chan struct{} { return m.syncChan }
    47  
    48  // DoWork flushes any batched cgo calls. In this build it only flushes the batched BLAS calls.
    49  func (m *ExternMetadata) DoWork() error {
    50  	if m.b != nil {
    51  		m.b.DoWork()
    52  	}
    53  	return nil
    54  }
    55  
    56  // Get allocates a memory of the size. In this build it returns a NoOpError.
    57  func (m *ExternMetadata) Get(dev Device, size int64) (tensor.Memory, error) { return nil, noopError{} }
    58  
    59  // GetFromValue allocates a memory of the size of v. In this build it returns a NoOpError, and v itself
    60  func (m *ExternMetadata) GetFromValue(dev Device, v Value) (tensor.Memory, error) {
    61  	return v, noopError{}
    62  }
    63  
    64  // Put puts a previously allocated memory slab of the provided size back into the pool. Currently this is a No-op in this build.
    65  func (m *ExternMetadata) Put(dev Device, mem tensor.Memory, size int64) {}
    66  
    67  // PutValue puts a previously allocated value into the pool. In this build,  it is a noop.
    68  func (m *ExternMetadata) PutValue(dev Device, v Value) {}
    69  
    70  // Transfer transfers a value from device to device. In this build, it's a noop, returning the input value, and a nil error
    71  func (m *ExternMetadata) Transfer(toDev, fromDev Device, v Value, synchronous bool) (retVal Value, err error) {
    72  	return v, nil
    73  }
    74  
    75  // Reset is a noop function for compatibility with the Cuda build
    76  func (m *ExternMetadata) Reset() {}
    77  
    78  // Cleanup cleans up the ancillary allocations made during the calling of batched external device function.
    79  //
    80  // The reason for this method is due to the fact that there is currently no way to free memory while the context is still running without causing
    81  // some weirdness to the CUDA calls.
    82  //
    83  // This is a No-op in this build
    84  func (m *ExternMetadata) Cleanup() {}
    85  
    86  // Signal sends a signal down the workavailable channel, telling the VM to call the DoWork method. Signal is a synchronous method
    87  func (m *ExternMetadata) Signal() {
    88  	m.signal()
    89  	if m.workAvailable != nil {
    90  		<-m.syncChan
    91  	}
    92  }
    93  
    94  // collectBLASWork is a muxer for CBLAS/CuBLAS (if any) and the devices
    95  func (m *ExternMetadata) collectBLASWork() {
    96  	if m.b != nil {
    97  		for range m.b.WorkAvailable() {
    98  			m.workAvailable <- false
    99  		}
   100  	}
   101  }
   102  
   103  func (m *ExternMetadata) signal() {
   104  	if m.workAvailable != nil {
   105  		m.workAvailable <- true
   106  	}
   107  }
   108  
   109  func (m *ExternMetadata) setEngine(e tensor.Engine) { m.Engine = e }
   110  
   111  // ValueOnDevice gets the value of the node as a Value but on the desired device. In this build the device is always CPU, so it's equivalent to calling .Value()
   112  func (n *Node) ValueOnDevice(dev Device, extern External) (retVal Value, allocOnExtern bool, err error) {
   113  	return n.Value(), false, nil
   114  }
   115  
   116  // GradOnDevice gets the gradient value of the node as a Value but on the desired device. In this build the device is always CPU, so it's equivalent to calling .Grad()
   117  func (n *Node) GradOnDevice(dev Device, extern External) (retVal Value, allocOnExtern bool, err error) {
   118  	retVal, err = n.Grad()
   119  	return retVal, false, err
   120  }