Запуск кількох потоків в одному графі
| Поле | Значення |
|---|---|
| Категорія | Графи та конвеєри |
| Складність | Просунутий |
| Орієнтовний час читання | 20-25 minutes |
| Мітки | graph, multistream, scheduler, join |
У попередніх розділах ми обробляли один вхідний потік і виводили один вихідний. У реальних системах з кількома камерами та паралельними гілками все складніше: декілька потоків обробляються незалежно, і їхні результати необхідно правильно об’єднати, перш ніж їх можна буде використовувати в подальших процесах. У цьому розділі показано базовий елемент об’єднання, який робить цей процес детермінованим — це граф об’єднання з двома іменованими входами та одним іменованим виходом, який генерує пакет лише тоді, коли обидві сторони створили відповідний кадр.
Кожен зразок, який ви передаєте, містить stream_id та frame_id. Політика об’єднання ByFrame чекає, поки обидва іменовані входи (left та right) не передадуть зразок з однаковим frame_id, після чого генерує рівно один об’єднаний пакет. В результаті ви створите гр аф об’єднання, розподілите детерміноване навантаження для кожного потоку/кадру через два входи та об’єднаєте пакети, перевіривши кількість вихідних даних і те, що кожен пакет містить два поля.
Покроковий огляд
Створення графа об’єднання
graphs::Combine (C++) / graphs.combine (Python) повертає звичайний загальнодоступний фрагмент Graph — в ньому немає нічого особливого, окрім його форми: два іменовані вхідні потоки, один іменований вихідний потік і політика об’єднання. Ми передаємо ["left", "right"] як імена вхідних потоків, "combined" як ім’я вихідного потоку та CombinePolicy.ByFrame для вибору відповідності за ідентифікатором кадру. Друк describe() показує отриману топологію, а build() перетворює опис на в иконуваний об’єкт. За замовчуванням граф працює асинхронно, тому кожен потік може незалежно просуватися вперед.
Вихідна черга має обмежений розмір. Замість того, щоб виділяти достатньо місця в черзі для всього обсягу роботи, у цьому прикладі кожен об’єднаний пакет витягується перед тим, як буде додано наступну пару. Виробник і споживач просуваються разом, тому використання пам’яті залишається обмеженим, навіть коли кількість кадрів збільшується.
CombinePolicy.ByFrame здійснює відповідність за Sample.frame_id; CombinePolicy.ByPts є альтернативою, яка здійснює відповідність за часовими мітками презентації (Sample.pts_ns), коли кадри не мають чіткого індексу кадру.
simaai::neat::Graph graph = simaai::neat::graphs::Combine({"left", "right"}, "combined",
simaai::neat::CombinePolicy::ByFrame);
std::cout << graph.describe() << "\n";
const int expected = streams * frames;
simaai::neat::Run run = graph.build();
Передайте потоки
Тепер ми обробляємо навантаження. Для кожного кадру та кожного потоку ми синтезуємо невеликий детермінований RGB-зразок, позначений його stream_id та унікальним frame_id, а потім передаємо його в обидва вказані вхідні потоки. Оскільки ідентифікатори обчислюються детерміновано (frame * streams + sid), з’єднання має однозначне відповідне поєднання для пошуку: кадр left N завжди має відпов ідний кадр right N. Після відповідного right передавання ми очищаємо об’єднаний вихід цієї пари, перш ніж перейти до наступної пари.
Кожен зразок явно створюється як Sample, що містить Tensor (HWC, UInt8, RGB) із встановленими frame_id та stream_id; run.push("left", sample) повертає булеве значення, яке слід перевірити на відповідність run.last_error().
if (!run.push("left", make_rgb_sample(std::to_string(sid), logical_frame))) {
throw std::runtime_error("left push failed: " + run.last_error());
}
if (!run.push("right", make_rgb_sample(std::to_string(sid), logical_frame))) {
throw std::runtime_error("right push failed: " + run.last_error());
}