- 1
- 2
- 3
- 4
- 5
foreach(string s in File.ReadAllText(input_text).Replace('ё', 'е').ToLower().Split(new char[] { ',', ' ', '.', '\t', '\r', '\n', '-', '?', '!', '\\', '/', ':', ';', '<', '>', '\'', '\"', '(', ')' }, StringSplitOptions.RemoveEmptyEntries).
AsParallel().Where<string>(s => (s.Length == 4)).GroupBy(x => x).Select(g => new { Value = g.Key, Count = g.Count() }). OrderByDescending(x => x.Count).Select(f => (f.Value + ' ' + f.Count.ToString())).ToArray())
{
Console.WriteLine(s);
}
Но сарказм автора оценил:
существуют соглашения по оформлению кода, в которых подобное является обзательным, потому говорить "не нужны" в корне не правильно.
Имеешь в виду ParallelQuery.ForAll()? Аналогично ;). Но тут есть способы выхода из положения.
Самое простое, наверное, использовать Array.ForEach или List.ForEach. Но это напрочь убивает ленивость.
Выведется что-то вроде:
мама 10
папа 5
вася 1
т.к. в виде запроса можно записать только where, group by и select
а остальное - всё равно в виде методов
так что лучше оставить как есть, просто добавить добавить перенос строки перед каждым новым чейном (точкой)
Как уже замечено выше, получается почти нечитаемый уродец.
Люди незамутненные сиквелом.
Или она возвращает новый хитрожопый контейнер, у которого все методы сделаны как параллельнные?
Хорошо бы ещё Replace и ToLower перенести в параллельную часть.
Дейскать "напейшу AsParallel() и оно само в 2-4 раза ускорится"
Я поясню мысль. При нормальный трейдинге один тред потихоньку читает содержимое файла. Второй же парсит и обновляет статистику.
То есть время выполнения T будет следующим
T=t_считывания+t'
Где t' - время, которое нужно чтобы закончить работу второму треду. Оно ничтожно.
Т.к. второй тред работает быстрее жесткого диска и большую часть времени он будет простаивать в ожидании новой порции данных с HDD.
LINQ будет работать иначе:
Сначала он прочитает файл ReadAllText(input_text) в память.
Далее создаст список на 100000 элементов
Потом сделает распаралеленый проход for i=1 to 100000 Replace('ё', 'е')
Потом еще один распаралеленый цикл for i=1 to 100000 ToLower()
итд.
Возможно я неправ и LINQ будет обрабатывать конвейерно, не создавая промежуточных списков, но в конце-концов все-равно имеем:
T=t_считывания+t_обработки_всех_100000_э лементов
Ну о побочном эффекте - загрузке всего файла в память я умолчу.
Но проблема даже не в том, что паралелится не то что нужно.
и только это
всё остальное происходит по-очереди
Отлично!
Тут мы имеем вот что:
(С вашего позволения я перенесу стадии Replace('ё', 'е') и ToLower() после стадий Split и Where. Для простоты и оптимальности)
- Чтение из памяти слова
- Выполнение на нём простейших операций - проверка Where, Replace('ё', 'е'), ToLower()
- Нахождение в списке статистики (память, хотя очень вероятно кеш) этого слова
- Опциональное создание новой статистической записи
- Инкремент статистики (количество таких слов)
ЦПУ очень быстро осуществит Where, Replace('ё', 'е'), ToLower(), и оставшееся время будет ждать пока оно запишется в память/кеш.
и новой порции данных из памяти. Ибо латентность памяти очень высока.
Зачем это распаралеливать? Чтобы два ядра простаивали в ожидании данных?
Мало того затраты на распаралеливание в данном случае могут запросто получится дороже, чем сама задача.
Воистину, если тебе так не нравится C#, .NET и прочее, то не морочь голову ни себе, ни людям.
Возможности параллельного программирования в дотнете воистину шикарны. Ты увидел лишь одну команду, и начал исходить желчью. Ты пробовал, например, OpenMP? Я пробовал, и немало. Там тоже влёгкую добиваешься распараллеливания. И так же как в дотнете, во многих случаях не получается никакого выигрыша. Естественно! Потому что алгоритм под параллельность нужно менять.
Я тут упоминал, что ToLower и Replace нужно перенести в параллельную часть. Но, естественно, ты прицепился к неэффективному алгоритму, написанному студентотой, и громогласно заявляешь, что linq сосёт.
Да, ты прав: в хорошей многопоточке вычисления нужно проводить параллельно с поступлением данных. Но опять же, - здесь мы видим неэффективный элементарный код, написанный на коленке. В дотнете есть ленивое чтение из файла. В данном случае можно применять его, только и всего.
Не спорю, более того все мои рассуждения основывались именно на этом предположении:
>перенесу стадии Replace('ё', 'е') и ToLower() после стадий Split и Where. Для простоты и оптимальности)
Я не говорю, что LINQ - совсем плохая технология. То есть, если б я делал ту лабу - я написал точно также (кроме оговоренных нюансов).
Она плоха лишь отчасти. Одна из причин - она отучает думать головой и заставляет думать, что всё за тебя ускорится само.
>если тебе так не нравится C#, .NET и прочее
Вы на говнокоде, сер.
>что linq сосёт.
Факт замедления кода в 20 раз просто не позволяет мне сказать, что оно рулит.
Как бы парадоксально это не звучало.
а может не изменит время
up to CLR
Дабы мои слова не казались беспочвенными наездами на чудесную технлогоию linq - предлагаю провести кому-нибудь, имеющему шарп и не менее 2-х физ. ядер простой эксперимент.
Создать список с 1000000 рандомных чисел.
И замерить скорость вычислений на нем (суммирования, например)
1. цикл for
2. linq
3. parallel linq
Запостить результаты в тред.
поиска (не)четных чисел в отсортированном мастак - да
сделаю завтра если никто раньше меня
коммутативность, ассоциативность, первый класс церковно-приходской
Просто жуть, что шарп с мозгами делает.
Всё еще хуже, чем я предполагал.
А randomы генерируй в [0,10000);
Хотя я зашел в тред и хотел предложить более честный и приближенный к практике вариант - умножение чисел по некоему модулю n.
Подобные дискретные умножения/возведения в степень используются не одним криптоалгоритмом.
Простое суммирование. LINQ в среднем вдвое медленнее for(4000 тиков против 2000), PLINQ вообще хренью занимается в 10 раз больше обычного(~80000 тиков).
А вообще - не стоит ковырять инты, нужно придумывать более сложные задачи с более сложными типами. Никто на дотНете высокопроизводительный вычислительный софт не пишет.
http://www.microsoft.com/en-us/sqlazurelabs/labs/numerics.aspx
Просто шикарно! Я думал оно процентов так на 20-30 замедлится.
Но это вообще замечательно - чудесная параллельная технология замедляющая вычисления в 10 раз.
И тут я подробно описал почему: http://govnokod.ru/9194#comment127899
Ну ладно, пусть чудо-LINQ замедляет код в 2 раза. Но тут торможение еще в 20 (двадцать)! раз от уже тормознутого LINQ.
PS. Благодарю за тесты.
+ аппаратную начинку тестовой станции в студию
вы совсем измерять производительность не умеете, да?
for: 306
linq: 2645
plinq: 1743 (в 1,5 раза быстрее)
вот и получается быстрее.
Абсолютно в говно правильно!
Зогчем мне голова? Пусть за меня думает Майкрософт.
>а может не изменит время
Как я и говорил чудес не бывает и этот ваш PLINQ соснул. Причем с причмоком.
нашел выше
Хотя ваши результаты тоже довольно интересны:
>for: 306
>linq: 2645
Замедление в 8 раз, против обычного цикла.
Мне знакомый гоп рассказывал, как они по приколу написали программу и обработали "Мастера и Маргариту", заменив
"," на ", бля, "
"?" на ", нахуй?"
В итоге получилось что-то типа "я, бля, пойду, нахуй?".
Ты прав, - вместо IEnumerable становится ParallelQuery, с тем же набором методов. Но параллельных.
там еще есть аргумент -- на сколько именно.
например: asParralel(32) делает код оптимальным для 32х ядерных машин.
.Print(DEFAULT_PRINTER) // Печатаем
.MakeCoffee("Black") // Делаем кофе
И вся программа из одной строки.
Не технология - мечта.