Здравствуйте, Erop, Вы писали:
E>Здравствуйте, alex_public, Вы писали:
_>>Вообще то в роли числодробилки C++ однозначно лучший язык. Если сравнивать с Фортраном, то он предлагает намного более высокоуровневые абстракции и при этом даже чуть быстрее в умелых руках. Однако как мы видим во многих местах до сих пор используют Фортран — тут есть и тот самый консерватизм (как при переходе с C++ на D в других местах) и небольшая доля прагматизма, если мы говорим об учёных, а не программистах (C++ очень сложный, по сравнению с Фортраном).
E>Это главная проблема С++, как числодробилки. Что бы решить сложную вычматематическую задачу нужен не спец в С++, а спец в вычматах. А таким спецм фортран ПОНЯТНЕЕ и УДОБНЕЕ. E>В числодробилках хитрые абстракции нужны редко, а вот боевая итерация по каким-то матрицам + возможность компилятора это всё векторизовать на любой платформе более или менее самостоятельно нужна практически всегда.
Ну ты сильно преувеличиваешь возможности встроенной автоматической оптимизации всего и вся компилятором.
Те же cache locality, false sharing и прочие милые эффекты никто не отменял, и человек, который знает только абстрактные вычматы в вакууме (в которых скорость доступа к любому элементу любой матрицы одинаково мгновенная и считается только количество сложений/умножений) и полагающийся исключительно на возможности компилятора, максимально быстрый код не выдаст.
Здравствуйте, Ikemefula, Вы писали:
G>>>Да и в общем случае сделать из синхронного черного ящика асинхронный нельзя, потому что нет гарантии не нарушения инвариантов. EP>>Придумай хотя бы пару примеров поломки инвариантов в функции которая принимает пользовательский stream, и гарантированно работает в многопоточной среде. I>А почему инварианты должны ломаться именно в этой функции ?
Тогда покажи пример где они ломаются не в этой функции.
I>Ты хочешь решить локальную проблему, глобальным механизмом. Соответсвенно инварианты будут ломаться так же глобально. I>Ты не можешь заранее сказать, что будет выполняться в тот момент, когда одна из нитей вызывает getline. Что будет если вторая нить вызовет тот же getline, скажем по какому нибудь эвенту и тд ?
Ты не болтай, а примеры приведи
Вот есть thread safe функция, пусть тот же самый getline. Когда есть несколько потоков — её могут прервать в любой момент.
Что может может поломаться если ей передать stream который делает yield внутри.
Другими словами, чем std::this_thread::yield безопасней this_coroutine::yield?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, artelk, Вы писали:
EP>Тут проблема будет даже и с обычными потоками — download_and_update_statistics не thread safe
Не thread safe, и что? Автор download_and_update_statistics и не задумывал его потокобезопасным. Метод работал корректно в однопоточном варианте. А после monkey.patch_all() перестал.
A>>Фишка в том, что автор этого "произвольного кода" ничего не знает о том, что в вызывающей стороне делаются хаки и игры со стеком, и в следующей версии функций может поломать инварианты. Не, лучше явный async\await... EP>Давай примеры: EP>
EP>Придумай хотя бы пару примеров поломки инвариантов в функции которая принимает пользовательский stream, и гарантированно работает в многопоточной среде.
Здравствуйте, artelk, Вы писали:
EP>>Тут проблема будет даже и с обычными потоками — download_and_update_statistics не thread safe A> Не thread safe, и что? Автор download_and_update_statistics и не задумывал его потокобезопасным. Метод работал корректно в однопоточном варианте. А после monkey.patch_all() перестал.
[кэп]А то что ты такую функцию не сможешь использовать и в банальном multi-thread[/кэп].
Те функции которые работают сокетами и т.п. — обычно thread-safe (например хоть urllib2.urlopen(url).read() из примера выше), если же нет — то об этом сказано в документации/интерфейсе
A>>>Фишка в том, что автор этого "произвольного кода" ничего не знает о том, что в вызывающей стороне делаются хаки и игры со стеком, и в следующей версии функций может поломать инварианты. Не, лучше явный async\await... EP>>Давай примеры: EP>>
EP>>Придумай хотя бы пару примеров поломки инвариантов в функции которая принимает пользовательский stream, и гарантированно работает в многопоточной среде.
A>Нафига?
Все примеры выше где ломаются инварианты имели не thread-safe код — обсуждать это не интересно.
Здравствуйте, gandjustas, Вы писали:
G>Для клиента я пишу на JS, там однопоточная асинхронность. На сервере — async\await самое то. G>Это как раз к тому, что потоки, особенно созданные вручную, почти не нужны, если у тебя есть нормальный асинронный IO.
У тебя сервер утилизирует только одно ядро процессора? )
G>Кстати в .NET тоже вполне возможно:
Ооо, наконец то код. Ну что же, я его весь посмотрел и вот мои выводы:
1. Он принимается. В том смысле что он действительно делает тоже самое что и мой. Правда переписать в 3 раза короче у тебя не вышло, а получилось скорее наоборот... Ну да ладно, я не буду цепляться к мелочам.
2. Как следствие пункта 1 делаем вывод, что с тобой всё же возможно конструктивное общение, хотя и долго пришлось добиваться этого.
3. Твоё решение задачки является чётким доказательством моего тезиса, что реализация await/async в C# заметно слабее простейшей реализации того же на C++ с помощью Boost.Coroutine. Т.к. тебе пришлось использовать системные сопроцедуры, предоставляемые виндой (которые являются полным аналогом Boost.Coroutine, только платформозависимым и независящим от языка), а не использовать механизм await/async (который тоже представляет собой сопроцедуры, но гораздо более слабые).
G>Поздравляю, ты изобрел асинхронные корутины. Их для .NET Рихтер изобрел в 2007 году G>Но у тебя как обычно несколько проблемы: G>1) нужен message pump для корутин, обязательно однопоточный. G>2) Нужен message pump для асинхронного IO, тут уже зависит от реализации. G>3) Все это дико тормозит без возможности улучшения (потому что 1 и 2).
А где ты увидел в этом моём коде использование очереди сообщений? )
И кстати как раз .net вариант использует очередь сообщений, в то время как это совсем не обязательно для неблокирующего IO.
_>>Потому что этот синтаксический сахар не является бесплатным по накладным ресурсам. G>
Ну посмотри на реализацию реально нагруженных серверов. Тот же nginx глянь. А потом покажи мне нечто сравнимое на C# await/async.
J>Те же cache locality, false sharing и прочие милые эффекты никто не отменял, и человек, который знает только абстрактные вычматы в вакууме (в которых скорость доступа к любому элементу любой матрицы одинаково мгновенная и считается только количество сложений/умножений) и полагающийся исключительно на возможности компилятора, максимально быстрый код не выдаст.
Сейчас, конечно, полегче стало, так как х86 победил всех. Я ещё помню времена, когда типовой сценарий был такой, что отлаживал ты код на персоналке, а боевой прогон делал на суперкомпе, время которого — дефицит, как бы.
Сейчас, конечно, отлаживаешь на персоналке, а прогон на кластере, но тоже компы в кластере и на столе могут сильно отличаться таки...
и тут фортран именно потому и лучше, что НУЖНЫЕ ДЛЯ ВЫЧМАТОВ оптимизации там вылизанны до блеска, а не так, как в си...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, jazzer, Вы писали:
J>который знает только абстрактные вычматы в вакууме (в которых скорость доступа к любому элементу любой матрицы одинаково мгновенная и считается только количество сложений/умножений) и полагающийся исключительно на возможности компилятора, максимально быстрый код не выдаст.
Уточню свой прошлый пост. Тут, безусловно, ты прав в том смысле, что твой алгоритм должен пролазить во все целевые архитектуры. Ну там блочные операции должны быть такими, что бы блоки помещались в память/кэш, если возможно, то использовалось зацепление и векторизация, вектора помещались в векторные регистры, если они на целевой платформе бывают и т. д.
А не прав в том, что вычматематики этого всего не знают. Абстрактные вычматы читают в ВУЗах на непрофильных специализациях, а спецы всё давно занют, понимают и использывают на боевых прогонах. Думаешь какой-нибудь крэй было так уж просто загрузить хотя бы процентов на 70% пиковой производительности?
И тем, не менее, все всё делали. Только это и так сложные довольно задачи, и тратить ещё силы и внимание на всякую ерунду, вроде той или иной векторизации там, или той или иной стратегии предсказания ветвлений, аппаратную поддержку циклов и прочие штуки не хочется, и во многих случаях фортран тут безусловно помогает.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Для клиента я пишу на JS, там однопоточная асинхронность. На сервере — async\await самое то. G>>Это как раз к тому, что потоки, особенно созданные вручную, почти не нужны, если у тебя есть нормальный асинронный IO.
_>У тебя сервер утилизирует только одно ядро процессора? )
Нет, там пул потоков оптимизированный под работу сервера. Создание дополнительных потоков на каждый чих ухудшает работу.
G>>Кстати в .NET тоже вполне возможно:
_>Ооо, наконец то код. Ну что же, я его весь посмотрел и вот мои выводы:
_>1. Он принимается. В том смысле что он действительно делает тоже самое что и мой. Правда переписать в 3 раза короче у тебя не вышло, а получилось скорее наоборот... Ну да ладно, я не буду цепляться к мелочам.
С учетом реализации boost coroutine мой код порядка на два короче
У меня кода, относящегося к задаче, меньше чем у тебя примерно в половину. Нет макросов и дополнительных функций для маршалинга в UI.
_>2. Как следствие пункта 1 делаем вывод, что с тобой всё же возможно конструктивное общение, хотя и долго пришлось добиваться этого.
_>3. Твоё решение задачки является чётким доказательством моего тезиса, что реализация await/async в C# заметно слабее простейшей реализации того же на C++ с помощью Boost.Coroutine. Т.к. тебе пришлось использовать системные сопроцедуры, предоставляемые виндой (которые являются полным аналогом Boost.Coroutine, только платформозависимым и независящим от языка), а не использовать механизм await/async (который тоже представляет собой сопроцедуры, но гораздо более слабые).
Прости, но ты походу не понимаешь о чем пишешь. В том коде что я показал нет async\await, там можно было и без тасков сделать, вышло бы на 3 строчки длиннее.
Вот тебе решение той же задачи с async\await:
private async void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
using (var src = File.OpenRead("TextFile1.txt"))
using (var dst = File.Create("TextFile2.txt"))
await ComplexFuncAsync(src, dst);
Console.WriteLine("Iteration " + i);
}
}
static async Task ComplexFuncAsync(Stream src, Stream dst)
{
var buf = new byte[256];
while(src.Position < src.Length)
{
var len = await src.ReadAsync(buf,0,buf.Length);
await dst.WriteAsync(buf, 0, len);
}
}
Это весь код, больше ни одной строчки не надо. И так пишут нормальные люди на C#.
А ты пытаешься доказать что на C# нельзя написать как на C++. Так оно и не нужно, есть более эффективные способы. Более того, на C++ гораздо хуже работает твой вариант, чем код выше.
G>>Поздравляю, ты изобрел асинхронные корутины. Их для .NET Рихтер изобрел в 2007 году G>>Но у тебя как обычно несколько проблемы: G>>1) нужен message pump для корутин, обязательно однопоточный. G>>2) Нужен message pump для асинхронного IO, тут уже зависит от реализации. G>>3) Все это дико тормозит без возможности улучшения (потому что 1 и 2).
_>А где ты увидел в этом моём коде использование очереди сообщений? )
Оно там есть. Как у тебя продолжения маршалятся в UI поток? Ты же из IOCP треда не дергаешь корутину?
_>И кстати как раз .net вариант использует очередь сообщений, в то время как это совсем не обязательно для неблокирующего IO.
Ты о чем? IOCP и есть очередь сообщений, ты про какую очередь?
Или ты про то, что по дефолту таски выполняют продолжение в текущем контексте синхронизации? Так это сделано для комфорта разработчика и решается вызовом ConfigureAwait(false).
_>>>Потому что этот синтаксический сахар не является бесплатным по накладным ресурсам. G>>
_>Ну посмотри на реализацию реально нагруженных серверов. Тот же nginx глянь. А потом покажи мне нечто сравнимое на C# await/async.
Ты реально гонишь...
В C++ все сильно завязано на стек, поэтому там нельзя автоматом переписывать хвост метода в продолжение, как это делает компилятор C#. Когда изобретут capture-by-move для лямбд, тогда это станет возможно, покачто нет.
А компилятор C еще более примитивный, он слишком низкоуровневый, чтобы таким заниматься.
Поэтому в программах на C никогда не будет аналога async\await, а на C++ ближайшее время не будет, может быть жалкое подобие.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>А почему инварианты должны ломаться именно в этой функции ?
EP>Тогда покажи пример где они ломаются не в этой функции.
А ты не читатель ?
I>>Ты хочешь решить локальную проблему, глобальным механизмом. Соответсвенно инварианты будут ломаться так же глобально. I>>Ты не можешь заранее сказать, что будет выполняться в тот момент, когда одна из нитей вызывает getline. Что будет если вторая нить вызовет тот же getline, скажем по какому нибудь эвенту и тд ?
EP>Ты не болтай, а примеры приведи
Я уже показал. В обычном приложении я имею право вызывать getline где мне вздумается, хоть в обрабочике события какого. А вот с твоей модной асинхронщиной это уже не пройдет.
EP>Вот есть thread safe функция, пусть тот же самый getline. Когда есть несколько потоков — её могут прервать в любой момент. EP>Что может может поломаться если ей передать stream который делает yield внутри.
Ты проверь на деле, у тебя ведь есть нужный код ? Добавь всего ничего — там где пополнение буфера эвент. А в хандлере вызови еще один getline.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>>>Вот такие фокусы и в джаве, и в дотнете, и в питоне. EP>>>Откуда именно тут coroutine, покажи полный код на C#. I>>Из либы. Зачем тебе код ?
EP>Полный код примера.
Что ты хочешь сделать с этим кодом ? Там, естественно, нет полноценных stackfull, их должен gc поддерживать.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Ещё раз. Я озвучил факт, который оппонент не знал/не понимал. I>>Ты сам себе придумал, что ктото чего то не понимает.
EP>Например gandjustas начинает петь ту же самую песню про message pump, которую ты пел летом
I>>Ну значит UI Loop из моего примера вызовется чудом, например сканированием адресного пространства на предмет наличия ::GetMessage и ::TranslateMessage или, как вариант, короутина догадается WinMain вызвать.
Правильно. Ты ведь говорил, что твоим короутинам не надо ничего знать про эвентлуп ? А оказалось не только надо, но и больше — надо всё приложение делать так, что бы поддерживать эту асинхронщину.
EP>>>Вот ты тоже не знал, не верил, отрицал. Зато теперь "И что с того ?" — большой прогресс I>>Если тебя подводит память, то я напомню — ты утверждал, что асинхронщину не надо всовывать в эвентлуп. И доказал это утверждение кодом в котором всунул асинхронщину в WM_CHAR.
EP>Опять двадцать пять
EP>// было:
мусор выбросил
case WM_SIZE:
switch(w_param)
{
case SIZE_MAXIMIZED:
task();
break;
case SIZE_MINIMIZED:
if(on_minization) on_minization();
break;
}
EP>}
EP>
Трансформация полностью локальная
И в итоге вся твоя асинхронщина прибита гвоздями к эвентлупу. Глаза раскрой — эвентлуп используется у тебя как примитивный диспетчер.
В другом твоем примере ты сделал точно так же — привязался к WM_CHAR.
То есть, в кратце, короутин как минимум недостаточно для асинхронщины, нужен диспетчер прибитый гвоздями к эвентлупу.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>[кэп]А то что ты такую функцию не сможешь использовать и в банальном multi-thread[/кэп]. EP>Те функции которые работают сокетами и т.п. — обычно thread-safe (например хоть urllib2.urlopen(url).read() из примера выше), если же нет — то об этом сказано в документации/интерфейсе
При чем здесь тред-сейф ? В асинхронном коде у тебя внутри ОДНОПОТОЧНОГО приложения будет сотня ниток. Вот эти нитки и будут ломать инварианты в разделяемых ресурсах.
Итого — вроде тредсейф, а инварианты ломаются.
A>>>>Фишка в том, что автор этого "произвольного кода" ничего не знает о том, что в вызывающей стороне делаются хаки и игры со стеком, и в следующей версии функций может поломать инварианты. Не, лучше явный async\await... EP>>>Давай примеры: EP>>>
EP>>>Придумай хотя бы пару примеров поломки инвариантов в функции которая принимает пользовательский stream, и гарантированно работает в многопоточной среде.
A>>Нафига?
EP>Все примеры выше где ломаются инварианты имели не thread-safe код — обсуждать это не интересно.
Вот простой фокус
// Вот код, с которым все шоколадно в синхронном однопоточном коде
value = read()
write(next(value))
// а вот однопоточныый вариант, но асинхронный
нить 1 value = read()
нить 2 value = read()
нить 2 write(next(value))
нить 1 write(next(value)) // нарушение инварианта
// и вот
нить 1 value = read()
нить 2 value = read()
нить 1 write(next(value))
нить 2 write(next(value)) // нарушение инварианта
Теперь делаем тредсейф
lock(_syncroot){
value = read()
write(next(value))
}
Здравствуйте, Ikemefula, Вы писали:
I>То есть, в кратце, короутин как минимум недостаточно для асинхронщины, нужен диспетчер прибитый гвоздями к эвентлупу.
Хватит уже передёргивать, у нас уже была асинхронная функция:
Здравствуйте, Ikemefula, Вы писали:
I>И это правильно ! Ты не думал, почему аналогичные структуры хотят ввести в стандарт С++ ?
Это ты про что? ) Я краем глаза посматриваю на C++14, но что-то не помню там такого. )
I>Наоборот — именно этот механизм и нужен для асинхронщины. Просто по той простой причине, что асинхронная многозадачность на порядок сложнее обычной скажем многопоточной многозадачности. В ней нет никакого ограничивающего контекста. Соответственно любая локальная задача превращается в глобальную проблему.
I>Потому и нужен механизм, который позволит решать такие проблемы локально, а не неявно модифицировать поведение непойми каких функций.
Запуск нового потока с использованием блокирующих функций в нём — это тоже со стороны получается асинхронным вызовом. Причём он прост и удобен для программирования. К сожалению он не подходит для 100% задач, но для очень многих всё же подходит...
I>Cам подумай: I>
I>// Вот код, с которым все шоколадно в синхронном однопоточном коде
I>value = read()
I>write(next(value))
I>// а вот однопоточныый вариант, но асинхронный
I>нить 1 value = read()
I>нить 2 value = read()
I>нить 2 write(next(value))
I>нить 1 write(next(value)) // нарушение инварианта
I>// и вот
I>нить 1 value = read()
I>нить 2 value = read()
I>нить 1 write(next(value))
I>нить 2 write(next(value)) // нарушение инварианта
I>
Не знаю откуда ты взял это, но оно явно не имеет никакого отношения к моей реализации. Т.к. в ней наша функция теряет управление сразу же после инициации асинхронного вызова read. И соответственно пока эта операция не будет завершена (плюс ещё пока поток занятый чем-то другим не освободится), никаких новых вызовов read или write не будет. Причём этот расклад никак не связан с внутренним устройством нашей функции.
_>Не знаю откуда ты взял это, но оно явно не имеет никакого отношения к моей реализации.
Я показываю, что твое решение не имеет никакого отношения к задаче.
> Т.к. в ней наша функция теряет управление сразу же после инициации асинхронного вызова read. И соответственно пока эта операция не будет завершена (плюс ещё пока поток занятый чем-то другим не освободится), никаких новых вызовов read или write не будет. Причём этот расклад никак не связан с внутренним устройством нашей функции.
А кто гарантирует, что новых вызовов не будет ? Представь, в однопоточном коде сто штук таких вот вызово. Когда синхронно, все шоколадно — отработали последовательно, каждая следующая модифицирует результат предыдущей.
Когда появляется асинхронщина, каждая следующая перезатирает результат предыдущей.
Здравствуйте, Ikemefula, Вы писали:
EP>>Наконец-то долгожданный пример I>Я этот вариант упоминал раз сто когда ты летом сказки рассказывал про короутины.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, artelk, Вы писали:
EP>>>Тут проблема будет даже и с обычными потоками — download_and_update_statistics не thread safe A>> Не thread safe, и что? Автор download_and_update_statistics и не задумывал его потокобезопасным. Метод работал корректно в однопоточном варианте. А после monkey.patch_all() перестал.
EP>[кэп]А то что ты такую функцию не сможешь использовать и в банальном multi-thread[/кэп]. EP>Те функции которые работают сокетами и т.п. — обычно thread-safe (например хоть urllib2.urlopen(url).read() из примера выше), если же нет — то об этом сказано в документации/интерфейсе
Обычно != всегда, да и "обычность" неочевидна.
И, наверно, напрасно в gevent свои примитивы синхронизации добавлены, ведь существующие системные напрочь не игнорируются. Если метод thread safe, то он тоочно будеть работать правильно, когда мы его пересадим на наши короутины.
EP>Все примеры выше где ломаются инварианты имели не thread-safe код — обсуждать это не интересно.
Ну заверни ты тело download_and_update_statistics в mutex Enter\Exit.
Чуть более жизненный пример (раз уж просил):
lock(sync)
{
if(list != null)
return list;
list = new List();
using(var stream = createStream())
{
string s;
while ((s=stream.ReadLine())!=null)
list.Add(s);
}
}