1
2 
Асинхронность и 
сопрограммы: 
обработка данных 
Григорий Демченко 
cтарший разработчик
3 
План Обзор технологий Предлагаемый поход Вопросы Обсуждение Дальнейшие улучшения Выводы
4 
Обзор технологий
5 
Map Возможна параллельная обработка с помощью контейнера для хранения результатов Не требуется синхронизация для обработки, только ожидание завершения стадий Каждая стадия ждет завершения предыдущей Проблемы: 
–Агрегация 
–Фильтрация 
–Работает хорошо только 1 к 1 
Парсинг 
Преобразование 
Обработка
6 
Reduce 
Агрегация Подсчет количества определенных слов Подсчет статистики по определенным метрикам Суть: уменьшение количества данных, «сжатие» для определенных целей 
#1 
Reduce 
#2 
#3
7 
Проблемы традиционных подходов Требуется синхронизация Сложный контроль потребления памяти Последовательное выполнение стадий Нагрузка на планировщик Внешние взаимодействия: 
–сетевые запросы 
–обращение к базе данных 
–обращение к диску
8 
Предлагаемый подход
9 
Что хочется? Эффективно использовать сеть: получение и посылка данных Эффективно использовать процессор: запускать задачи, не дожидаясь завершения стадий Эффективно использовать память: выделять память под конкретные нужды Разгрузка планировщика: планировать более крупные задачи Нацеленность на результат: получение первых конечных данных как можно быстрее По возможности не использовать синхронизацию явно Максимизация параллельности операций Обработка сложных сценариев (например, петли)
10 
Задача Выдать 20 наиболее используемых слов сайта
11 
Обработка данных Преобразование одних данных в другие Data Oriented Design – всё есть преобразование данных 
Преобразование 
Исходные данные 
Результат
12 
Диаграмма преобразования данных 
Фильтр 
Парсинг: URL 
Загрузка 
Парсинг: текст 
Парсинг: 
href 
Разделение слов 
host 
path 
host 
body 
url 
body 
paragraph 
word 
url
13 
Диаграмма преобразования данных 
Фильтр 
Парсинг: URL 
Загрузка 
Парсинг: 
текст 
Парсинг: href 
Разделение слов 
host 
path 
host body 
url 
body 
paragraph 
Async DNS resolving 
Async partial reading 
Header parsing 
word 
url
14 
Диаграмма преобразования данных 
Фильтр 
Парсинг: URL 
Загрузка 
Парсинг: 
текст 
Парсинг: href 
Разделение слов 
host 
path 
host body 
url 
body 
paragraph 
Async DNS resolving 
Async partial reading 
Header parsing 
word 
url 
url
15 
Как реализовать? Обратные связи – традиционные способы не подходят Асинхронность в обработчиках
16 
Канал 
Парсинг: текст 
Разделение слов 
paragraph 
Канал: 
Channel<std::string>
17 
Канал: параллельность 
Парсинг: 
текст 
Разделение слов 
paragraph 
Канал: 
Channel<std::string> 
Парсинг: 
текст 
Парсинг: 
текст 
Разделение слов
18 
Канал: интерфейс put – посылка значения get – извлечение значения Потокобезопасный Можно: for (auto&& w: wordsChan) { ... } 
template<typename T> 
struct Channel 
{ 
bool empty() const; 
void put(T val); 
bool get(T& val); 
T get(); 
void open(); 
void close(); 
};
19 
Канал: параллельность 
Парсинг: 
текст 
Разделение слов 
paragraph 
Канал: Channel<std::string> 
Парсинг: 
текст 
Парсинг: 
текст 
Разделение слов 
Channel::put 
Channel::get
20 
Канал: параллельность 
Парсинг: 
текст 
Разделение слов 
paragraph 
Канал: 
Channel<std::string> 
Парсинг: 
текст 
Парсинг: 
текст 
Разделение слов 
Channel::put 
Channel::get 
Channel::close 
returns: false
21 
Сложности Канал может быть пустым, что делать потребителю? Как обрабатывать асинхронные взаимодействия? Сколько использовать обработчиков для параллельной работы?
22 
Решение: сопрограммы! Подпрограмма: выполняются все действия перед возвратом управления Сопрограмма: частичное выполнение действий перед возвратом управления Сопрограмма обобщает понятие подпрограммы, т.е. подпрограмма - это частный случай сопрограммы. Для эффективной реализации на С++ будем использовать boost.context.
23 
Сопрограмма 
Сопрограмма 
Планировщик 
async: socket.read() 
yield 
read completed 
Channel: get() 
… 
Channel empty: get() 
… 
Channel::put 
… 
resume 
yield 
resume
24 
Обработчики Связь двух каналов 
template<typename T_src, typename T_dst, typename F_pipe> 
void piping(T_src& s, T_dst& d, F_pipe f, int n = 1) 
{ 
goN(n, [&s, &d, f] { 
auto c = closer(d); 
f(s, d); 
}); 
} 
piping(c1, c2, [](Channel<int>& src, Channel<int>& dst) { 
for (int v: src) 
dst.put(v + 1); 
}); 
closer: автоматически закрывает канал 
goN: асинхронно запускает n сопрограмм
25 
Обработчики Связь двух каналов 1 к 1 (map) 
template<typename T_src, typename T_dst, typename F_pipe> 
void piping1to1(T_src& s, T_dst& d, F_pipe f, int n = 1) 
{ 
piping(s, d, [f] (T_src& s, T_dst& d) { 
for (auto&& v: s) 
d.put(std::move(f(v))); 
}, n); 
} 
piping1to1(c1, c2, [](int v) { 
return v + 1; 
}, 10); 
будет запущено 10 обработчиков
26 
Разделение каналов 
go([&] { 
auto c1 = closer(contentHref); 
auto c2 = closer(contentText); 
for (auto&& c: content) 
{ 
contentHref.put(c); 
contentText.put(c.second); 
} 
}); 
Split 
Парсинг: текст 
Парсинг: 
href 
host + body 
body 
host + body 
body
27 
Пример обработчика: загрузка 
StrPair loadContent(const StrPair& url) 
{ 
auto&& host = url.first; 
auto&& path = url.second; 
Resolver r; 
Resolver::EndPoints ends = r.resolve(host, 80); 
VERIFY(ends != Resolver::EndPoints(), "Cannot resolve hostname: " + host); 
Socket s; 
s.connect(*ends); 
// ... 
} 
Загрузка 
host + body 
host + path 
асинхронный 
асинхронный
28 
Пример обработчика: ссылки Преобразование ссылки из строки в host+path 
StrPair parseUrl(const Str& url) 
{ 
static const regex e("http://([wd._-]*[wd_-]+)(.*)", regex::icase); 
smatch what; 
bool result = regex_search(url, what, e); 
if (!result) 
return {}; 
Str host = what[1]; 
Str path = what[2]; 
if (path.empty()) 
path = "/"; 
return {host, path}; 
} 
piping1to01(filteredUrl, parsedUrl, parseUrl); 
piping1to01: игнорирует пустые элементы
29 
Пример обработчика: слова Разделение текста на отдельные слова 
void splitWords(const Str& text, Channel<Str>& words) 
{ 
static const regex e("([a-zA-Z]+)"); 
sregex_token_iterator i = make_regex_token_iterator(text, e, 1); 
sregex_token_iterator ie; 
while (i != ie) 
words.put(*i++); 
} 
piping1toMany(text, words, splitWords); 
piping1toMany: один ко многим
30 
Петля Помещаем url после разбора снова в фильтр: piping1toMany(contentHref, url, parseHref); Как оборвать петлю? 
Фильтр 
Парсинг: URL 
Загрузка 
Парсинг: 
href 
host 
path 
host 
body 
url 
url
31 
Обработка петель Решение: использовать closeAndWait 
int threads = std::thread::hardware_concurrency(); 
ThreadPool tp(threads, "tp"); 
Channel<Str> url; 
url.put("http://www.boost.org"); 
closeAndWait(tp, url); 
template<typename T_channel> 
void closeAndWait(ThreadPool& tp, T_channel& c) 
{ 
tp.wait(); 
c.close(); 
tp.wait(); 
} 
засыпание обработчиков на Channel::get 
закрываем самый первый канал 
выход из всех обработчиков
32 
Результат Результат для 1000 страниц http://www.boost.org 
Слово 
Количество 
the 
10988 
of 
4943 
to 
4838 
and 
4837 
a 
4421 
boost 
3193 
for 
3136 
is 
3103 
in 
2637 
library 
1951 
that 
1755 
be 
1622 
as 
1358 
are 
1310 
with 
1301 
c 
1260 
it 
1170 
or 
1157 
this 
1095 
can 
1094
33 
Вопросы
34 
Количество обработчиков Какое число обработчиков необходимо в случае: 
–Лёгких задач? 
–Тяжелых задач? 
–Сетевых операций?
35 
Количество обработчиков Какое число обработчиков необходимо в случае: 
–Лёгких задач ~1-2 
–Тяжелых задач = числу ядер 
–Сетевых операций >> числа ядер
36 
Обработка блокирующих вызовов Каким образом обрабатывать блокирующие вызовы в случае: 
–Обращения к диску? 
–Синхронные запросы к удаленной БД?
37 
Обработка блокирующих вызовов Каким образом обрабатывать блокирующие вызовы в случае: 
–Обращения к диску 
–Синхронные запросы к удаленной БД 
Ответ: запускать в отдельном пуле потоков, число потоков >> числа ядер
38 
Кеширование DNS запросов 
Загрузка 
host + body 
host + path
39 
Кеширование DNS запросов 
struct DNSCache 
{ 
void add(const Str& host); 
Resolver::Endpoints get(const Str& host) const; 
private: 
static Resolver::Endpoints resolve(const Str& host); 
mutable std::mutex mutex; 
std::unordered_map<Str, Resolver::Endpoints> cache; 
}; 
Загрузка 
host + body 
host + path
40 
Кеширование DNS запросов 
struct DNSCache 
{ 
void add(const Str& host); 
Resolver::Endpoints get(const Str& host) const; 
private: 
static Resolver::Endpoints resolve(const Str& host); 
mutable std::mutex mutex; 
std::unordered_map<Str, Resolver::Endpoints> cache; 
}; 
Загрузка 
host + body 
host + path
41 
Кеширование DNS запросов 
Загрузка 
host + body 
host + path 
Resolver 
host + endpoint 
состояние: 
host → endpoint
42 
Нагрузка на планировщик 
Какова нагрузка на планировщик в случае частого обмена сообщениями через каналы? 
bool get(T& val) { 
Lock lock(mutex); 
if (!queue.empty()) { 
val = std::move(queue.front()); 
queue.pop(); 
return true; 
} 
if (closed) 
return false; 
Waiter w(val); 
waiters.push(w); 
lock.release(); 
deferProceed([this, &w](Handler proceed) { 
w.setProceed(std::move(proceed)); 
mutex.unlock(); 
}); 
return w.hasValue(); 
} 
значение в очереди => сразу возвращаем 
создаем ожидающего и добавляем его в список 
устанавливаем обработчик для возобновления выполнения 
после продолжения значение записывается в переменную val (при наличии)
43 
Нагрузка на планировщик 
Планировщик используется только при вызове proceed()! 
Combining – техника; нет нагрузки в случае простаивания 
void put(T val) 
{ 
Lock lock(mutex); 
Waiter* w = waiters.pop(); 
if (w) 
{ 
lock.unlock(); 
w->proceed(std::move(val)); 
} 
else 
{ 
queue.emplace(std::move(val)); 
} 
} 
если есть ожидающий => передаем значение непосредственно ему и возобновляем выполнение 
добавляем значение в очередь 
аналогично реализуются: 
•неблокирующие мьютексы 
•неблокирующие condition variables
44 
Обсуждение
45 
Петли Легко реализуются Нет проблем с завершением
46 
Каналы: ограничение по размеру Реализация: 2 очереди для ожидания вместо одной Ограничивает потребление памяти Сетевое использование: TCP flow control Контролирует latency
47 
Обработчики и состояние Отдавайте предпочтение обработчикам без состояний, т.к. они позволяют параллельное выполнение стадии Reduce-стадии как правило имеют состояние Часто можно разбить reduce-стадии на несколько независимых, результат объединить на следующей стадии
48 
Дальнейшие улучшения
49 
Буферизированные каналы Позволяют снизить lock contention на каналы Увеличивают throughput Увеличивают latency
50 
Планировщик «в глубину» Обычно используют планировщик «в ширину», т.е. очередь задач. Вместо очереди задач можно использовать стек задач. Получаем планировщик «в глубину» с другими свойствами.
51 
Планировщик «в глубину» 
Преимущества: Более быстрое получение первых результатов Снижение нагрузки на обработчики за счет более равномерного распределения данных между стадиями Уменьшение потребления памяти «Прогретые» процессорные кеши Отлично работает в связке с bounded каналами, заставляя данные более активно перемещаться к результату 
Недостатки: Отсутствие fairness Потеря контроля над latency
52 
Планы на будущее Распределенность Отказоустойчивость Оптимизация планировщика
53 
Выводы
54 
Предлагаемый подход позволяет Реализовать простые сценарии (фильтрация, кеширование, парсинг, преобразование и т.д.) Реализовать сложные сценарии (петли, split, merge) Использовать асинхронное сетевое взаимодействие Забыть про синхронизацию Простая параллелизация: 
–Стадии выполняются параллельно, в отличие от MR 
–Несколько обработчиков одной стадии также выполняются параллельно
55 
Спасибо за внимание!
56 
http://habrahabr.ru/users/gridem 
https://github.com/gridem/Synca 
gridem@yandex-team.ru 
https://bitbucket.org/gridem/synca

