Beatmup
Beatmup::NNets::Model Class Reference

Neural net model. More...

#include <model.h>

Inheritance diagram for Beatmup::NNets::Model:
Beatmup::GL::ProgramBank Beatmup::Object Beatmup::NNets::Classifier Beatmup::NNets::DeserializedModel

Classes

struct  Connection
 Connection descriptor. More...
 
struct  UserOutput
 A user-defined output descriptor. More...
 

Public Member Functions

 Model (Context &context, std::initializer_list< AbstractOperation * > ops)
 Instantiates a model from a list of operations interconnecting them in a feedforward fashion. More...
 
 Model (Context &context)
 Instantiates an empty model. More...
 
 ~Model ()
 
void append (AbstractOperation *newOp, bool connect=false)
 Adds a new operation to the model. More...
 
void append (std::initializer_list< AbstractOperation * > newOps, bool connect=false)
 Adds new operations to the model. More...
 
void addOperation (const std::string &opName, AbstractOperation *newOp)
 Adds a new operation to the model before another operation in the execution order. More...
 
void addOperation (const AbstractOperation &operation, AbstractOperation *newOp)
 
void addConnection (const std::string &sourceOpName, const std::string &destOpName, int output=0, int input=0, int shuffle=0)
 Adds a connection between two given ops. More...
 
void addOutput (const std::string &operation, int output=0)
 Enables reading output data from the model memory through getOutputData(). More...
 
void addOutput (const AbstractOperation &operation, int output=0)
 
const float * getOutputData (size_t &numSamples, const std::string &operation, int output=0) const
 Reads data from the model memory. More...
 
const float * getOutputData (size_t &numSamples, const AbstractOperation &operation, int output=0) const
 
virtual void prepare (GraphicPipeline &gpu, ChunkCollection &data)
 Prepares all operations: reads the model data from chunks and builds GPU programs. More...
 
bool isReady () const
 
void execute (TaskThread &thread, GraphicPipeline *gpu)
 Runs the inference. More...
 
bool isOperationInModel (const AbstractOperation &operation) const
 Checks if a specific operation makes part of the model. More...
 
AbstractOperationgetFirstOperation ()
 
AbstractOperationgetLastOperation ()
 
const AbstractOperationgetFirstOperation () const
 
const AbstractOperationgetLastOperation () const
 
size_t getNumberOfOperations () const
 
template<class OperationClass = AbstractOperation>
OperationClass & getOperation (const std::string &operationName)
 Retrieves an operation by its name. More...
 
const ProgressTrackinggetPreparingProgress () const
 Returns model preparation progress tracking. More...
 
const ProgressTrackinggetInferenceProgress () const
 Returns inference progress tracking. More...
 
unsigned long countMultiplyAdds () const
 Provides an estimation of the number of multiply-adds characterizing the model complexity. More...
 
unsigned long countTexelFetches () const
 Provides an estimation of the total number of texels fetched by all the operations in the model per image. More...
 
size_t getMemorySize () const
 Returns the amount of texture memory in bytes currently allocated by the model to run the inference. More...
 
Listing serialize () const
 Returns serialized representation of the model as a Listing. More...
 
std::string serializeToString () const
 Returns serialized representation of the model as a string. More...
 
void setProfiler (Profiler *profiler)
 Attaches a profiler instance to meter the execution time per operation during the inference. More...
 
- Public Member Functions inherited from Beatmup::GL::ProgramBank
 ProgramBank (Context &context)
 
 ~ProgramBank ()
 
GL::RenderingProgramoperator() (GraphicPipeline &gpu, const std::string &code, bool enableExternalTextures=false)
 Provides a program given a fragment shader source code. More...
 
void release (GraphicPipeline &gpu, GL::RenderingProgram *program)
 Marks a program as unused any more. More...
 
- Public Member Functions inherited from Beatmup::Object
virtual ~Object ()
 

Protected Member Functions

void freeMemory ()
 Frees all allocated storages. More...
 
StorageallocateStorage (GraphicPipeline &gpu, const Size size, bool forGpu=true, bool forCpu=false, const int pad=0, const int reservedChannels=0)
 Allocates a new storage. More...
 
StorageallocateFlatStorage (GraphicPipeline &gpu, const int size)
 Allocates a new flat storage. More...
 
GL::VectorallocateVector (GraphicPipeline &gpu, const int size)
 Allocates a vector that can be used as operation input or output. More...
 
InternalBitmapallocateTexture (GraphicPipeline &gpu, const Size size)
 Allocates a texture that can be used as operation input or output. More...
 
bool isPreceding (const AbstractOperation &first, const AbstractOperation &second) const
 Checks whether an operation goes before another operation in the model according the ops execution order. More...
 
AbstractOperationoperator[] (const std::string &operationName)
 
const AbstractOperationoperator[] (const std::string &operationName) const
 
void addConnection (AbstractOperation &source, AbstractOperation &dest, int output=0, int input=0, int shuffle=0)
 

Protected Attributes

std::vector< AbstractOperation * > ops
 model operations More...
 
ProgressTracking preparingProgress
 model preparation progress More...
 
ProgressTracking inferenceProgress
 inference progress More...
 
