Логика программирования - разделение задач между потоками - PullRequest
0 голосов
/ 18 июня 2011

Допустим, вы хотите, чтобы 5 потоков обрабатывали данные одновременно.Также предположим, что у вас есть 89 задач, которые нужно обработать.

Вы знаете, что 89/5 = 17 с остатком 4. Лучший способ разделить задачи - это иметь процесс 4 (остаток) потоковУ 18 (17 + 1) задач каждая, а затем 1 (# threads - остаток) поток для обработки 17.

Это исключит остаток.Просто для проверки:

Thread 1: Tasks  1-18  (18 tasks)
Thread 2: Tasks 19-36  (18 tasks)
Thread 3: Tasks 37-54  (18 tasks)
Thread 4: Tasks 55-72  (18 tasks)
Thread 5: Tasks 73-89  (17 tasks)

Предоставление вам в общей сложности 89 выполненных задач.

Мне нужен способ получить начальный и конечный диапазон каждого потока математически / программируемо;где следующее должно напечатать точную вещь, которую я перечислил выше:

$NumTasks = 89
$NumThreads = 5
$Remainder = $NumTasks % $NumThreads 
$DefaultNumTasksAssigned = floor($NumTasks / $NumThreads)

For $i = 1 To $NumThreads
    if $i <= $Remainder Then
        $NumTasksAssigned = $DefaultNumTasksAssigned + 1
    else
        $NumTasksAssigned = $DefaultNumTasksAssigned
    endif
    $Start = ??????????
    $End = ??????????
    print Thread $i: Tasks $Start-$End ($NumTasksAssigned tasks)
Next

Это также должно работать для любого числа $NumTasks.

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

Ответы [ 3 ]

7 голосов
/ 18 июня 2011

Почему? Вместо того, чтобы заранее определять порядок планирования, поместите все задачи в очередь, а затем попросите каждый поток выполнить их одну за другой, когда они будут готовы. Тогда ваши задачи будут выполняться «как можно быстрее».

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

Думайте об этом как о банке с 1 линией на счетчика против одной линии и большим количеством счетчиков. В первом случае вы можете застрять позади человека, который вносит монеты и считает их по одному, а второй вы переходите к следующему доступному кассиру, а мистер PocketChange отсчитывает время.

1 голос
/ 18 июня 2011

Я второе замечание Уилла Хартунга. Вы можете просто кормить их одной задачей за раз (или несколькими задачами за раз, в зависимости от того, много ли у вас накладных расходов, то есть, если отдельные задачи обычно выполняются очень быстро по сравнению со стоимостью запуска / утилизации потоков). Ваши последующие комментарии эффективно объясняют, что ваши «потоки» несут большие затраты на создание, и, следовательно, вы хотите однократно выполнить их с максимально возможной работой, вместо того, чтобы тратить время на создание новых «потоков», каждый из которых питает небольшой объем работы.

Во всяком случае ... собирается на math question ...

Если вы хотите назначить задачи только один раз, используйте следующую формулу вместо ????????? в твоей логике, надо делать свое дело:

$Start = 1 
         + (($i -1) * ($DefaultNumTasksAssigned + 1) 
        - (floor($i / ($Remainder + 1)) * ($i - $Remainder))
$End = $Start + $NumTasksAssigned -1

Формула объясняется следующим образом:
1 для того факта, что ваш дисплей / логика основана на единице, а не на нуле Второй термин заключается в том, что мы обычно добавляем ($ DefaultNumTasksAssigned + 1) с каждой итерацией.
Третий член дает поправку за последние несколько итераций.
Его первая часть, (floor($i / ($Remainder + 1)) обеспечивает 0, пока $ i не достигнет первого потока
это не получает одно дополнительное задание, и 1 после этого.
Вторая часть выражает, сколько нам нужно исправить.

Формула для $ End проще, единственный трюк - минус 1, потому что значения Start и End являются включающими (например, между 1 и 19 есть 19 задач, а не 18)

Следующая слегка измененная часть логики также должна работать: она избегает «причудливой» формулы, сохраняя текущую вкладку переменной $ Start, а не пересчитывая ее каждый раз.

$NumTasks = 89
$NumThreads = 5
$Remainder = $NumTasks % $NumThreads 
$DefaultNumTasksAssigned = floor($NumTasks / $NumThreads)
$Start = 1
For $i = 1 To $NumThreads
    if $i <= $Remainder Then    // fixed here!  need <= because $i is one-based
        $NumTasksAssigned = $DefaultNumTasksAssigned + 1
    else
        $NumTasksAssigned = $DefaultNumTasksAssigned
    endif
    $End = $Start + $NumTasksAssigned -1
    print Thread $i: Tasks $Start-$End ($NumTasksAssigned tasks)

    $Start = $Start + $NumTasksAssigned
Next

Вот приведенная выше Python-транскрипция

>>> def ShowWorkAllocation(NumTasks, NumThreads):
...   Remainder = NumTasks % NumThreads
...   DefaultNumTasksAssigned = math.floor(NumTasks / NumThreads)
...   Start = 1
...   for i in range(1, NumThreads + 1):
...     if i <= Remainder:
...        NumTasksAssigned = DefaultNumTasksAssigned + 1
...     else:
...        NumTasksAssigned = DefaultNumTasksAssigned
...     End = Start + NumTasksAssigned - 1
...     print("Thread ", i, ": Tasks ", Start, "-", End, "(", NumTasksAssigned,")")
...     Start = Start + NumTasksAssigned
...
>>>
>>> ShowWorkAllocation(89, 5)
Thread  1 : Tasks  1 - 18 ( 18 )
Thread  2 : Tasks  19 - 36 ( 18 )
Thread  3 : Tasks  37 - 54 ( 18 )
Thread  4 : Tasks  55 - 72 ( 18 )
Thread  5 : Tasks  73 - 89 ( 17 )

>>> ShowWorkAllocation(11, 5)
Thread  1 : Tasks  1 - 3 ( 3 )
Thread  2 : Tasks  4 - 5 ( 2 )
Thread  3 : Tasks  6 - 7 ( 2 )
Thread  4 : Tasks  8 - 9 ( 2 )
Thread  5 : Tasks  10 - 11 ( 2 )
>>>

>>> ShowWorkAllocation(89, 11)
Thread  1 : Tasks  1 - 9 ( 9 )
Thread  2 : Tasks  10 - 17 ( 8 )
Thread  3 : Tasks  18 - 25 ( 8 )
Thread  4 : Tasks  26 - 33 ( 8 )
Thread  5 : Tasks  34 - 41 ( 8 )
Thread  6 : Tasks  42 - 49 ( 8 )
Thread  7 : Tasks  50 - 57 ( 8 )
Thread  8 : Tasks  58 - 65 ( 8 )
Thread  9 : Tasks  66 - 73 ( 8 )
Thread  10 : Tasks  74 - 81 ( 8 )
Thread  11 : Tasks  82 - 89 ( 8 )
>>>
0 голосов
/ 18 июня 2011

Я думаю, что вы решили не ту половину своей проблемы .

Будет практически невозможно точно определить время, которое потребуется для выполнения всех ваших задач, если только всеверно следующее:

  • ваши задачи на 100% связаны с процессором: то есть они используют 100% процессоров во время работы и не требуют никаких операций ввода-вывода
  • ни одна из ваших задач не должна синхронизироваться с любыми другими вашими задачами
  • у вас ровно столько потоков, сколько у вас процессоров
  • компьютер, на котором выполняются эти задачи, не выполняет никакихдругие интересные задачи одновременно

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

Наконец, если у вас нет действительно странного оборудования, вряд ли у вас может быть ровно пятьпотоки работают одновременно.(Обычно конфигурации процессоров бывают кратны, по крайней мере, двум.) Обычно предпочтительным вариантом является примерно 1 поток на процессор, если ваши задачи сильно загружены процессором, около 2 потоков на процессор, если задачи тратят половину своего времени на загрузку процессора иполовину своего времени на выполнение операций ввода-вывода и т. д.

tl; dr : Нам нужно узнать гораздо больше о том, как выглядят ваши задачи и оборудование, прежде чем мы сможем проконсультировать вас по этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...