Почему это не параллельно.Цикл ForEach улучшает производительность?

#c# #.net #multithreading

#c# #.net #многопоточность

Вопрос:

У меня есть следующий код:

            if (!this.writeDataStore.Exists(mat))
            {
                BlockingCollection<ImageFile> imageFiles = new BlockingCollection<ImageFile>();
                Parallel.ForEach(fileGrouping, fi => DecompressAndReadGzFile(fi, imageFiles));


                this.PushIntoDb(mat, imageFiles.ToList());
            }
  

DecompressAndReadGzFile является статическим методом в том же классе, в котором содержится этот метод. Согласно названию метода, я распаковываю и считываю gz-файлы, их много, то есть до 1000, так что издержки распараллеливания окупаются за счет преимуществ. Однако я не вижу преимуществ. Когда я использую ANTS performance profiler, я вижу, что они выполняются точно в то же время, как если бы никакого распараллеливания не происходило. Я также проверяю ядра процессора с помощью process Explorer, и похоже, что, возможно, работа выполняется на двух ядрах, но одно ядро, похоже, выполняет большую часть работы. Чего я не понимаю в плане получения параллельного.Использовать каждый для распаковки и чтения файлов параллельно?

ОБНОВЛЕННЫЙ ВОПРОС: Какой самый быстрый способ считывать информацию из списка файлов?

Проблема (упрощенная):

  1. Существует большой список файлов .gz (1200).
  2. В каждом файле есть строка, содержащая «DATA: «, расположение и номер строки не являются статическими и могут варьироваться от файла к файлу.
  3. Нам нужно извлечь первое число после «DATA:» (просто для простоты) и сохранить его в объекте в памяти (например, в списке)

В первоначальном вопросе я использовал параллельный.Цикл ForEach, но я, похоже, не привязан к процессору более чем на 1 ядре.

Комментарии:

1. Выполняется ли какая-либо синхронизация в DecompressAndReadGzFile ?

2. Насколько мне известно, нет. Хотя есть вызов imageFiles. Добавить, который автоматически добавляет блокировку, насколько я понимаю.

Ответ №1:

Возможно ли, что потоки тратят большую часть своего времени на ожидание ввода-вывода? Читая несколько файлов одновременно, вы можете увеличить нагрузку на диск, чем при выполнении одной операции. Возможно, вы могли бы повысить производительность, используя последовательное чтение одним потоком, но затем распределяя декомпрессию с привязкой к процессору по отдельным потокам… но на самом деле вы можете обнаружить, что вам действительно нужен только один поток, выполняющий декомпрессию в любом случае, если диск работает медленнее, чем сам процесс декомпрессии.

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

(Вам также следует подумать о том, что вы делаете с распакованными файлами. Вы записываете их обратно на диск? Если это так, опять же, есть вероятность, что вы в основном ожидаете, что диск будет перегружен.)

Комментарии:

1. Я не записываю распакованные файлы на диск. Я считываю файлы сжатия в память с помощью GZipStream, создаю TextReader для извлечения того, что мне нужно, помещая это в коллекцию imageFiles в приведенном выше коде. Я мог бы попробовать ваше предложение по RAM-диску.

2. @Seth: Обратите внимание, что предложение ramdisk на самом деле просто предназначено для проверки того, что вы привязаны к вводу-выводу, а не к процессору. Если это так, то в конечном итоге вы просто перенесете затраты на этап «копирование данных на ramdisk».

3. Помните те драйверы программного обеспечения turbo Booster для жестких дисков много лет назад? Они ускорили дисковый ввод-вывод, сжимая данные в памяти, а затем записывая сжатые данные на диск.

Ответ №2:

есть ли вероятность, что ваш статический метод совместно использует какой-либо глобальный ресурс среди своих вызовов. Потому что в этом случае этот статический метод будет вызываться последовательно и никакой параллельной выгоды. Можете ли вы поместить код класса fileGrouping?