Чи повинен я використовувати ThreadPools або паралельну бібліотеку завдань для операцій, пов'язаних з IO


78

В одному зі своїх проектів, який є свого роду агрегатором, я аналізую канали, подкасти тощо з Інтернету.

Якщо я використовую послідовний підхід, враховуючи велику кількість ресурсів, для їх обробки потрібен досить багато часу (через проблеми з мережею та подібні речі);

foreach(feed in feeds)
{
   read_from_web(feed)
   parse(feed)
}

Отже, я хочу реалізувати паралельність і не міг вирішити, чи повинен я в основному використовувати ThreadPools для обробки з робочими потоками, або просто покладатися на TPL, щоб його сортувати.

ThreadPools напевно впорається з роботою для мене за допомогою робочих потоків, і я отримаю те, що я очікую (а в багатоядерних центральних процесорах також будуть використані й інші ядра).

паралельність

Але я все одно хочу також розглянути TPL, оскільки це рекомендований метод, але я трохи стурбований цим. Перш за все, я знаю, що TPL використовує ThreadPools, але додає додатковий рівень прийняття рішень. Мене здебільшого турбує стан, коли є одноядерне середовище. Якщо я не помиляюся, TPL починається з числа робочих потоків, рівного кількості доступних CPU-ядер на самому початку. Я боюся, що TPL не дасть результатів, подібних до послідовного підходу для мого випадку, пов'язаного з IO.

Тож для операцій, пов’язаних з IO (у моєму випадку читання ресурсів з Інтернету), чи найкраще використовувати ThreadPools і контролювати речі, або краще просто покладатися на TPL? Чи може TPL також використовуватися в сценаріях, пов'язаних з IO?

Оновлення : Моє головне занепокоєння полягає в тому, що - в одноядерному процесорному середовищі TPL буде просто поводитися як послідовний підхід або все одно запропонує паралельність? Я вже читаю паралельне програмування з Microsoft .NET і тому книгу, але не міг знайти точної відповіді на це.

Примітка: це переформулювання мого попереднього запитання [ чи можна використовувати паралельність потоків та паралелізм разом? ], що було сформульовано неправильно.

Відповіді:


107

Тому я натомість вирішив написати тести для цього та побачити це на практичних даних.

Легенда тесту

  • Itr: Ітерація
  • Послідовність: послідовний підхід.
  • PrlEx: Паралельні розширення - Parallel.ForEach
  • TPL: Паралельна бібліотека завдань
  • TPool: ThreadPool

Результати тесту

Одноядерний процесор [Win7-32] - працює під керуванням VMWare -

Test Environment: 1 physical cpus, 1 cores, 1 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.82s  04.05s  02.69s  02.60s
#2      07.48s  03.18s  03.17s  02.91s
#3      07.66s  03.21s  01.90s  01.68s
#4      07.43s  01.65s  01.70s  01.76s
#5      07.81s  02.20s  01.75s  01.71s
#6      07.67s  03.25s  01.97s  01.63s
#7      08.14s  01.77s  01.72s  02.66s
#8      08.04s  03.01s  02.03s  01.75s
#9      08.80s  01.71s  01.67s  01.75s
#10     10.19s  02.23s  01.62s  01.74s
________________________________________________________________________________

Avg.    08.40s  02.63s  02.02s  02.02s
________________________________________________________________________________

Одноядерний процесор [WinXP] - працює під керуванням VMWare -

Test Environment: 1 physical cpus, NotSupported cores, NotSupported logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.79s  04.05s  02.75s  02.13s
#2      07.53s  02.84s  02.08s  02.07s
#3      07.79s  03.74s  02.04s  02.07s
#4      08.28s  02.88s  02.73s  03.43s
#5      07.55s  02.59s  03.99s  03.19s
#6      07.50s  02.90s  02.83s  02.29s
#7      07.80s  04.32s  02.78s  02.67s
#8      07.65s  03.10s  02.07s  02.53s
#9      10.70s  02.61s  02.04s  02.10s
#10     08.98s  02.88s  02.09s  02.16s
________________________________________________________________________________

Avg.    08.46s  03.19s  02.54s  02.46s
________________________________________________________________________________

Двоядерний процесор [Win7-64]

Test Environment: 1 physical cpus, 2 cores, 2 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      07.09s  02.28s  02.64s  01.79s
#2      06.04s  02.53s  01.96s  01.94s
#3      05.84s  02.18s  02.08s  02.34s
#4      06.00s  01.43s  01.69s  01.43s
#5      05.74s  01.61s  01.36s  01.49s
#6      05.92s  01.59s  01.73s  01.50s
#7      06.09s  01.44s  02.14s  02.37s
#8      06.37s  01.34s  01.46s  01.36s
#9      06.57s  01.30s  01.58s  01.67s
#10     06.06s  01.95s  02.88s  01.62s
________________________________________________________________________________

Avg.    06.17s  01.76s  01.95s  01.75s
________________________________________________________________________________

Чотириядерний процесор [Win7-64] - Підтримується HyprerThreading -

Test Environment: 1 physical cpus, 4 cores, 8 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.56s  02.03s  01.71s  01.69s
#2      07.42s  01.63s  01.71s  01.69s
#3      11.66s  01.69s  01.73s  01.61s
#4      07.52s  01.77s  01.63s  01.65s
#5      07.69s  02.32s  01.67s  01.62s
#6      07.31s  01.64s  01.53s  02.17s
#7      07.44s  02.56s  02.35s  02.31s
#8      08.36s  01.93s  01.73s  01.66s
#9      07.92s  02.15s  01.72s  01.65s
#10     07.60s  02.14s  01.68s  01.68s
________________________________________________________________________________

Avg.    08.35s  01.99s  01.75s  01.77s
________________________________________________________________________________

Узагальнення

  • Незалежно від того, працюєте ви в одноядерному або багатоядерному середовищі, Parallel Extensions, TPL і ThreadPool поводиться однаково і дає приблизні результати .
  • Тим не менш, TPL має такі переваги, як легка обробка винятків, підтримка скасування та можливість легко повернути результати Завдання . Хоча паралельне розширення - це ще одна життєздатна альтернатива.

Запуск тестів самостійно

Ви можете завантажити джерело тут і запустити самостійно. Якщо ви можете опублікувати результати, я також додам їх.

Оновлення: Виправлено вихідне посилання.


6
+1 Для вирішення вашої проблеми за допомогою наукового підходу. Якщо я не помиляюся, усі вищезазначені технології використовують пул потоків, що пояснює подібні результати. Загалом мені подобається використовувати паралельні розширення, оскільки синтаксис настільки простий, і я ледачий.
Jacobs Data Solutions

Я трохи очистив джерела; github.com/raistlinthewiz/concurrency-tests
HuseyinUslu

1
Дякуємо, що пройшли всю роботу, щоб виконати тест.
GregoryBrad

15

Якщо ви намагаєтеся максимізувати пропускну здатність для завдань, пов’язаних із введенням-виведенням, ви обов’язково повинні поєднувати традиційні API асинхронної моделі обробки (APM) з роботою на основі TPL. API APM - це єдиний спосіб розблокувати потік процесора, поки асинхронний зворотний виклик введення-виведення очікує на розгляд. TPL надає в TaskFactory::FromAsyncдопоміжний метод для надання допомоги в об'єднанні APM і TPL коду.

Перегляньте цей розділ .NET SDK на MSDN під назвою TPL та традиційне .NET асинхронне програмування, щоб отримати додаткову інформацію про те, як поєднати ці дві моделі програмування для досягнення асинхронної нірвани.


2

Ви маєте рацію, що TPL видаляє частину елементів керування, коли ви створюєте власний пул потоків. Але це правильно лише в тому випадку, якщо ви не хочете копати глибше. TPL дозволяє створювати тривалі завдання, які не є частиною пулу потоків TPL і можуть слугувати вашій меті. Опублікована книга, що читається безкоштовно « Паралельне програмування» з Microsoft .NET , дасть вам набагато більше розуміння того, як передбачається використовувати TPL. У вас завжди є можливість вказати Paralle.For, Tasks явні параметри, скільки потоків слід виділити. Окрім цього, ви можете замінити планувальник TPL своїм власним, якщо вам потрібен повний контроль.


1

Ви можете призначити власний планувальник завдань для завдання TPL. За замовчуванням робота з викрадення досить розумна.


Я прочитав цей ресурс і насправді мене це турбує; У .NET Framework 4 планувальник завдань за замовчуванням тісно інтегрований з пулом потоків. Якщо ви використовуєте планувальник завдань за замовчуванням, робочими потоками, які виконують паралельні завдання, керує клас .NET ThreadPool. Як правило, на вашому комп’ютері принаймні стільки робочих потоків, скільки ядер . - Що робити, якщо я працюю в одноядерному середовищі? Я не можу знайти жодних роз’яснень щодо цього ..
ХусейінУслу

1
Отже, ви хочете застосувати мінімальний ступінь паралелізму, наприклад, в цьому випадку на останніх X градусах? TaskCreationOptions.LongRunning може бути тим, що ти хочеш. Спеціальний планувальник - це ще один варіант, якщо ви не можете переконати стандартного планувальника робити те, що ви хочете.
ehnmark

Насправді, здається, на основі результатів тестування, які я опублікував, мої занепокоєння були неправдивими.
HuseyinUslu

0

Я боюся, що TPL не дасть результатів, подібних до послідовного підходу для мого випадку, пов'язаного з IO.

Я думаю, що буде. Що таке вузьке місце? Розбір чи завантаження? Багатопотоковість не сильно допоможе вам із завантаженням з Інтернету.

Я б використовував паралельну бібліотеку завдань для обрізання, застосування маски або ефектів для завантажених зображень, вирізання зразка з подкасту тощо. Це більш масштабовано.

Але це буде не порядок прискорення. Витратьте свої ресурси на впровадження деяких функцій, тестування.

PS. "Вау, моя функція виконується за 0,7 с замість 0,9";)


Вузьке місце - це завантаження вмісту. Звичайний користувач передплачує 10+ каналів, і з моїх тестів потрібно 15+ секунд - принаймні для мого тестового підмножини - щоб завантажити та проаналізувати всі з послідовним підходом. Я відчуваю, що є достатньо місця для вдосконалення.
HuseyinUslu

На основі результатів тестів, які я опублікував, здається, це правда, що місця для вдосконалення було достатньо.
HuseyinUslu

0

Якщо ви розпаралелізуєте свої дзвінки на URL-адреси, я думаю, це покращить вашу програму, навіть якщо вона має лише одне ядро. Погляньте на цей код:

var client = new HttpClient();
var urls = new[]{"a", "url", "to", "find"};

// due to the EAP pattern, this will run in parallel.
var tasks = urls.Select(c=> client.GetAsync(c));

var result = Tasks.WhenAll(task).ContinueWith(a=> AnalyzeThisWords(a.Result));
result.Wait(); // don't know if this is needed or it's correct to call wait

Різниця між багатопотоковістю та асинхронністю в цьому випадку полягає в тому, як здійснюється зворотний виклик / завершення.

При використанні EAP кількість завдань не пов'язана з кількістю потоків.

Оскільки ви покладаєтесь на завдання GetAsync, клієнт http використовує мережевий потік (сокет, клієнт tcp або інший) і сигналізує про це, щоб викликати подію після завершення BeginRead / EndRead. Отже, в цей момент не задіяні нитки.

Після виклику завершення може бути створений новий потік, але TaskScheduler (використовується у виклику GetAsync / ContinueWith call) створює новий потік, використовує існуючий потік або вбудовує завдання, щоб використовувати викличний потік.

Якщо AnalyzeThisWordsблокування надто багато часу, ви починаєте отримувати вузькі місця, оскільки "зворотний виклик" у ContinueWith робиться від працівника пулу потоків.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.