Re[19]: Зачем нам асинхронность?
От: alex_public  
Дата: 24.08.20 01:48
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

_>>Под ломать ты подразумеваешь unsafe код или что? )

MA>Нет, я имел ввиду использование чего-то вроде Box, Cell и т.п. вместо традиционных прямых решений.

А что такое традиционные решения? Rust позиционируется как новый системный язык, т.е. замена C/C++. В C++ используются в точности такие же конструкции, как и в Rust. Единственно отличие в том, что в C++ можно написать и небезопасный код (в стиле C), без блока unsafe, т.е. приходится контролировать отсутствие такого кода не с помощью компилятора, а административными мерами.

MA>Классические сильно-связные структуры на подобии деревьев реализуется ужасненько, фактически полностью нивелируя сами возможности языка, как и его преимущества перед другими. Я поэтому и сказал, что безопасность это фикция, т.к. помощи от его системы типов в таком раскладе не много, скорее мешает.


Система типов не даёт написать некорректный код. Да, при этом она иногда блокирует возможность написания корректного кода, корректность которого компилятору не очевидна. Для обычного прикладного кода (а не какой-нибудь низкоуровневой работы с железом) это встречается достаточно редко. И скажем для тех же деревьев я не вижу никаких принципиальных проблем.
Re[21]: Зачем нам асинхронность?
От: alex_public  
Дата: 24.08.20 01:54
Оценка:
Здравствуйте, Sinclair, Вы писали:

_>>Да, и кстати в случае задействования ещё одного потока сразу же встанет вопрос: а зачем мы страдали со всеми этими асинхронными приседаниями, если у нас всё равно ещё один поток используется для Parse? Не проще ли было сразу всё разместить в нём, причём с использованием банально синхронного ввода-вывода?

S>Это хорошо работает в том случае, когда у нас этот Parse и Download — один. А если мы попробуём дёрнуть десяток-другой таких задач, то у нас образуется десяток-другой потоков, которые отжирают адресное пространство и нагружают планировщик.
S>А с IOCP у нас потребуется O(1) потоков на ту же нагрузку — экономим адресное пространство и фактически выделяемую память для стеков.

Ха, случай "десятка-другого" потоков — это как раз наоборот пример идеальной производительности потокового решения, т.к. у нас количество задач будет приблизительно равно числу ядер процессора. Да и для сотни потоков планировщик без проблем будет справляться. А вот если счёт начнёт идти в тысячах, то это уже однозначный знак обязательного перехода к асинхронному вводу-выводу.
Re[22]: Зачем нам асинхронность?
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.08.20 02:58
Оценка:
Здравствуйте, Sharov, Вы писали:

S>В смысле отжирают? Если не ошибась,они уже отожрали в момент старта приложения, по факту своего нахождения в пуле.

Нет конечно. Посмотрите отладчиком на работающее приложение — там будет три-четыре потока. Никаких десятков потоков там и рядом нет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Зачем нам асинхронность?
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.08.20 03:02
Оценка: +4
Здравствуйте, alex_public, Вы писали:
_>Ха, случай "десятка-другого" потоков — это как раз наоборот пример идеальной производительности потокового решения, т.к. у нас количество задач будет приблизительно равно числу ядер процессора.
Хм, мы сейчас про десктоп или про сервер? На моём лаптопе 4 (четыре) ядра.

_>Да и для сотни потоков планировщик без проблем будет справляться. А вот если счёт начнёт идти в тысячах, то это уже однозначный знак обязательного перехода к асинхронному вводу-выводу.

Давайте посмотрим в таск менеджер. Вот у меня на лаптопе прямо сейчас работают 3992 потока.
Приложение же работает не в вакууме — а вы пишете так, как будто кроме ваших потоков, никаких других нет.
В реальности автор каждого говноприложения думает именно так — "нафига мне все эти задурения, полсотни потоков планировщик прожуёт не задумываясь", и в итоге мы имеем то, что имеем — пять-десять тысяч потоков, которые, по большому счёту не нужны.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[20]: Зачем нам асинхронность?
От: Mystic Artifact  
Дата: 24.08.20 05:35
Оценка:
Здравствуйте, alex_public, Вы писали:

_>А что такое традиционные решения?

Указатели, псевдо-указатели в пул/регион, смарт поинтеры и в тяжелых случаях GC ссылки. Последние 3 не бесплатные. Все 4 вполне широко применяются в C++.

_>Система типов не даёт написать некорректный код. Да, при этом она иногда блокирует возможность написания корректного кода, корректность которого компилятору не очевидна. Для обычного прикладного кода (а не какой-нибудь низкоуровневой работы с железом) это встречается достаточно редко. И скажем для тех же деревьев я не вижу никаких принципиальных проблем.