bool ready
 if true, ops are connected to each other and storages are allocated More...
 
- Protected Attributes inherited from Beatmup::GL::ProgramBank
Contextcontext
 

Private Attributes

std::multimap< const AbstractOperation *, Connectionconnections
 source operation => connection descriptor mapping More...
 
std::multimap< const AbstractOperation *, UserOutputuserOutputs
 operation => user output mapping More...
 
std::vector< Storage * > storages
 allocated storages used during the inference More...
 
std::vector< GL::Vector * > vectors
 allocated vectors used during the inference More...
 
std::vector< InternalBitmap * > textures
 allocated images used during the inference More...
 
Profilerprofiler
 pointer to a Profiler attached to the model More...
 

Detailed Description

Neural net model.

Contains a list of operations and programmatically defined interconnections between them using addConnection(). Enables access to the model memory at any point in the model through addOutput() and getOutputData(). The memory needed to store internal data during the inference is allocated automatically; storages are reused when possible. The inference of a Model is performed by InferenceTask.

Definition at line 92 of file model.h.

Constructor & Destructor Documentation

◆ Model() [1/2]

Model::Model ( Context context,
std::initializer_list< AbstractOperation * >  ops 
)

Instantiates a model from a list of operations interconnecting them in a feedforward fashion.

The first output of every operation is connected to the first input of its successor. Optional connections may be added after model creation.

Parameters
[in,out]contextA context instance
[in]opsOperations given in the execution order. The Model does not take ownership of them.

Definition at line 27 of file model.cpp.

27  :
29  profiler(nullptr), ready(false),
30  ops(ops.begin(), ops.end())
31 {
32  // establish feedforward connections
33  for (size_t i = 1; i < this->ops.size(); ++i)
34  addConnection(*this->ops[i - 1], *this->ops[i]);
35 }
ProgramBank(Context &context)
Definition: program_bank.h:46
bool ready
if true, ops are connected to each other and storages are allocated
Definition: model.h:125
std::vector< AbstractOperation * > ops
model operations
Definition: model.h:122
Profiler * profiler
pointer to a Profiler attached to the model
Definition: model.h:119
void addConnection(AbstractOperation &source, AbstractOperation &dest, int output=0, int input=0, int shuffle=0)
Definition: model.cpp:91

◆ Model() [2/2]

Model::Model ( Context context)

Instantiates an empty model.

Parameters
[in,out]contextA context instance used to store internal resources needed for inference

Definition at line 38 of file model.cpp.

38 : Model(context, {}) {}
Model(Context &context, std::initializer_list< AbstractOperation * > ops)
Instantiates a model from a list of operations interconnecting them in a feedforward fashion.
Definition: model.cpp:27

◆ ~Model()

Model::~Model ( )

Definition at line 40 of file model.cpp.

40  {
41  for (auto op : ops)
42  op->disconnect();
43  freeMemory();
44 }
void freeMemory()
Frees all allocated storages.
Definition: model.cpp:415
jlong jint op

Member Function Documentation

◆ freeMemory()

void Model::freeMemory ( )
protected

Frees all allocated storages.

Definition at line 415 of file model.cpp.

415  {
416  for (auto storage : storages)
417  delete storage;
418  storages.clear();
419  for (auto vector : vectors)
420  delete vector;
421  vectors.clear();
422  for (auto texture : textures)
423  delete texture;
424  textures.clear();
425 }
std::vector< Storage * > storages
allocated storages used during the inference
Definition: model.h:116
std::vector< InternalBitmap * > textures
allocated images used during the inference
Definition: model.h:118
std::vector< GL::Vector * > vectors
allocated vectors used during the inference
Definition: model.h:117

◆ allocateStorage()

Storage & Model::allocateStorage ( GraphicPipeline gpu,
const Size  size,
bool  forGpu = true,
bool  forCpu = false,
const int  pad = 0,
const int  reservedChannels = 0 
)
protected

Allocates a new storage.

Its views might be used as operations inputs and outputs. The storage is destroyed together with the model.

Parameters
[in,out]gpuA graphic pipeline instance
[in]sizeThe storage size (width, height, number of channels)
[in]forGpuAllocate for the use on GPU
[in]forCpuAllocate for the use on CPU
[in]padStorage padding: number of pixels added on both sides along width and height of every channel
[in]reservedChannelsNumber of additional channels that may be sampled together with the storage. This does not change the storage size, but impacts the way the channels are packed into the textures. It allows the storage to be sampled with other storages of a specific total depth in the same shader, if the addDepth is greater or equal to the total depth.
Returns
newly allocated storage.

Definition at line 428 of file model.cpp.

428  {
429  Storage* storage = new Storage(context, gpu, size, pad, reservedDepth);
430  if (forGpu)
431  storage->allocate(gpu);
432  if (forCpu)
433  storage->allocate();
434  storages.push_back(storage);
435  return *storage;
436 }
3D tensor stored in a set of textures.
Definition: storage.h:126
void allocate(GraphicPipeline &gpu)
Allocates the storage in GPU memory.
Definition: storage.cpp:167
jlong jobject size

◆ allocateFlatStorage()

