Parallel.For, хочу посоветоваться

Здравая мысль.

А надо ли основному потоку (который может тоже исполнять что-то параллельно, т.к. будет предусмотрена асинхронная работа for'а) тоже назначать affinity mask из 1 бита на время работы for'а?

А что мне миллион?

Я миллиард хочу

6.8 сек против 2.2 сек без lock'а (разница в 3+ раза).

Но так тоже неинтересно.

Запустим 10 потоков, которые делают "lock inc" в цикле (по 100 млн итераций каждая).

Получим 26.5 сек против 0.7 сек (разница почти в 40 раз!).

Понятное дело, что на практике потоки не будут делать в цикле

lock inc

, но эксперимент вполне показательный.

Тут на другом форуме предложили ещё одну идею, где lock'ов будет ещё меньше, чуть позже опишу технологию.

Добавлено через 1 минуту

Можно и DLL сделать, и OBJ...

Добавлено через 2 минуты

Ethereal

,

VTsaregorodtsev

(и другие), что думаете по поводу кол-ва потоков (вопрос 1) и границ цикла (вопрос 3).

Ещё я думаю, что нужно при запуске for'а задавать (опционально) процедуру обработки результата, которая будет запускаться после for'а (в нитке, выполнившей последнюю итерацию). Полезно при асинхронных запусках for'а.

Зачем запускать for асинхронно? К примеру, для отображения % выполнения в основном потоке или для ожидания ввода юзером какого-то значения (которое понадобится позже) и пр.

Добавлено через 2 минуты

Меня удивляет, почему в Embarcadero не додумались передавать в процедуру цикла номер потока. Ведь так можно выделить заранее массив из N элементов (где N - кол-во потоков) и сбрасывать туда результаты работы итерации безо всяких lock'ов, если результаты, например, суммируются.

Добавлено через 34 минуты

Сделал сейчас (на Delphi) прогу, суммирующую побайтно в 8 потоках область памяти длиной в 1 Гб (каждый поток суммирует в свою переменную).

После суммирования делается

inc

отдельной переменной (в одном случае с lock'ом, в другом - без).

Результат: без lock'а - 1.2 сек, с lock'ом - 32.5 сек (в 27 раз)!

Если делать этот

inc

/

lock inc

не каждый раз, а 1 раз в 4 итерации, то получим 8.6 сек против 1.35 сек (6+ раз).

Процедуры не очень типичные, но вполне реалистичные

Добавлено через 12 минут

Тот "загадочный ещё один метод с другого форума" больше похож на первый предложенный мной вариант, поэтому ещё такой момент интересует...

Т.к. итерации всё равно будут идти не совсем последовательно (скажем, 1,3,2,5,4,6,7,9,8,11,10,13,12...), может, вообще стоит делить весь цикл на равные части и отдавать потокам целиковые куски?

Т.е. не так:

thread1=0,4,8,12...

thread2=1,5,9,13...

thread3=2,6,10,14...

thread4=3,7,11,15...

А вот так:

thread1=0..249

thread2=250..499

thread3=500..749

thread4=750..999

Для упрощения реализации (и ускорения).

В итоге итерации будут происходить примерно в таком виде: (0,250,750,1,1000,251,751,1001,252,2,752,253,1002,3,753,503,1003...).

Или не стоит, т.к. иногда этот вариант может быть более напряжным? Хотя и там, и там цикл не последовательный...

Добавлено через 6 минут

На самом деле всё решается внутри процедуры цикла элементарным

i+start

.

Если делать совсем по-нормальному, тогда нужно и шаг приращения указывать (включая отрицательные значения), чего я делать не буду.

Если сравнить сишный и паскалевский for, то возможности второго тоже сильно урезаны.

Так что... реально ли нужно это начальное значение или всё же 0 достаточно?

p.s. Наверное, я всё же буду исходить из сложности реализации, но сдаётся мне, что проблем с этим быть не должно.