Григорий Демченко, “Асинхронность и сопрограммы: обработка данных“

  • 1.
  • 2.
    2 Асинхронность и сопрограммы: обработка данных Григорий Демченко cтарший разработчик
  • 3.
    3 План Обзортехнологий Предлагаемый поход Вопросы Обсуждение Дальнейшие улучшения Выводы
  • 4.
  • 5.
    5 Map Возможнапараллельная обработка с помощью контейнера для хранения результатов Не требуется синхронизация для обработки, только ожидание завершения стадий Каждая стадия ждет завершения предыдущей Проблемы: –Агрегация –Фильтрация –Работает хорошо только 1 к 1 Парсинг Преобразование Обработка
  • 6.
    6 Reduce АгрегацияПодсчет количества определенных слов Подсчет статистики по определенным метрикам Суть: уменьшение количества данных, «сжатие» для определенных целей #1 Reduce #2 #3
  • 7.
    7 Проблемы традиционныхподходов Требуется синхронизация Сложный контроль потребления памяти Последовательное выполнение стадий Нагрузка на планировщик Внешние взаимодействия: –сетевые запросы –обращение к базе данных –обращение к диску
  • 8.
  • 9.
    9 Что хочется?Эффективно использовать сеть: получение и посылка данных Эффективно использовать процессор: запускать задачи, не дожидаясь завершения стадий Эффективно использовать память: выделять память под конкретные нужды Разгрузка планировщика: планировать более крупные задачи Нацеленность на результат: получение первых конечных данных как можно быстрее По возможности не использовать синхронизацию явно Максимизация параллельности операций Обработка сложных сценариев (например, петли)
  • 10.
    10 Задача Выдать20 наиболее используемых слов сайта
  • 11.
    11 Обработка данныхПреобразование одних данных в другие Data Oriented Design – всё есть преобразование данных Преобразование Исходные данные Результат
  • 12.
    12 Диаграмма преобразованияданных Фильтр Парсинг: URL Загрузка Парсинг: текст Парсинг: href Разделение слов host path host body url body paragraph word url
  • 13.
    13 Диаграмма преобразованияданных Фильтр Парсинг: URL Загрузка Парсинг: текст Парсинг: href Разделение слов host path host body url body paragraph Async DNS resolving Async partial reading Header parsing word url
  • 14.
    14 Диаграмма преобразованияданных Фильтр Парсинг: URL Загрузка Парсинг: текст Парсинг: href Разделение слов host path host body url body paragraph Async DNS resolving Async partial reading Header parsing word url url
  • 15.
    15 Как реализовать?Обратные связи – традиционные способы не подходят Асинхронность в обработчиках
  • 16.
    16 Канал Парсинг:текст Разделение слов paragraph Канал: Channel<std::string>
  • 17.
    17 Канал: параллельность Парсинг: текст Разделение слов paragraph Канал: Channel<std::string> Парсинг: текст Парсинг: текст Разделение слов
  • 18.
    18 Канал: интерфейсput – посылка значения get – извлечение значения Потокобезопасный Можно: for (auto&& w: wordsChan) { ... } template<typename T> struct Channel { bool empty() const; void put(T val); bool get(T& val); T get(); void open(); void close(); };
  • 19.
    19 Канал: параллельность Парсинг: текст Разделение слов paragraph Канал: Channel<std::string> Парсинг: текст Парсинг: текст Разделение слов Channel::put Channel::get
  • 20.
    20 Канал: параллельность Парсинг: текст Разделение слов paragraph Канал: Channel<std::string> Парсинг: текст Парсинг: текст Разделение слов Channel::put Channel::get Channel::close returns: false
  • 21.
    21 Сложности Каналможет быть пустым, что делать потребителю? Как обрабатывать асинхронные взаимодействия? Сколько использовать обработчиков для параллельной работы?
  • 22.
    22 Решение: сопрограммы!Подпрограмма: выполняются все действия перед возвратом управления Сопрограмма: частичное выполнение действий перед возвратом управления Сопрограмма обобщает понятие подпрограммы, т.е. подпрограмма - это частный случай сопрограммы. Для эффективной реализации на С++ будем использовать boost.context.
  • 23.
    23 Сопрограмма Сопрограмма Планировщик async: socket.read() yield read completed Channel: get() … Channel empty: get() … Channel::put … resume yield resume
  • 24.
    24 Обработчики Связьдвух каналов template<typename T_src, typename T_dst, typename F_pipe> void piping(T_src& s, T_dst& d, F_pipe f, int n = 1) { goN(n, [&s, &d, f] { auto c = closer(d); f(s, d); }); } piping(c1, c2, [](Channel<int>& src, Channel<int>& dst) { for (int v: src) dst.put(v + 1); }); closer: автоматически закрывает канал goN: асинхронно запускает n сопрограмм
  • 25.
    25 Обработчики Связьдвух каналов 1 к 1 (map) template<typename T_src, typename T_dst, typename F_pipe> void piping1to1(T_src& s, T_dst& d, F_pipe f, int n = 1) { piping(s, d, [f] (T_src& s, T_dst& d) { for (auto&& v: s) d.put(std::move(f(v))); }, n); } piping1to1(c1, c2, [](int v) { return v + 1; }, 10); будет запущено 10 обработчиков
  • 26.
    26 Разделение каналов go([&] { auto c1 = closer(contentHref); auto c2 = closer(contentText); for (auto&& c: content) { contentHref.put(c); contentText.put(c.second); } }); Split Парсинг: текст Парсинг: href host + body body host + body body
  • 27.
    27 Пример обработчика:загрузка StrPair loadContent(const StrPair& url) { auto&& host = url.first; auto&& path = url.second; Resolver r; Resolver::EndPoints ends = r.resolve(host, 80); VERIFY(ends != Resolver::EndPoints(), "Cannot resolve hostname: " + host); Socket s; s.connect(*ends); // ... } Загрузка host + body host + path асинхронный асинхронный
  • 28.
    28 Пример обработчика:ссылки Преобразование ссылки из строки в host+path StrPair parseUrl(const Str& url) { static const regex e("http://([wd._-]*[wd_-]+)(.*)", regex::icase); smatch what; bool result = regex_search(url, what, e); if (!result) return {}; Str host = what[1]; Str path = what[2]; if (path.empty()) path = "/"; return {host, path}; } piping1to01(filteredUrl, parsedUrl, parseUrl); piping1to01: игнорирует пустые элементы
  • 29.
    29 Пример обработчика:слова Разделение текста на отдельные слова void splitWords(const Str& text, Channel<Str>& words) { static const regex e("([a-zA-Z]+)"); sregex_token_iterator i = make_regex_token_iterator(text, e, 1); sregex_token_iterator ie; while (i != ie) words.put(*i++); } piping1toMany(text, words, splitWords); piping1toMany: один ко многим
  • 30.
    30 Петля Помещаемurl после разбора снова в фильтр: piping1toMany(contentHref, url, parseHref); Как оборвать петлю? Фильтр Парсинг: URL Загрузка Парсинг: href host path host body url url
  • 31.
    31 Обработка петельРешение: использовать closeAndWait int threads = std::thread::hardware_concurrency(); ThreadPool tp(threads, "tp"); Channel<Str> url; url.put("http://www.boost.org"); closeAndWait(tp, url); template<typename T_channel> void closeAndWait(ThreadPool& tp, T_channel& c) { tp.wait(); c.close(); tp.wait(); } засыпание обработчиков на Channel::get закрываем самый первый канал выход из всех обработчиков
  • 32.
    32 Результат Результатдля 1000 страниц http://www.boost.org Слово Количество the 10988 of 4943 to 4838 and 4837 a 4421 boost 3193 for 3136 is 3103 in 2637 library 1951 that 1755 be 1622 as 1358 are 1310 with 1301 c 1260 it 1170 or 1157 this 1095 can 1094
  • 33.
  • 34.
    34 Количество обработчиковКакое число обработчиков необходимо в случае: –Лёгких задач? –Тяжелых задач? –Сетевых операций?
  • 35.
    35 Количество обработчиковКакое число обработчиков необходимо в случае: –Лёгких задач ~1-2 –Тяжелых задач = числу ядер –Сетевых операций >> числа ядер
  • 36.
    36 Обработка блокирующихвызовов Каким образом обрабатывать блокирующие вызовы в случае: –Обращения к диску? –Синхронные запросы к удаленной БД?
  • 37.
    37 Обработка блокирующихвызовов Каким образом обрабатывать блокирующие вызовы в случае: –Обращения к диску –Синхронные запросы к удаленной БД Ответ: запускать в отдельном пуле потоков, число потоков >> числа ядер
  • 38.
    38 Кеширование DNSзапросов Загрузка host + body host + path
  • 39.
    39 Кеширование DNSзапросов struct DNSCache { void add(const Str& host); Resolver::Endpoints get(const Str& host) const; private: static Resolver::Endpoints resolve(const Str& host); mutable std::mutex mutex; std::unordered_map<Str, Resolver::Endpoints> cache; }; Загрузка host + body host + path
  • 40.
    40 Кеширование DNSзапросов struct DNSCache { void add(const Str& host); Resolver::Endpoints get(const Str& host) const; private: static Resolver::Endpoints resolve(const Str& host); mutable std::mutex mutex; std::unordered_map<Str, Resolver::Endpoints> cache; }; Загрузка host + body host + path
  • 41.
    41 Кеширование DNSзапросов Загрузка host + body host + path Resolver host + endpoint состояние: host → endpoint
  • 42.
    42 Нагрузка напланировщик Какова нагрузка на планировщик в случае частого обмена сообщениями через каналы? bool get(T& val) { Lock lock(mutex); if (!queue.empty()) { val = std::move(queue.front()); queue.pop(); return true; } if (closed) return false; Waiter w(val); waiters.push(w); lock.release(); deferProceed([this, &w](Handler proceed) { w.setProceed(std::move(proceed)); mutex.unlock(); }); return w.hasValue(); } значение в очереди => сразу возвращаем создаем ожидающего и добавляем его в список устанавливаем обработчик для возобновления выполнения после продолжения значение записывается в переменную val (при наличии)
  • 43.
    43 Нагрузка напланировщик Планировщик используется только при вызове proceed()! Combining – техника; нет нагрузки в случае простаивания void put(T val) { Lock lock(mutex); Waiter* w = waiters.pop(); if (w) { lock.unlock(); w->proceed(std::move(val)); } else { queue.emplace(std::move(val)); } } если есть ожидающий => передаем значение непосредственно ему и возобновляем выполнение добавляем значение в очередь аналогично реализуются: •неблокирующие мьютексы •неблокирующие condition variables
  • 44.
  • 45.
    45 Петли Легкореализуются Нет проблем с завершением
  • 46.
    46 Каналы: ограничениепо размеру Реализация: 2 очереди для ожидания вместо одной Ограничивает потребление памяти Сетевое использование: TCP flow control Контролирует latency
  • 47.
    47 Обработчики исостояние Отдавайте предпочтение обработчикам без состояний, т.к. они позволяют параллельное выполнение стадии Reduce-стадии как правило имеют состояние Часто можно разбить reduce-стадии на несколько независимых, результат объединить на следующей стадии
  • 48.
  • 49.
    49 Буферизированные каналыПозволяют снизить lock contention на каналы Увеличивают throughput Увеличивают latency
  • 50.
    50 Планировщик «вглубину» Обычно используют планировщик «в ширину», т.е. очередь задач. Вместо очереди задач можно использовать стек задач. Получаем планировщик «в глубину» с другими свойствами.
  • 51.
    51 Планировщик «вглубину» Преимущества: Более быстрое получение первых результатов Снижение нагрузки на обработчики за счет более равномерного распределения данных между стадиями Уменьшение потребления памяти «Прогретые» процессорные кеши Отлично работает в связке с bounded каналами, заставляя данные более активно перемещаться к результату Недостатки: Отсутствие fairness Потеря контроля над latency
  • 52.
    52 Планы набудущее Распределенность Отказоустойчивость Оптимизация планировщика
  • 53.
  • 54.
    54 Предлагаемый подходпозволяет Реализовать простые сценарии (фильтрация, кеширование, парсинг, преобразование и т.д.) Реализовать сложные сценарии (петли, split, merge) Использовать асинхронное сетевое взаимодействие Забыть про синхронизацию Простая параллелизация: –Стадии выполняются параллельно, в отличие от MR –Несколько обработчиков одной стадии также выполняются параллельно
  • 55.
    55 Спасибо завнимание!
  • 56.
    56 http://habrahabr.ru/users/gridem https://github.com/gridem/Synca gridem@yandex-team.ru https://bitbucket.org/gridem/synca