Storage & Model::allocateFlatStorage ( GraphicPipeline gpu,
const int  size 
)
protected

Allocates a new flat storage.

Its views are be used as operations inputs and outputs. Flat storages can be inputs of Dense layers. The storage is destroyed together with the model.

Parameters
[in,out]gpuA graphic pipeline instance
[in]sizeNumber of samples in the storage
Returns
newly allocated storage.

Definition at line 439 of file model.cpp.

439  {
440  Storage* storage = new Storage(context, gpu, Size(1, 1, size));
441  storage->allocate(gpu);
442  storages.push_back(storage);
443  return *storage;
444 }
Operation 3D input/output size.
Definition: storage.h:37

◆ allocateVector()

GL::Vector & Model::allocateVector ( GraphicPipeline gpu,
const int  size 
)
protected

Allocates a vector that can be used as operation input or output.

Differently to flat storages, vectors store floating point data (GL ES 3.1 and higher) or 16-bit signed fixed point values with 8 bits fractional part (GL ES 2.0).

Parameters
[in,out]gpuA graphic pipeline instance
[in]sizeNumber of samples in the vector

Definition at line 447 of file model.cpp.

447  {
449 #ifdef BEATMUP_OPENGLVERSION_GLES20
451 #else
453 #endif
454  GL::Vector* vector = new GL::Vector(context, gpu, size, format);
455  vectors.push_back(vector);
456  return *vector;
457 }
Real-valued vector usable by GPU.
Format
Vector data format.
@ FIXED16
16 bit per element
@ FLOAT
32 bit per element, floating point
JNIEnv jobject jint format

◆ allocateTexture()

InternalBitmap & Model::allocateTexture ( GraphicPipeline gpu,
const Size  size 
)
protected

Allocates a texture that can be used as operation input or output.

Parameters
[in,out]gpuA graphic pipeline instance
[in]sizeImage size. The depth can be 1, 3 or 4 channels.

Definition at line 460 of file model.cpp.

460  {
462  switch (size.getDepth()) {
463  case 1:
465  break;
466  case 3:
468  break;
469  case 4:
471  break;
472  default:
473  throw InvalidArgument("Unsupported depth: " + std::to_string(size.getDepth()));
474  }
475  textures.push_back(new InternalBitmap(context, pixelFormat, size.getWidth(), size.getHeight()));
476  return *textures.back();
477 }
Bitmap whose memory is managed by the Beatmup engine.
@ SingleByte
single channel of 8 bits per pixel (like grayscale), unsigned integer values
@ QuadByte
4 channels of 8 bits per pixel (like RGBA), unsigned integer values
@ TripleByte
3 channels of 8 bits per pixel (like RGB), unsigned integer values
std::string to_string(Beatmup::NNets::ActivationFunction function)
jlong jint jint jint jint pixelFormat

◆ isPreceding()

bool Model::isPreceding ( const AbstractOperation first,
const AbstractOperation second 
) const
protected

Checks whether an operation goes before another operation in the model according the ops execution order.

Parameters
[in]firstThe first operation (expected to be executed earlier)
[in]secondThe first operation (expected to be executed later)
Returns
true if both operations are in the model, and the first one is executed before the second one, false otherwise.

Definition at line 480 of file model.cpp.

480  {
481  for (size_t firstIdx = 0; firstIdx < ops.size(); ++firstIdx)
482  if (ops[firstIdx] == &first) {
483  for (size_t secondIdx = firstIdx + 1; secondIdx < ops.size(); ++secondIdx)
484  if (ops[secondIdx] == &second)
485  return true;
486  return false;
487  }
488  return false;
489 }

◆ operator[]() [1/2]

AbstractOperation * Model::operator[] ( const std::string &  operationName)
protected

Definition at line 492 of file model.cpp.

492  {
493  for (auto op : ops)
494  if (op->getName() == operationName)
495  return op;
496  throw InvalidArgument("Operation not found: " + operationName);
497 }

◆ operator[]() [2/2]

const AbstractOperation * Model::operator[] ( const std::string &  operationName) const
protected

Definition at line 500 of file model.cpp.

500  {
501  for (auto op : ops)
502  if (op->getName() == operationName)
503  return op;
504  throw InvalidArgument("Operation not found: " + operationName);
505 }

◆ addConnection() [1/2]

void Model::addConnection ( AbstractOperation source,
AbstractOperation dest,
int  output = 0,
int  input = 0,
int  shuffle = 0 
)
protected

Definition at line 91 of file model.cpp.

91  {
92  RuntimeError::check(0 <= output && output < source.getOutputCount(),
93  "Operation " + source.getName() + " does not have output #" + std::to_string(output));
94  RuntimeError::check(0 <= input && input < dest.getInputCount(),
95  "Operation " + dest.getName() + " does not have input #" + std::to_string(input));
96  connections.emplace(&source, Connection{ &dest, output, input, shuffle });
97  ready = false;
98 }
virtual int getOutputCount() const
Returns number of operation outputs.
Definition: operation.h:135
virtual int getInputCount() const
Returns number of operation inputs.
Definition: operation.h:129
std::string getName() const
Definition: operation.h:242
std::multimap< const AbstractOperation *, Connection > connections
source operation => connection descriptor mapping
Definition: model.h:113
static void check(const bool condition, const std::string &message)
Definition: exception.h:64

