Впервые дошли руки поиграться с файберами/корутинами. Кроме меня в игре два игрока: каждый из них ловит мяч (целое число), прибавляет к нему единицу и посылает другому игроку. Если число доросло до 100 000, посылает его главному треду и завершается. Главная ф-я создает двух игроков, знакомит друг с другом, запускает мяч и ждет пока оба не пришлют свои результаты. Показанный ниже код, в зависимости от (не)переданного аргумента командной строки, устраивает такую игру либо с игроками в отдельных параллельных тредах ОС, либо в разных корутинах в пределах одного треда.
На моем ноуте вариант с тредами ОС работает ~520 мс, а вариант с корутинами - 137 мс. При этом код фактически один и тот же, и, как я понимаю, реализация мэйлбоксов у них одна - т.е. в варианте с корутинами имеют место ненужные блокировки, можно было бы быстрее. Чуть разный код получился у ждущего треда - в одном случае идет блокирующее ожидание сообщения, в другом - неблокирующее + переключение. Но сам факт, что можно легко выбирать между разными скедулерами или задействовать свой, гибридный, доставляет. Было бы занятно сравнить времена и реализации с другими языками.
Upd: версия на Go отрабатывает за 35 мс.
Upd2: ее аналог на D (с самодельными типизированными каналами вместо универсальных мэйлбоксов и только корутинами) работает 24 мс.
import std.stdio, core.thread, std.concurrency, std.datetime; void player(string name) { Tid other = receiveOnly!Tid; while(true) { int v = receiveOnly!int; other.send(v+1); if (v >= 100000) { ownerTid.send(v); return; } yield(); } } void main(string[] argv) { bool useFibers = argv.length > 1; if (useFibers) scheduler = new FiberScheduler; else scheduler = new ThreadScheduler; StopWatch sw; sw.start(); scheduler.start({ auto p1 = spawn(&player, "Anne"); auto p2 = spawn(&player, "Bob"); p1.send(p2); // meet each other p2.send(p1); p1.send(1); // send the ball int n = 0; while(n<2) { //wait for them to finish if (useFibers) { receiveTimeout(0.msecs, (int v) { writeln(v); n++; }); yield(); } else { receiveOnly!int.writeln; n++; } } }); sw.stop(); writeln("time: ", sw.peek.msecs, " ms"); }
На моем ноуте вариант с тредами ОС работает ~520 мс, а вариант с корутинами - 137 мс. При этом код фактически один и тот же, и, как я понимаю, реализация мэйлбоксов у них одна - т.е. в варианте с корутинами имеют место ненужные блокировки, можно было бы быстрее. Чуть разный код получился у ждущего треда - в одном случае идет блокирующее ожидание сообщения, в другом - неблокирующее + переключение. Но сам факт, что можно легко выбирать между разными скедулерами или задействовать свой, гибридный, доставляет. Было бы занятно сравнить времена и реализации с другими языками.
Upd: версия на Go отрабатывает за 35 мс.
Upd2: ее аналог на D (с самодельными типизированными каналами вместо универсальных мэйлбоксов и только корутинами) работает 24 мс.
no subject
Date: 2015-11-20 12:44 pm (UTC)no subject
Date: 2015-11-21 04:59 pm (UTC)Но это же сильно зависит от задачи.
Например, треды - они что, шаред мемори или нет? Если шаред мемори, то включить спин - и будет как с корутинами. А если не шаред мемори, то у нас происходит сериализация, IPC и прочий мрак (и опять, если бы как-то спин включить, чтобы не слезать с ядра, пока ответ ждёшь).
no subject
Date: 2015-11-22 05:20 am (UTC)Это я просто что первое под руку попалось в стандартной библиотеке взял. Можно будет потом со спинлоками попробовать.
no subject
Date: 2015-11-22 09:02 am (UTC)