Так я и говорю, что на задачах приближенных к интересным ему постоянно не очевидна корректность.
Re[18]: Зачем нам асинхронность?
От: artelk  
Дата: 24.08.20 10:32
Оценка: 1 (1) +3
Здравствуйте, alex_public, Вы писали:

_>IOCP поток естественно не будет ничего ждать. А вот "обработка данных, пришедших по сети" будет произведена только после ожиданий (причём в такой модели двойных).


_>Понимаешь, на самом деле все эти ждущие потоки (что в синхронном варианте, что в асинхронном) — это абсолютно не страшная вещь. И я акцентировал внимание на их наличие в асинхронном коде только по той причине, что некоторые дурачки утверждали в своих статьях, что их нет и что как раз по этому асинхронный код намного лучше. Нет, они как раз есть. Но в этом ничего страшного с точки зрения эффективности нет. А вот что реально "страшно" с точки зрения эффективности работы программы, так это суммарная задержка от момента попадания пакета в сетевую карту, до момента его обработки в прикладном коде. Потому как рассуждать о количестве выдерживаемых в минуту запросов без уточнения величины латентности могут только очень сомнительные специалисты.


Good point, правильнее было бы написать "нет еще одного дополнительного ждущего потока специально под данную асинхронную операцию" (хотя иногда .Net может и запустить еще один IO поток, если запросов слишком много).
Самое интересное возникает в пиковых нагрузках, когда к моменту выполнения продолжения очередной завершенной IO операции в очереди IO потока уже есть следующая. В этом случае IO поток вообще не будет засыпать и лишних переключений контекста не будет, все потоки (потоки IO и потоки worker thread pool) будут полностью использовать выделенные им кванты времени.

По поводу двойных ожиданий. Вот тут пишут, что HttpClient умеет выполнять продолжения в IOCP потоке, без перекидывания их в worker thread.
Отредактировано 24.08.2020 10:52 artelk . Предыдущая версия .
Re[23]: Зачем нам асинхронность?
От: Sharov Россия  
Дата: 24.08.20 14:12
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>В смысле отжирают? Если не ошибась,они уже отожрали в момент старта приложения, по факту своего нахождения в пуле.

S>Нет конечно. Посмотрите отладчиком на работающее приложение — там будет три-четыре потока. Никаких десятков потоков там и рядом нет.

Да, это я маху дал. Но опять же, мы можем контролировать (ограничить сверху) выделение памяти для потоков через ThreadPool.SetMaxThreads.
Кодом людям нужно помогать!
Re[24]: Зачем нам асинхронность?
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.08.20 15:03
Оценка: +2
Здравствуйте, Sharov, Вы писали:
S>Да, это я маху дал. Но опять же, мы можем контролировать (ограничить сверху) выделение памяти для потоков через ThreadPool.SetMaxThreads.
Погодите. Вы сейчас какой механизм имеете в виду? Мы же тут обсуждаем IOCP против рукопашных потоков.
Если мы запускаем потоки руками, то никакого ограничения через пул нету.
А если мы пользуемся потоками из пула и завинчиваем MaxThreads, то есть риск, что заказанная операция "Read; Parse" даже не начнётся, пока кто-то из спящих потоков не дождётся своего события и не отработает до конца.
В итоге мы имеем совсем неудачный компромисс: у нас спят N потоков, отъедая N*1MB стека каждый; и ещё M заданий висят в очереди, даже не начав I/O операцию.
То есть и волки голодны, и овцы тогось.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Зачем нам асинхронность?
От: Sharowarsheg  
Дата: 24.08.20 15:10
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Кста по хэшмапам. Вместо одного хэшмапа делаешь например массив хэшмапов

S>и доступ через GetHashCode() % 16. У тебя конкуренция в среднем должна уменьшиться в 16 раз, а значит и скорость

Нужно не забывать, что вместо гигабайта хешмепов у тебя получается шестнадцать гигабайтов хешмепов.
Re[25]: Зачем нам асинхронность?
От: Sharov Россия  
Дата: 24.08.20 15:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Sharov, Вы писали:

S>>Да, это я маху дал. Но опять же, мы можем контролировать (ограничить сверху) выделение памяти для потоков черезSetMaxThreads.
S>Погодите. Вы сейчас какой механизм имеете в виду? Мы же тут обсуждаем IOCP против рукопашных потоков.
S>Если мы запускаем потоки руками, то никакого ограничения через пул нету.

Меня переклинило, что пул создает обычные (сколько-то их там у него) потоки сразу при старте, отжирая сразу всю необходимую память.
Очевидно, что это не так, ибо тогда настройка SetMaxThreads не имела бы смысла. Пул, конечно же, создает потоки по мере необходимости,
и кол-во созданных потоков мы и можем ограничить. Разумеется, в ущерб латентности, если запросов сильно больше потоков.

S>А если мы пользуемся потоками из пула и завинчиваем MaxThreads, то есть риск, что заказанная операция "Read; Parse" даже не начнётся, пока кто-то из спящих потоков не дождётся своего события и не отработает до конца.

S>В итоге мы имеем совсем неудачный компромисс: у нас спят N потоков, отъедая N*1MB стека каждый; и ещё M заданий висят в очереди, даже не начав I/O операцию.
S>То есть и волки голодны, и овцы тогось.

Если речь об обычных потоках, а не iocp, тогда да, все, кажется, так.
Кодом людям нужно помогать!
Re[16]: Зачем нам асинхронность?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.08.20 15:52
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>> Кста по хэшмапам. Вместо одного хэшмапа делаешь например массив хэшмапов

S>>и доступ через GetHashCode() % 16. У тебя конкуренция в среднем должна уменьшиться в 16 раз, а значит и скорость

S>Нужно не забывать, что вместо гигабайта хешмепов у тебя получается шестнадцать гигабайтов хешмепов.

Почему? Данные распределятся по 16 хэшмапам.
Внутри то хэшмапа GetHashCode() % SizeHashMap на связный список коллизий
и солнце б утром не вставало, когда бы не было меня
Re[17]: Зачем нам асинхронность?
От: Sharowarsheg  
Дата: 24.08.20 17:41
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>>>и доступ через GetHashCode() % 16. У тебя конкуренция в среднем должна уменьшиться в 16 раз, а значит и скорость


S>>Нужно не забывать, что вместо гигабайта хешмепов у тебя получается шестнадцать гигабайтов хешмепов.

S> Почему? Данные распределятся по 16 хэшмапам.
S>Внутри то хэшмапа GetHashCode() % SizeHashMap на связный список коллизий

Не совсем. Часть элементов таблицы обычно пустая, если ты хочешь O(1), чтобы списки коллизий не слишком росли. Поэтому на практике, если таблицы достаточно большие, оказывается, что каждая из них не уменьшается в 16 раз, а становится несколько более разреженной. Хотя, может быть, это свойство конкретного дотнетовского Dictionary. Там это заметно даже если не забыть сдвинуть хеши на 4 бита вправо.
Re[18]: Зачем нам асинхронность?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.08.20 17:51
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>>>и доступ через GetHashCode() % 16. У тебя конкуренция в среднем должна уменьшиться в 16 раз, а значит и скорость


S>>>Нужно не забывать, что вместо гигабайта хешмепов у тебя получается шестнадцать гигабайтов хешмепов.

S>> Почему? Данные распределятся по 16 хэшмапам.
S>>Внутри то хэшмапа GetHashCode() % SizeHashMap на связный список коллизий

S>Не совсем. Часть элементов таблицы обычно пустая, если ты хочешь O(1), чтобы списки коллизий не слишком росли. Поэтому на практике, если таблицы достаточно большие, оказывается, что каждая из них не уменьшается в 16 раз, а становится несколько более разреженной. Хотя, может быть, это свойство конкретного дотнетовского Dictionary. Там это заметно даже если не забыть сдвинуть хеши на 4 бита вправо.


Ну общее количество записей будет таким же и среднее наполнение тоже. Обычно это около 75%. При полном заполнении ищется простое число в 2 раза большее чем текущий размер.
Конечно если ты им не задашь начальный размер в миллионы записей. А так будет то же, что и при одной хэш таблице.

В дотнете словарь состоит их 2 таблиц. Вторая представляет собой связный список на массиве, а первая содержит индекс начала связного списка.

Вообзе похож на мой http://rsdn.org/forum/alg/437992.1
Автор: Serginio1
Дата: 10.11.03

но сделан на однй таблице
и солнце б утром не вставало, когда бы не было меня
Отредактировано 24.08.2020 17:57 Serginio1 . Предыдущая версия .
Re[19]: Зачем нам асинхронность?
От: Sharowarsheg  
Дата: 24.08.20 17:54
Оценка:
Здравствуйте, Serginio1, Вы писали:


S> Ну общее количество записей будет таким же и среднее наполнение тоже. Обычно это около 75%. При полном заполнении ищется простое число в 2 раза большее чем текущий размер.

S>Конечно если ты им не задашь начальный размер в миллионы записей. А так будет то же, что и при одной хэш таблице.

Хм, у меня обычно фактический размер начинается с десятков миллионов записей. Не то, чтобы я как-то старался задавать начальный размер особенно. Пришлось даже мутить специально коллекцию из многих Dictionary, потому что у библиотечного ограничение типа 25 или 30 миллионов всего. Если меньше, то непонятно, зачем вообще хештаблицы и многопоточка.
Отредактировано 24.08.2020 17:54 Sharowarsheg . Предыдущая версия .
Re[20]: Зачем нам асинхронность?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.08.20 18:02
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:



S>> Ну общее количество записей будет таким же и среднее наполнение тоже. Обычно это около 75%. При полном заполнении ищется простое число в 2 раза большее чем текущий размер.

S>>Конечно если ты им не задашь начальный размер в миллионы записей. А так будет то же, что и при одной хэш таблице.

S>Хм, у меня обычно фактический размер начинается с десятков миллионов записей. Не то, чтобы я как-то старался задавать начальный размер особенно. Пришлось даже мутить специально коллекцию из многих Dictionary, потому что у библиотечного ограничение типа 25 или 30 миллионов всего. Если меньше, то непонятно, зачем вообще хештаблицы и многопоточка.


На саамом деле ты много не выигрываешь от первичного задания размера, можешь и проиграть. Ну в данном случае если размер известен раздели его на 16.
Ну а в дотнете ограничение в int.MaxValue
и солнце б утром не вставало, когда бы не было меня
Re[21]: Зачем нам асинхронность?
От: Sharowarsheg  
Дата: 24.08.20 18:17
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>На саамом деле ты много не выигрываешь от первичного задания размера, можешь и проиграть. Ну в данном случае если размер известен раздели его на 16.

S>Ну а в дотнете ограничение в int.MaxValue

Нет, по крайней мере в ранних версиях (2.0) ограничение что-то между 25 и 30 миллионов.
Re[22]: Зачем нам асинхронность?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.08.20 20:54
Оценка:
Здравствуйте, Sharowarsheg, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>На саамом деле ты много не выигрываешь от первичного задания размера, можешь и проиграть. Ну в данном случае если размер известен раздели его на 16.

S>>Ну а в дотнете ограничение в int.MaxValue

S>Нет, по крайней мере в ранних версиях (2.0) ограничение что-то между 25 и 30 миллионов.


https://stackoverflow.com/questions/3460729/is-there-a-limit-to-entries-in-a-dictionary

Может проблема была в 32 разрядной оси? Да и 3.5 была проблема с дефрагментацией LOH
и солнце б утром не вставало, когда бы не было меня
Re[23]: Зачем нам асинхронность?
От: Sharowarsheg  
Дата: 24.08.20 21:53
Оценка: 2 (1)
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, Sharowarsheg, Вы писали:


S>>Здравствуйте, Serginio1, Вы писали:


S>>>На саамом деле ты много не выигрываешь от первичного задания размера, можешь и проиграть. Ну в данном случае если размер известен раздели его на 16.

S>>>Ну а в дотнете ограничение в int.MaxValue

S>>Нет, по крайней мере в ранних версиях (2.0) ограничение что-то между 25 и 30 миллионов.


S>https://stackoverflow.com/questions/3460729/is-there-a-limit-to-entries-in-a-dictionary


S> Может проблема была в 32 разрядной оси? Да и 3.5 была проблема с дефрагментацией LOH


Там что-то про то, что размер массива должен быть простым числом, а запас простых чисел кончается.
На 64 разрядах предел побольше, но тоже ничего и близко к int.MaxValue нет.

using System;
using System.Collections.Generic;


namespace DictionaryTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<int, int> X = new Dictionary<int, int>();
            int i = 0;
            while (true)
            {
                if ((i % 1_000) == 0) Console.WriteLine("i={0}", i);
                X.Add(i, i);
                i++;
            }
        }
    }
}


.NET 4.5 x64 i = 95_991_000 (array dimensions exceed supported range)
.NET 4.5 x86 i = 23_997_000 (out of memory)

хотя, кстати, если поменять <int, int> на <long, long>, то получается

.NET 4.5 x64 i = 47_995_000 (array dimensions exceed supported range)
.NET 4.5 x86 i = 23_997_000 (out of memory)

что довольно удивительно.
Отредактировано 24.08.2020 22:38 Sharowarsheg . Предыдущая версия .
Re[24]: Зачем нам асинхронность?
От: rameel https://github.com/rsdn/CodeJam
Дата: 25.08.20 09:21
Оценка: 5 (3)
Здравствуйте, Sharowarsheg, Вы писали:

S>.NET 4.5 x64 i = 47_995_000 (array dimensions exceed supported range)

S>.NET 4.5 x86 i = 23_997_000 (out of memory)

.NET Core 5 Preview 7 x64
i = 767_933_000
Out of memory.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[25]: Зачем нам асинхронность?
От: rameel https://github.com/rsdn/CodeJam
Дата: 25.08.20 09:37
Оценка: 5 (3)
Здравствуйте, rameel, Вы писали:

R>.NET Core 5 Preview 7 x64

R>i = 767_933_000
R>Out of memory.

Если задать Capacity сразу на максимум — грубо на 1_900_000_000 то отрабатывает, так как в процессе не приходится перестраивать таблицу
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.