◆ append() [1/2]

void Model::append ( AbstractOperation newOp,
bool  connect = false 
)

Adds a new operation to the model.

The operation is added to the end of the operations list. The execution order corresponds to the addition order. The Model does not takes ownership of the passed pointer.

Parameters
[in]newOpThe new operation
[in]connectIf true, the main operation input (#0) is connected to the main output (#0) of the last operation

Definition at line 47 of file model.cpp.

47  {
48  for (auto op : ops) {
49  if (op == newOp)
50  throw RuntimeError("Cannot add operation " + newOp->getName() + " to the model: already added");
51  else
52  if (op->getName() == newOp->getName())
53  throw RuntimeError("Cannot add operation " + newOp->getName() + " to the model: an operation with the same exists in the model");
54  }
55  ops.push_back(newOp);
56  if (connect)
57  addConnection(*ops[ops.size() - 2], *ops.back(), 0, 0, 0);
58  ready = false;
59 }

◆ append() [2/2]

void Model::append ( std::initializer_list< AbstractOperation * >  newOps,
bool  connect = false 
)

Adds new operations to the model.

The operations are added to the end of the operations list. The execution order corresponds to the addition order. The Model does not takes ownership of the passed pointer.

Parameters
[in]newOpsThe new operations
[in]connectIf true, the main input (#0) of every operation is connected to the main output (#0) of the preceding operation

Definition at line 62 of file model.cpp.

62  {
63  for (auto op : newOps)
64  append(op, connect);
65 }
void append(AbstractOperation *newOp, bool connect=false)
Adds a new operation to the model.
Definition: model.cpp:47

◆ addOperation() [1/2]

void Model::addOperation ( const std::string &  opName,
AbstractOperation newOp 
)

Adds a new operation to the model before another operation in the execution order.

The Model does not takes ownership of the passed pointer. The new operation is not automatically connected to other operations.

Parameters
[in]opNameName of the operation the new operation is inserted before
[in]newOpThe new operation

Definition at line 68 of file model.cpp.

68  {
69  auto it = std::find_if(ops.begin(), ops.end(), [&opName](AbstractOperation* op){ return op->getName() == opName; });
70  if (it == ops.end())
71  throw InvalidArgument("Cannot find operation " + opName);
72  ops.insert(it, newOp);
73 }
Abstract neural net operation (layer).
Definition: operation.h:46
JNIEnv jlong jobject jstring opName

◆ addOperation() [2/2]

void Model::addOperation ( const AbstractOperation operation,
AbstractOperation newOp 
)

Definition at line 76 of file model.cpp.

76  {
77  auto it = std::find(ops.begin(), ops.end(), &op);
78  if (it == ops.end())
79  throw InvalidArgument("Operation " + op.getName() + " is not in the model");
80  ops.insert(it, newOp);
81 }

◆ addConnection() [2/2]

void Model::addConnection ( const std::string &  sourceOpName,
const std::string &  destOpName,
int  output = 0,
int  input = 0,
int  shuffle = 0 
)

Adds a connection between two given ops.

Parameters
[in]sourceOpNameName of the operation emitting the data
[in]destOpNameName of the operation receiving the data
[in]outputOutput number of the source operation
[in]inputInput number of the destination operation
[in]shuffleIf greater than zero, the storage is shuffled. For shuffle = n, the output channels are sent to the destination operation in the following order: 0, 1, 2, 3, 4n, 4n+1, 4n+2, 4n+3, 8n, 8n+1, 8n+2, 8n+3, ..., 4, 5, 6, 7, 4n+4, 4n+5, 4n+6, 4n+7, 8n+4, ...

Definition at line 84 of file model.cpp.

84  {
85  auto& source = getOperation(sourceOpName);
86  auto& dest = getOperation(destOpName);
87  addConnection(source, dest, output, input, shuffle);
88 }
OperationClass & getOperation(const std::string &operationName)
Retrieves an operation by its name.
Definition: model.h:303

◆ addOutput() [1/2]

void Model::addOutput ( const std::string &  operation,
int  output = 0 
)

Enables reading output data from the model memory through getOutputData().

A given operation output is connected to a storage that might be accessed by the application after the run.

Parameters
[in]operationName of the operation or the operation itself to get data from
[in]outputThe operation output index

Definition at line 101 of file model.cpp.

101  {
102  auto op = (*this)[opName];
103  auto outputs = userOutputs.equal_range(op);
104  for (auto i = outputs.first; i != outputs.second; ++i)
105  if (i->second.index == output)
106  // already added
107  return;
108  userOutputs.emplace(op, UserOutput{ output });
109  ready = false;
110 }
std::multimap< const AbstractOperation *, UserOutput > userOutputs
operation => user output mapping
Definition: model.h:114

◆ addOutput() [2/2]

void Model::addOutput ( const AbstractOperation operation,
int  output = 0 
)

Definition at line 113 of file model.cpp.

113  {
114  RuntimeError::check(isOperationInModel(operation), "Operation " + operation.getName() + " is not in the model");
115  auto outputs = userOutputs.equal_range(&operation);
116  for (auto i = outputs.first; i != outputs.second; ++i)
117  if (i->second.index == output)
118  // already added
119  return;
120  userOutputs.emplace(&operation, UserOutput{ output });
121  ready = false;
122 }
bool isOperationInModel(const AbstractOperation &operation) const
Checks if a specific operation makes part of the model.
Definition: model.cpp:407

◆ getOutputData() [1/2]

const float * Model::getOutputData ( size_t &  numSamples,
const std::string &  operation,
int  output = 0 
) const

Reads data from the model memory.

addOutput() is needed to be called first in order to enable reading the data. Otherwise null is returned.

Parameters
[out]numSamplesReturns number of samples in the pointed data buffer
[in]operationName of the operation or the operation itself to get data from
[in]outputThe operation output index
Returns
pointer to the data stored as a 3D array of (height, width, channels) layout, or null.

Definition at line 125 of file model.cpp.

125  {
126  return getOutputData(numSamples, *(*this)[operation], output);
127 }
const float * getOutputData(size_t &numSamples, const std::string &operation, int output=0) const
Reads data from the model memory.
Definition: model.cpp:125

◆ getOutputData() [2/2]

const float * Model::getOutputData ( size_t &  numSamples,
const AbstractOperation operation,
int  output = 0 
) const

Definition at line 130 of file model.cpp.

130  {
131  auto outputs = userOutputs.equal_range(&operation);
132  for (auto i = outputs.first; i != outputs.second; ++i)
133  if (i->second.index == output) {
134  numSamples = i->second.data.size();
135  return i->second.data.data();
136  }
137 
138  numSamples = 0;
139  return nullptr;
140 }

◆ prepare()

void Model::prepare ( GraphicPipeline gpu,
ChunkCollection data 
)
virtual

Prepares all operations: reads the model data from chunks and builds GPU programs.

The inputs of the model needed to be provided. Preparation progress is tracked by a ProgressTracking instance (getPreparingProgress()).

Parameters
[in,out]gpuA graphic pipeline instance
[in]dataChunkCollection containing the model data

Definition at line 143 of file model.cpp.

143  {
144  if (ready)
145  return;
146  freeMemory();
147 
148  std::map<Storage*, std::vector<AbstractOperation*>> refs;
149  // Contains ops that use a specific storage as input, meaning that it cannot be reused elsewhere.
150  // If no ops refer a storage, in can be recycled.
151 
152  // find input depth capping
153  // If too many channels are sampled by an op having multiple inputs, its input storages will have reserved channels.
154  const int sampledChannelsLimit = 4 * gpu.getLimit(GraphicPipeline::Limit::TEXTURE_IMAGE_UNITS);
155  std::map<AbstractOperation*, int> sampledChannels; // op => number of sampled channels
156  for (auto conn : connections) {
157  auto* op = conn.second.dest;
158  // get the number of sampled channels
159  int min, max;
160  op->getSampledChannels(conn.second.input, min, max);
161  // cap the maximum: a storage will not have more channels than the limit anyway
162  max = std::min(max, sampledChannelsLimit);
163  // add to input channels
164  sampledChannels[op] += max;
165  }
166 
167  // loop through connected ops
168  data.open();
169  preparingProgress.reset(ops.size());
170  for (auto src : ops) {
171  std::vector<Beatmup::Object*> outputs(src->getOutputCount(), nullptr); // src output index => storage/vector bound to the output
172  std::vector<int> paddings(src->getOutputCount(), 0); // src output index => max padding over all connections
173  Bitset connectedOutputs(src->getOutputCount(), false);
174 
175  // loop over connections to find max paddings per output
176  auto connections = this->connections.equal_range(src);
177  for (auto i = connections.first; i != connections.second; ++i) {
178  const auto& conn = i->second;
179  paddings[conn.output] = std::max(paddings[conn.output], conn.dest->getInputPadding(conn.input));
180  }
181 
182  // loop over connections
183  for (auto i = connections.first; i != connections.second; ++i) {
184  const auto& conn = i->second;
185  auto* dst = conn.dest;
186  connectedOutputs.set(conn.output);
187 
188  if (outputs[conn.output])
189  RuntimeError::check(src->acceptsStorageOutput(conn.output) ^ src->acceptsVectorOutput(conn.output) ^ src->acceptsTextureOutput(conn.output),
190  "Operation output accepting different types can only have a single connection");
191  // To avoid output type mismatch when connecting second time
192 
193  // if a regular Storage is accepted by both source and destination
194  if (src->acceptsStorageOutput(conn.output) && dst->acceptsStorageInput(conn.input)) {
195  const Size size = src->getOutputSize(conn.output);
196  Storage* storage = nullptr;
197 
198  // check if the output storage is already allocated
199  if (outputs[conn.output]) {
200  storage = static_cast<Storage*>(outputs[conn.output]);
201  refs[storage].push_back(dst);
202  }
203 
204  else {
205  // decide on reserved depth (if capping)
206  int depthCapping = 0;
207  if (sampledChannels[dst] > sampledChannelsLimit) {
208  // the op exceeds the limit
209  int min, max;
210  dst->getSampledChannels(conn.input, min, max);
211  const int cappingMargin = std::min(sampledChannelsLimit, size[2]) - min; // this is how much we can cap at the current input
212  if (cappingMargin > 0) {
213  depthCapping = std::min(cappingMargin, sampledChannels[dst] - sampledChannelsLimit);
214  // reduce the excess
215  sampledChannels[dst] -= depthCapping;
216  }
217  }
218 
219  // try to recycle an existing storage first
220  for (auto& i : refs) {
221  auto candidate = i.first;
222  auto& users = i.second;
223  const int reservedDepth = sampledChannelsLimit - 4 * candidate->getNumberOfTextures();
224  // check if (1) size matches, (2) padding is sufficient, (3) reserved depth matches the number of channels to cap or no capping
225  if (candidate->getSize() == size && candidate->getPadding() >= dst->getInputPadding(conn.input) && (reservedDepth == depthCapping || depthCapping == 0)
226  && users.empty())
227  {
228  // found!
229  storage = candidate;
230  users.push_back(dst);
231  break;
232  }
233  if (storage)
234  break;
235  }
236 
237  // no matching storage found, allocate a new one
238  if (!storage) {
239  storage = (size[0] == 1 && size[1] == 1) ?
240  // allocate flat storage if the output size is of 1x1 pixels
241  &allocateFlatStorage(gpu, size[2]) :
242  &allocateStorage(gpu,
243  size,
244  src->usesGpu(), !src->usesGpu(),
245  paddings[conn.output],
246  depthCapping
247  );
248  refs.emplace(storage, std::vector<AbstractOperation*>{ dst });
249  }
250 
251  // mark output as allocated
252  outputs[conn.output] = storage;
253  }
254 
255  // connect
256  src->setOutput(*storage, conn.output);
257  if (conn.shuffle > 0)
258  dst->setInput(Storage::View(*storage, conn.shuffle), conn.input);
259  else
260  dst->setInput(*storage, conn.input);
261  }
262 
263  // if a Vector is accepted
264  else if (src->acceptsVectorOutput(conn.output) && dst->acceptsVectorInput(conn.input)) {
265  RuntimeError::check(conn.shuffle == 0, "Cannot shuffle vector");
266  GL::Vector* vector;
267 
268  // check if the output storage is already allocated
269  if (outputs[conn.output])
270  vector = static_cast<GL::Vector*>(outputs[conn.output]);
271  else {
272  vector = &allocateVector(gpu, src->getOutputSize(conn.output).volume());
273  outputs[conn.output] = vector;
274  }
275 
276  // connect
277  src->setOutput(*vector, conn.output);
278  dst->setInput(*vector, conn.input);
279  }
280 
281  // if a texture is accepted
282  else if (src->acceptsTextureOutput(conn.output) && dst->acceptsTextureInput(conn.input)) {
283  RuntimeError::check(conn.shuffle == 0, "Cannot shuffle texture");
284  InternalBitmap* texture;
285 
286  // check if the output storage is already allocated
287  if (outputs[conn.output])
288  texture = static_cast<InternalBitmap*>(outputs[conn.output]);
289  else
290  outputs[conn.output] = texture = &allocateTexture(gpu, src->getOutputSize(conn.output));
291 
292  // connect
293  src->setOutput(*texture, conn.output);
294  dst->setInput(*texture, conn.input);
295  }
296 
297  else
298  throw RuntimeError("Cannot connect " + src->getName() + " (output #" + std::to_string(conn.output) + ") "
299  "to " + dst->getName() + " (input #" + std::to_string(conn.input) + "): storage type mismatch");
300  }
301 
302  // allocate user outputs if not yet
303  auto userOutputs = this->userOutputs.equal_range(src);
304  for (auto i = userOutputs.first; i != userOutputs.second; ++i) {
305  int idx = i->second.index;
306  if (idx >= src->getOutputCount())
307  throw InvalidArgument("Operation " + src->getName() + " does not have output #" + std::to_string(idx));
308  if (!connectedOutputs[idx])
309  if (src->acceptsStorageOutput(idx)) {
310  src->setOutput(allocateStorage(gpu, src->getOutputSize(idx), src->usesGpu(), !src->usesGpu()), idx);
311  }
312  else if (src->acceptsVectorOutput(idx)) {
313  src->setOutput(allocateVector(gpu, src->getOutputSize(idx).volume()), idx);
314  }
315  }
316 
317  // prepare operation
318  src->prepare(gpu, data, *this);
319 
320  // remove references to storages used by the current operation. This allows their reuse in other connections.
321  for (auto& i : refs) {
322  auto& users = i.second;
323  for (auto op = users.begin(); op != users.end(); )
324  if (*op == src)
325  users.erase(op);
326  else
327  ++op;
328  }
329 
330  // advance the progress bar
332  }
333 
334  data.close();
335  ready = true;
336 }
A set of boolean flags.
Definition: bitset.h:30
virtual void close()=0
Closes the collection after a reading session.
virtual void open()=0
Opens the collection to read chunks from it.
int getLimit(Limit limit) const
Definition: pipeline.cpp:936
@ TEXTURE_IMAGE_UNITS
maximum number of texture units per fragment shader
Storage & allocateFlatStorage(GraphicPipeline &gpu, const int size)
Allocates a new flat storage.
Definition: model.cpp:439
Storage & allocateStorage(GraphicPipeline &gpu, const Size size, bool forGpu=true, bool forCpu=false, const int pad=0, const int reservedChannels=0)
Allocates a new storage.
Definition: model.cpp:428
ProgressTracking preparingProgress
model preparation progress
Definition: model.h:123
GL::Vector & allocateVector(GraphicPipeline &gpu, const int size)
Allocates a vector that can be used as operation input or output.
Definition: model.cpp:447
InternalBitmap & allocateTexture(GraphicPipeline &gpu, const Size size)
Allocates a texture that can be used as operation input or output.
Definition: model.cpp:460
Maps a 3D tensor onto a storage.
Definition: storage.h:308
void reset(unsigned int max)
Resets the progress to zero.
CustomPoint< numeric > min(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:724
CustomPoint< numeric > max(const CustomPoint< numeric > &a, const CustomPoint< numeric > &b)
Definition: geometry.h:728
return(jlong) new Beatmup jlong jstring src

◆ isReady()

bool Beatmup::NNets::Model::isReady ( ) const
inline
Returns
true if the model is ready to be used for inference (prepare() has been called).

Definition at line 278 of file model.h.

278 { return ready; }

◆ execute()

void Model::execute ( TaskThread thread,
GraphicPipeline gpu 
)

Runs the inference.

Parameters
[in,out]threadTask thread instance
[in,out]gpuA graphic pipeline

Definition at line 339 of file model.cpp.

339  {
340  if (gpu)
342 
343  // reset the progress tracker
344  inferenceProgress.reset(ops.size());
345 
346  // loop through ops
347  for (auto op : ops) {
348  if (thread.isTaskAborted())
349  return;
350 
351  // start profiling
352  if (thread.isManaging() && profiler)
353  (*profiler)(op->getName());
354 
355  // run operation
356  try {
357  if (gpu)
358  op->execute(thread, *gpu);
359  else
360  op->execute(thread);
361  } catch (const std::exception& ex) {
362  throw InferenceTimeError(*op, ex);
363  }
364 
365  // get user outputs
366  auto userOutputs = this->userOutputs.equal_range(op);
367  for (auto it = userOutputs.first; it != userOutputs.second; ++it) {
368  int idx = it->second.index;
369  auto& data = it->second.data;
370  if (gpu)
371  if (op->acceptsStorageOutput(idx)) {
372  // get data pointer from storage
373  auto view = op->getOutput(idx);
374  if (!view.getStorage().isUpToDate(ProcessingTarget::CPU))
375  view.getStorage().pull(*gpu);
376 
377  // copy to the vector
378  Storage::Scanner scan(view);
379  scan.move(0, 0);
380  data.resize(view.getSize().volume());
381  for (auto it = data.begin(); it != data.end(); it += view.getDepth()) {
382  scan.fill(it, data.end());
383  ++scan;
384  }
385  }
386  else if (op->acceptsVectorOutput(idx)) {
387  GL::Vector* vector;
388  op->getOutput(vector, idx);
389  vector->fetch(*gpu, data);
390  }
391  }
392 
393  if (thread.isManaging()) {
394  // stop profiler
395  if (profiler) {
396  gpu->flush(); // wait till GPU is done
397  profiler->lap();
398  }
399 
400  // increase inference progress
402  }
403  }
404 }
void fetch(GraphicPipeline &gpu, std::vector< float > &output) const
Grabs vector values back from GPU to user memory.
void switchMode(Mode mode)
Switches GPU mode.
Definition: pipeline.cpp:941
@ INFERENCE
Textures are feature maps computed in fragment shaders.
void flush()
Waits until all operations submitted to GPU are finished.
Definition: pipeline.cpp:931
Wrapper for exceptions occuring during the model inference.
Definition: model.h:357
ProgressTracking inferenceProgress
inference progress
Definition: model.h:124
Scans a storageview in RAM for further computations on CPU.
Definition: storage.h:466
bool isManaging() const
Definition: parallelism.h:172
virtual bool isTaskAborted() const =0
Returns true if the task is asked to stop from outside.

◆ isOperationInModel()

bool Model::isOperationInModel ( const AbstractOperation operation) const

Checks if a specific operation makes part of the model.

Returns
true if the operation is in the model.

Definition at line 407 of file model.cpp.

407  {
408  for (auto op : ops)
409  if (op == &operation)
410  return true;
411  return false;
412 }

◆ getFirstOperation() [1/2]

AbstractOperation& Beatmup::NNets::Model::getFirstOperation ( )
inline

Definition at line 293 of file model.h.

293 { return *ops.front(); }

◆ getLastOperation() [1/2]

AbstractOperation& Beatmup::NNets::Model::getLastOperation ( )
inline

Definition at line 294 of file model.h.

294 { return *ops.back(); }

◆ getFirstOperation() [2/2]

const AbstractOperation& Beatmup::NNets::Model::getFirstOperation ( ) const
inline

Definition at line 295 of file model.h.

295 { return *ops.front(); }

◆ getLastOperation() [2/2]

const AbstractOperation& Beatmup::NNets::Model::getLastOperation ( ) const
inline

Definition at line 296 of file model.h.

296 { return *ops.back(); }

◆ getNumberOfOperations()

size_t Beatmup::NNets::Model::getNumberOfOperations ( ) const
inline

Definition at line 297 of file model.h.

297 { return ops.size(); }

◆ getOperation()

template<class OperationClass = AbstractOperation>
OperationClass& Beatmup::NNets::Model::getOperation ( const std::string &  operationName)
inline

Retrieves an operation by its name.

Definition at line 303 of file model.h.

303  {
304  return *static_cast<OperationClass*>((*this)[operationName]);
305  }

◆ getPreparingProgress()

const ProgressTracking& Beatmup::NNets::Model::getPreparingProgress ( ) const
inline

Returns model preparation progress tracking.

Definition at line 310 of file model.h.

310 { return preparingProgress; }

◆ getInferenceProgress()

const ProgressTracking& Beatmup::NNets::Model::getInferenceProgress ( ) const
inline

Returns inference progress tracking.

Definition at line 315 of file model.h.

315 { return inferenceProgress; }

◆ countMultiplyAdds()

unsigned long Model::countMultiplyAdds ( ) const

Provides an estimation of the number of multiply-adds characterizing the model complexity.

Queries the number of multiply-adds of every operation of the model and sums them up.

Definition at line 508 of file model.cpp.

508  {
509  unsigned long result = 0;
510  for (auto op : ops)
511  result += op->countMultiplyAdds();
512  return result;
513 }
Beatmup::IntPoint result

◆ countTexelFetches()

unsigned long Model::countTexelFetches ( ) const

Provides an estimation of the total number of texels fetched by all the operations in the model per image.

Definition at line 516 of file model.cpp.

516  {
517  unsigned long result = 0;
518  for (auto op : ops)
519  result += op->countTexelFetches();
520  return result;
521 }

◆ getMemorySize()

size_t Model::getMemorySize ( ) const

Returns the amount of texture memory in bytes currently allocated by the model to run the inference.

When the model is ready to run, this represents the size of the memory needed to store internal data during the inference. The resulting value does not include the size of GLSL shaders binaries stored in GPU memory which can be significant.

Definition at line 524 of file model.cpp.

524  {
525  size_t size = 0;
526  for (auto& entry : storages)
527  size += entry->getMemorySize();
528  for (auto& entry : vectors)
529  size += entry->getMemorySize();
530  for (auto& entry : textures)
531  size += entry->getMemorySize();
532  return size;
533 }

◆ serialize()

Listing Beatmup::NNets::Model::serialize ( ) const

Returns serialized representation of the model as a Listing.

◆ serializeToString()

std::string Model::serializeToString ( ) const

Returns serialized representation of the model as a string.

Definition at line 579 of file model.cpp.

579  {
580  Listing listing(serialize());
581  std::stringstream strstr;
582  listing.printOut(strstr);
583  return strstr.str();
584 }
Parser of simple YAML-like listings.
Definition: listing.h:40
Listing serialize() const
Returns serialized representation of the model as a Listing.

◆ setProfiler()

void Beatmup::NNets::Model::setProfiler ( Profiler profiler)
inline

Attaches a profiler instance to meter the execution time per operation during the inference.

This may slow down the inference.

Parameters
[in]profilerA profiler instance or null pointer (to disable the profiling)

Definition at line 350 of file model.h.

350 { this->profiler = profiler; }

Member Data Documentation

◆ connections

std::multimap<const AbstractOperation*, Connection> Beatmup::NNets::Model::connections
private

source operation => connection descriptor mapping

Definition at line 113 of file model.h.

◆ userOutputs

std::multimap<const AbstractOperation*, UserOutput> Beatmup::NNets::Model::userOutputs
private

operation => user output mapping

Definition at line 114 of file model.h.

◆ storages

std::vector<Storage*> Beatmup::NNets::Model::storages
private

allocated storages used during the inference

Definition at line 116 of file model.h.

◆ vectors

std::vector<GL::Vector*> Beatmup::NNets::Model::vectors
private

allocated vectors used during the inference

Definition at line 117 of file model.h.

◆ textures

std::vector<InternalBitmap*> Beatmup::NNets::Model::textures
private

allocated images used during the inference

Definition at line 118 of file model.h.

◆ profiler

Profiler* Beatmup::NNets::Model::profiler
private

pointer to a Profiler attached to the model

Definition at line 119 of file model.h.

◆ ops

std::vector<AbstractOperation*> Beatmup::NNets::Model::ops
protected

model operations

Definition at line 122 of file model.h.

◆ preparingProgress

ProgressTracking Beatmup::NNets::Model::preparingProgress
protected

model preparation progress

Definition at line 123 of file model.h.

◆ inferenceProgress

ProgressTracking Beatmup::NNets::Model::inferenceProgress
protected

inference progress

Definition at line 124 of file model.h.

◆ ready

bool Beatmup::NNets::Model::ready
protected

if true, ops are connected to each other and storages are allocated

Definition at line 125 of file model.h.


The documentation for this class was generated from the following files: