# Класс CDistributedInference

<!-- TOC -->

- [Класс CDistributedInference](#class-cdistributedinference)
    - [Настройки](#настройки)
    - [Использование](#использование)

<!-- /TOC -->

Класс реализует инференс нейросети на нескольких потоках CPU одновременно.
Число копий переданной нейросети будет = числу потоков инференса.
При чём при инференсе нейросети не будут хранить каждая у себя свою копию обученных параметров слоёв.
Данные сети будут делить между собой общие обученные параметры слоёв, что уменьшает потребление памяти инференса.


## Настройки

**1. Обученная Нейросеть для Инференса**

   Сеть должна быть подготовлена к инференсу: 

   * Удалены лишние слои, необходимые только для обучения
   * Все слои имеют инициализированные веса, как минимум ранее был произведён reshahe сети

   **1.1.** из памяти программы
   ```
   const CDnn& dnn
   ```
   **1.2.** из файла на диске
   ```
   CArchive& archive
   ```
   Также тогда понадобится начальное состояние генератора случайных чисел
   ```
   int seed = 42
   ```
   Иначе состояние генератора случайных чисел бралось бы непосредсвенно из переданной нейро-сети.

**2. Число Потоков ЦПУ**

   Определяет число потоков, на которых одновременно будет выполняться инференс.

   Если поставить = 0, то число потоков выберется по количеству потоков процессора вашего компьютера
   ```
   int threadsCount
   ```

**3. Оптимизацию Архитектуры Сети**

   Произвести ли оптимизацию архитектуры сети, чтобы ускорить её инференс

   При этой оптимизации также будет выполнены оптимизации, подходящие только для CPU
   ```
   bool optimizeDnn = true
   ```

**4. Максимальный Объём Памяти**

   Можно указать максимальный объём памяти, который могут использовать все сети в совокупности

   Если поставить = 0, то ограничения памяти ставится не будет, и данный инференс может утилизировать столько памяти, сколько есть на компьютере
   ```
   size_t memoryLimit = 0
   ```

**5. Число моделей**

   Число копий переданной нейросети будет будет равно числу потоков.

   На каждом потоке будет выполняться ровно 1 нейросеть.
   ```
   int GetModelCount()
   ```

**6. Запустить Инференс на Всех Нейросетях Одновременно**

   Запустить инференс на всех сетях одновременно можно при помощи метода
   ```
   void RunOnce( IDistributedDataset& data )
   ```
   При чём главный поток, в котором произошёл вызов будет ожидать окончаний работы на всех сетях
   Для запуска инференса потребуется объект класса `IDistributedDataset`, который устанавливает 
   на входы каждой сети необходимые данные, согласно датасету

**7. Получение результатов**

   Результат можно получить в виде массива выходных блобов данных.

   Поскольку следующий инференс затрёт данные в памяти данных блобов, читать их можно только между
   текущим и следующим запуском `RunOnce`. Если эти данные вам потребуются позднее, то их нужно
   скопировать из этих блобов самостоятельно.
   ```
   void GetLastBlob( const CString& layerName, CObjectArray<const CDnnBlob>& blobs ) const
   ```


## Использование

Для использования данного класса потребуется объект, 
который будет подавать входы каждой сети необходимые данные, согласно датасету

На примере показано, как данный класс для всех запусков инференса для всех моделей устанавливает одинаковый вход
в виде массива единиц
```cpp
class CCustomDataset : public IDistributedDataset {
public:
	CCustomDataset( int inputSize ) : inputSize( inputSize ) { input.Add( 1, inputSize ); }

	int SetInputBatch( CDnn& dnn, int /*thread_id*/ ) override {
		CPtr<CDnnBlob> in = CDnnBlob::CreateTensor( dnn.GetMathEngine(), CT_Float, { 1, 1, 1, 1, 1, 1, inputSize } );
		in->CopyFrom( input.GetPtr() );
		// Для установки входов, надо знать их количество, размерности и имена в сети
		CheckCast<CSourceLayer>( dnn.GetLayer( "data" ) )->SetBlob( in );
		// Если результат работы > 0, значит для данной сети данные были установлены,
		// Иначе инференс для сети не был бы запущен
		return 1;
	}

private:
	const int inputSize;
	CArray<float> input;
};
```

Пример выполнения много-поточного инференса:
```cpp
	// Имеется обученная сеть
	CRandom random( 42 );
	CDnn dnn( random, MathEngine() );

	// Создаётся для этой сети объект для запуска много-поточного инференса
	CDistributedInference distributed( dnn, /*count*/0 );

	{
		// Датасет для подачи на вход
		CCustomDataset dataset( inputSize );
		// Выполняется много-поточный инференс
		distributed.RunOnce( dataset );
	}

	{ // Получаем результаты без копирования
		CObjectArray<const CDnnBlob> results;
		distributed.GetLastBlob( "sink", results );
		// Применяем результаты для своих нужд
		// ...
	}

	{
		// Датасет для подачи на вход
		CCustomDataset datasetNew( inputSize );
		// Выполняется повторно много-поточный инференс для новых данных
		distributed.RunOnce( datasetNew );
		// Получаем результаты снова
	}
```

Нейросеть может быть считана из архива и 
объект для запуска много-поточного инференса быть сконструирован с необходимыми настройками
```cpp
	CDistributedInference distributed( archive,
		/*threadsCount*/ 4,
		/*seed*/ 42,
		/*optimizeDnn*/ false,
		/*memoryLimit*/ 300 * 1024 * 1024
	);
```
