#c# #multithreading
#c# #многопоточность
Вопрос:
У меня есть длительный процесс, который считывает большие файлы и записывает сводные файлы. Чтобы ускорить процесс, я обрабатываю несколько файлов одновременно, используя обычные старые потоки:
ThreadStart ts = new ThreadStart(Work);
Thread t = new Thread(ts);
t.Start();
Я обнаружил, что даже при чтении отдельными потоками отдельных файлов и отсутствии блокировки между ними и использовании 4 потоков на 24-ядерном блоке я не могу получить даже до 10% на CPU или 10% на дисковом вводе / выводе. Если я использую больше потоков в своем приложении, кажется, что оно работает еще медленнее.
Я бы предположил, что я делаю что-то не так, но любопытно, что если я запускаю весь exe-файл во второй и третий раз, то он фактически обрабатывает файлы в два и три раза быстрее. Мой вопрос в том, почему я не могу получить 12 потоков в моем одном приложении для обработки данных и налогообложения компьютера, а также 4 потока в 3 экземплярах моего приложения?
Я профилировал приложение, и наиболее трудоемкие и часто вызываемые функции — это все вызовы обработки строк.
Комментарии:
1. Невозможно сказать без фактического кода, выполняющего обработку файла.
2. Что сказал вам запуск профиля о том, где находится узкое место?
3. Должны быть некоторые общие места (к которым обращаются потоки обработки) с кодом блокировки / синхронизации, не могли бы вы поделиться этим кодом синхронизации
4. По сути, функция Work() просто считывает строку из gzipstream через streamreader, анализирует ее и записывает в буфер, который периодически сохраняется на диске.
5. Согласен с Дэниелом, нужно больше кода. Одна вещь, которую вы могли бы искать, — это все, что разделяется между потоками. Если один поток ожидает, пока другой освободит ресурс, это может замедлить работу.
Ответ №1:
Возможно, ваша вычислительная проблема связана не с процессором, а с вводом-выводом. Это не помогает утверждать, что ваш дисковый ввод-вывод «только на 10%». Я не уверен, что такой счетчик производительности вообще существует.
Причина, по которой он становится медленнее при использовании большего количества потоков, заключается в том, что все эти потоки пытаются одновременно получить доступ к своим соответствующим файлам, в то время как дисковой подсистеме трудно совместить все разные потоки. Видите ли, даже при использовании современных технологий, таких как твердотельные накопители, где время поиска на несколько порядков меньше, чем на традиционных жестких дисках, все равно возникает штраф.
Скорее, вы должны сделать вывод, что ваша проблема связана с диском, и один поток, вероятно, будет самым быстрым способом решения вашей проблемы.
Можно утверждать, что вы могли бы использовать асинхронные методы для обработки прочитанного бита, в то время как в фоновом режиме считывается следующий бит, но я думаю, что вы увидите очень небольшое улучшение производительности.
У меня была похожая проблема не так давно в небольшом инструменте, где я хотел вычислить подписи MD5 всех файлов на моем жестком диске, и я обнаружил, что процессор работает слишком быстро по сравнению с системой хранения, и я получил аналогичные результаты, пытаясь повысить производительность, используя больше потоков.
Использование библиотеки параллельных задач не решило эту проблему.
Комментарии:
1. Я согласен с тем, что вы говорите, но я не понимаю, почему, если проблема с дисковым вводом-выводом, я могу обрабатывать файлы быстрее, используя несколько исполняемых файлов поверх одного exe с большим количеством потоков. Я чувствую, что, вероятно, делаю что-то не так, но я не знаю, что, поскольку каждый поток работает независимо.
2. Ускоряется ли ваш процесс, когда вы ничего не делаете с материалом, который вы читаете? Возможно, вам следует отключить это и проверить результат. Я предполагаю, что часть выполняемой вами обработки имеет проблемы с блокировкой, даже если вы пока этого не видите.
3. Насколько быстрее быстрее? Мы говорим об увеличении производительности, состоящем из одной или двух цифр?
4. Вы БУФЕРИЗУЕТЕ файлы или используете FileReader для потока Gzip? Затем… добро пожаловать в ад ввода-вывода.
Ответ №2:
Прежде всего, на 24-ядерном блоке, если вы используете только 4 потока, максимальный процессор, который он когда-либо мог использовать, составляет 16,7%, так что на самом деле вы получаете 60% использования, что довольно хорошо.
Трудно сказать, привязана ли ваша программа к вводу / выводу на данный момент, я предполагаю, что это так. Вам нужно запустить профилировщик в вашем проекте и посмотреть, на какие разделы кода ваш проект тратит большую часть своего времени. Если он находится на операции чтения / записи, он связан с вводом-выводом.
Возможно, у вас используется какая-то форма блокировки между потоками. Это приведет к замедлению работы программы по мере добавления дополнительных потоков, и да, запуск второго процесса исправит это, но также исправит вашу блокировку.
Все это сводится к тому, что без информации о профилировании мы не можем сказать, ускорит ли использование второго процесса или замедлит работу, нам нужно знать, зависает ли программа на операции ввода-вывода, операции блокировки или просто занимает много времени в функции, которая может бытьлучше распараллеливается.
Комментарии:
1. Это хорошо — кажется, что плакат плох в математике первого уровня, и остальные другие люди тоже. Вот объяснение: поток выполняется только на одном ядре. Когда-либо. Не может работать на 2 ядрах одновременно. Таким образом, при 100% использовании 4 ядер …. из 24 …. уровень использования os 4/24 = максимум 16,7 ( немного для ОС) в любом случае. Физически невозможно получить больше. Хотите 100%? Перейдите на 4-ядерную машину.
2. @TomTom Как вы можете прочитать в вопросе, после 4 потоков приложение фактически теряет производительность. Кроме того, на моем 4-ядерном компьютере такое же поведение наблюдается, когда я помещаю более 2 потоков в какую-то интенсивную задачу. Для меня очевидно, что приложение имеет максимальный временной интервал, выделенный ОС, и все его потоки должны совместно использовать этот временной интервал.
3. @ThunderGr Каждый поток получает свой собственный временной интервал, который является точкой потока. Одноядерный компьютер может запускать один одновременный временной интервал, двухъядерный процессор может запускать два одновременных временных интервала и т. Д… Проблема с постером заключается в том, что вычислительная мощность процессора — это не его проблема, его программа ожидает либо на диске возврата некоторого ввода-вывода, либо на каком-либо ресурсе, который заблокирован для освобождения. Добавление большего количества потоков увеличивает нехватку всего, что не хватает в его программе, и замедляет ее работу
4. И как это может объяснить тот факт, что, когда poster запускает новый процесс, работа ускоряется? Если ввод-вывод или ресурс заблокирован, не повлияет ли это и на новые процессы?
5. @ThunderGr Как вы получили это от OP, когда он сказал: «Если я использую больше потоков в своем приложении, кажется, что оно работает еще медленнее».
Ответ №3:
Я думаю, вы выяснили, что файловый кэш не идеален в случае, когда один процесс одновременно записывает данные во многие файлы. Файловый кэш должен синхронизироваться с диском, когда количество грязных страниц кэша превышает пороговое значение. Кажется, что одновременные записи в одном процессе достигают порогового значения быстрее, чем запись одного потока. Вы можете прочитать о кэше файловой системы здесь Производительность и настройка файлового кэша
Ответ №4:
Попробуйте использовать библиотеку задач из .net 4 (System.Многопоточность.Задача). Эта библиотека имеет встроенные оптимизации для разного количества процессоров.
Понятия не имею, в чем ваша проблема, возможно, потому, что ваш фрагмент кода на самом деле не информативен
Комментарии:
1. Использование библиотеки задач не решит его проблему, он уже использует потоки, которые, когда вы копаете достаточно глубоко, будут использовать Task.
2. Спасибо за информацию, капитан. Я просто имел в виду, что в библиотеке задач есть оптимизация планирования и количества потоков. Это не то же самое, что вы используете грубые потоки. Хорошего дня