Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.57/7: Рейтинг темы: голосов - 7, средняя оценка - 4.57
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32

PLINQ: Читать файл, каждую строку преобразовывать и записывать преобразованную строку в другой файл

13.04.2015, 18:47. Показов 1508. Ответов 11
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
есть простая задача: читать файл, каждую строку преобразовывать и записывать преобразованную строку в другой файл. строки достаточно мудреные и на их конвертацию тратится прилично процессорных ресурсов. дабы разложить задачу по ядрам, решил ее распараллелить. для этого операцию конвертации объединил с выводом в файл и оформил в виде единственного метода. соорудил буфер-список, в который вычитываются строки из входного файла, по мере заполнения которого, выполняется вышеозначенный метод.

в общем, получилось вот так:

C#
1
2
3
4
5
6
7
8
9
/// <param name="Convertor">преобразователь строк</param>
/// <param name="Buf">строковый буфер</param>
/// <param name="Count">счетчик</param>
void DumpBuf(LineConvertor Convertor, List<string> Buf, ref int Count)
{
    Buf.AsParallel().ForAll(s => Convertor.Output(s));
    Buf.Clear();
    Count = 0;
}
замечательно все разливается по процессорным ядрам, но, конечно же, я столкнулся с тем, что порядок вывода строк в файл не соблюдается. еще немножко почитав умный хелп, я добавил метод AsOrdered():

C#
1
2
3
4
5
6
void DumpBuf(LineConvertor Convertor, List<string> Buf, ref int Count)
{
    Buf.AsParallel().AsOrdered().ForAll(s => Convertor.Output(s));
    Buf.Clear();
    Count = 0;
}
но это мало помогло. файл вот такого содержания

C#
1
2
3
4
5
6
7
8
9
10
00000001
00000002
00000003
00000004
00000005
00000006
00000007
00000008
00000009
00000010
при размере буфера 5 выдает беспорядочное следование строк в пределах буфера

C#
1
2
3
4
5
6
7
8
9
10
00000001
00000002
00000003
00000005
00000004
00000009
00000008
00000006
00000010
00000007
либо я не так понял назначение метода AsOrdered(), либо это ForAll() бермутит воду во пруду - т.е. делает все-равно все по-своему.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.04.2015, 18:47
Ответы с готовыми решениями:

Скопировать файл логов в другой файл, добавив в каждую строку определенный префикс в начале
Есть задача: скопировать файл логов в другой файл, добавив в каждую строку определенный префикс в начале. Проблема в том, что исходный файл...

Читать большой файл байтами и записывать в другой
потехоньку перехожу с дельфи на си диз подскажите как прочитать файл по байтам в массив размером 30 метров. сам делаю чеа лажа какаято...

Как скопировать строку c одного файла и вставить в другой файл эту же строку с символом в конце “_”
Подскажите, как можно через cmd скопировать строку с одного файла 1.txt и вставить в другой 2.txt, эту же строку и ниже в столбик ее только...

11
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
13.04.2015, 18:51
blackofe, файлы большие?
0
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
13.04.2015, 18:55  [ТС]
ну, вообще-то, да. около 2 гиг. в результате преобразований получаются файлы 5 гиг. кол-во строк - 7-8 млн.

Добавлено через 1 минуту
просто я попробовал распараллелить, и у меня получилось время обработки такого файла сократить вдвое - 22 минуты против 45. размер буфера я взял 16 (при 24-ядерной конфигурации хоста). т.е. результат обнадеживающий. но возникло дополнительное требование клиента: порядок строк должен сохраняться. вот и ломаю голову..
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
13.04.2015, 19:11
Держите класс:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public static class ArrayPerformanceHelper
    {
        public static TResult[] PartitionedParallelMap<T, TResult>(this T[] array, Func<T, TResult> map)
        {
            var result = new TResult[array.Length];
            Parallel.ForEach(Partitioner.Create(0, array.Length),
                             range =>
                             {
                                 for (int i = range.Item1; i < range.Item2; ++i)
                                     result[i] = map(array[i]);
                             });
            return result;
        }
 
        public static void PartitionedParallelMapDirty<T>(this T[] array, Func<T, T> map)
        {
            Parallel.ForEach(Partitioner.Create(0, array.Length),
                             range =>
                             {
                                 for (int i = range.Item1; i < range.Item2; ++i)
                                     array[i] = map(array[i]);
                             });
        }
    }
на вход даете ему кусок буффера, на выходе получаете смаппленный в том же порядке. Производительность должна возрасти (имеется ввиду относительно AsParallel), порядок сохранится.

Буффер лучше читать большими кусками, по 128мб хотя бы. Причем считали, отдали на обработку, а сами подгружаем дальше, то есть не блокирующе ждем, пока там нам обработают, а FireAndForget. То есть один поток читает и кормит данными метод, метод высчитывает всё, после чего уже третий поток записывает всё в результирующий файл. Конвеерная обработка такая.

Первый метод возвращает новый массив, второй метод "грязный" - портит массив, который ему передали, зато работает быстрее, а главное требует в 2 раза меньше памяти.

Добавлено через 7 минут
Ну а что касается порядка, нужно читать спеку, тогда не будете удивляться:
https://msdn.microsoft.com/ru-... 10%29.aspx

Operator ForAll<TSource>
Result when the source sequence is ordered Executes nondeterministically in parallel
Result when the source sequence is unordered Executes nondeterministically in parallel
1
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
13.04.2015, 20:04  [ТС]
написал ответ, но он куда-то подевался. попробую еще раз.

спасибо за ответ. с кодом буду разбираться.

два момента.

первый: под буфером (в контексте моей задачи) я подразумевал список строк. через AsParallel().ForAll() я хотел добиться одновременного (по возможности) выполнения операций над всеми (также по возможности) строками. поэтому нет смысла делать буфер слишком большим – не больше числа ядер в системе. и даже меньше. по наблюдениям задача работает быстрее с размером буфера 16 на 24-ядерном хосте, чем с размером буфера 24.

второй момент: наверное, я чуток подразобрался с AsOrdered() и изменил код так:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <param name="Convertor">преобразователь строки</param>
/// <param name="Writer">выходной поток</param>
/// <param name="Buf">строковый буфер</param>
/// <param name="Count">счетчик</param>
void DumpBuf(LineConvertor Convertor, TextWriter Writer, List<string> Buf, ref int Count)
{
    var outbuf = Buf.AsParallel()
                .AsOrdered()
                .Select<string, string>(s => Convertor.OutputStr(s));
    foreach(var v in outbuf)
        Writer.WriteLine(v);
    Buf.Clear();
    Count = 0;
}
результат правильный: строки выводятся в первоначальном порядке, потому что Select работает как раз как надо, а потом уже foreach поочередно выводит строки в файл. сейчас хочу посмотреть, насколько ухудшится при этом скорость работы.

Добавлено через 4 минуты
только что проверил. падение производительности не катастрофическое - 23 минуты против 22. можно жить!

Code
1
2
3
4
5
6
7
8
9
10
11
before:
 
Convert() #start
Convert() #end | 00:22:35.7822392
---[ 00:22:36.1887821 ]-------------------------
 
after:
 
Convert() #start
Convert() #end | 00:23:32.7713049
---[ 00:23:33.2773759 ]-------------------------
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
13.04.2015, 20:46
blackofe, еще раз: читаете массив строк, используете PartitionedParallelMapDirty, записываете получившийся строковый массив в файл, всё! Это в разы быстрее AsParallel...

Хотя конечно, если вы фанат LINQ и вам важен сам факт его использования, то AsParallel без конкурентов.

Добавлено через 3 минуты
Неужели вам не любопытно? Возьмите размер буфера 65536 элементов, попробуйте на нем метод, который я предложил выше, и выложите тест производительности.
1
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
14.04.2015, 16:18  [ТС]
попробую обязательно. просто хотелось и самому разобраться.

Добавлено через 16 часов 59 минут
попробовал использовать предложенный метод ArrayPerformanceHelper.PartitionedParall elMap (до dirty-версии пока не добрался). на небольших файлах (порядка 250 тыс. строк) он показал вполне ощутимый прирост производительности. файл, который AsParallel разбирал за 45 секунд, PartitionedParallelMap обработал за 35. но когда дело дошло до реальных файлов, он пасонул. на сервере с 24 ядрами и 48 гигами оперативки я получил следующие результаты:

AsParallel() без сохранения порядка:
Code
1
2
3
Convert() #start
Convert() #end | 00:22:35.7822392
---[ 00:22:36.1887821 ]-------------------------
AsParallel() c сохранением порядка:
Code
1
2
3
Convert() #start
Convert() #end | 00:23:32.7713049
---[ 00:23:33.2773759 ]-------------------------
с использованием ArrayPerformanceHelper (размер буфера 65536 строк. при средней длине строки 264 байта получаем примерно 17 с лишком мегабайт):
Code
1
2
3
4
Convert() #start
BUF SIZE: 65536
Convert() #end | 00:34:34.8510253
---[ 00:34:35.3678691 ]-------------------------
последний вариант оказывается раза в полтора медленнее. так что я остановлюсь на варианте AsParallel().AsOrdered().Select().

но за пример спасибо. возможно, в других случаях пригодится.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
14.04.2015, 16:41
blackofe, во-первых стоит использовать Dirty, т.к. вам не нужно оригинальный массив сохранять в целости, во-вторых можно поиграть с размером буфера - больше или меньше.
0
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
14.04.2015, 16:50  [ТС]
я как раз собираюсь попробовать "грязный" метод. буфер делал меньше, но на небольших файлах. в общем, в процессе.
0
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
15.04.2015, 20:42  [ТС]
докладываю. протестировал "грязный" метод. производительность показывает лучше, чем "чистый":

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Convert() #start
BUF SIZE: 2048
Convert() #end | 00:35:57.0407880
---[ 00:35:57.4490902 ]-------------------------
 
Convert() #start
BUF SIZE: 8192
Convert() #end | 00:25:20.3594743
---[ 00:25:20.7802348 ]-------------------------
 
Convert() #start
BUF SIZE: 4096
Convert() #end | 00:28:20.2104483
---[ 00:28:20.6245674 ]-------------------------
 
Convert() #start
BUF SIZE: 16384
Convert() #end | 00:25:36.5024515
---[ 00:25:36.9179491 ]-------------------------
 
Convert() #start
BUF SIZE: 32768
Convert() #end | 00:27:28.7140280
---[ 00:27:29.1233523 ]-------------------------
наилучший результат я получил при размере буффера 4-8 тыс. строк. но, однако же результат все-равно уступает варианту с упорядоченным AsParallel():

Code
1
2
3
4
Convert() #start
pool size = 16
Convert() #end | 00:23:28.7707673
---[ 00:23:29.2153615 ]-------------------------
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6101 / 4957 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
15.04.2015, 20:52
blackofe, ну, это очень странно, AsParallel по идее не может быть быстрее, т.к. базируется на тех же примитивах, только еще и больше ресурсов тратит, было даже целое исследование на эту тему. ИМХО как-то что-то не так делаете
0
0 / 0 / 0
Регистрация: 30.05.2014
Сообщений: 32
15.04.2015, 22:36  [ТС]
возможно не так. к примеру, в реализации с AsParallel я реюзаю буфер. а в случае с ArrayPerformanceHelper я каждый раз набиваю буфер заново, причем каждую строку снабжаю своим конвертором, чтобы избежать "наводок". так что сравнение нечестное. по хорошему, надо было бы во-первых, сделать конвертор статическим (по идее это можно было бы сделать), а во-вторых, реюзать буфер. тогда, наверняка ArrayPerformanceHelper показал бы результат лучше, чем AsParallel.

если дойдут руки, попробую это реализовать.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
15.04.2015, 22:36
Помогаю со студенческими работами здесь

Как записывать в файл на новую строку?
Помогите пожалуйста.. Как записывать в файл на новую строку (запись производится в конец предыдущей строки).. Программа состряпана до жути...

Вывести строку, содержащую эти же слова, но расположенные в обратном порядке. Записать полученную строку в другой файл
Доброго времени суток! Крайне необходима помощь экспертов в С++! Кто может - не оставьте меня в беде))) Вот задания: ...

Не работает код (программа считывает из файла строку, убирает лишние пробелы и записывает в другой файл строку, словами наоборот)
#include &quot;stdafx.h&quot; #include &lt;iostream&gt; #include &lt;fstream&gt; #include &lt;string&gt; #include &lt;algorithm&gt; using namespace std; string...

Как читать строку из .ini файла?
Привет вмем! У меня такая прблема, пишу в TIniFile следушее TIniFile *ini = new TIniFile(L&quot;C:\\test.ini&quot;); ...

Дан файл. Удалить из него третью строку. Результат записать в другой файл
Дан файл. Удалить из него третью строку. Результат записать в другой файл. Формат входных данных В файле записано несколько...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru