Я пишу тестовое приложение, которое имеет число потоков и количество операций для выполнения в качестве входных параметров. Каждый тест создается как отдельный класс, который имеет метод Execute (int numberOfRepeats). numberOfRepeats – фактически количество повторений в каждом потоке.
Я создаю тесты следующим образом:
Например, у меня есть 32 потока и 50 длительных эталонных операций. Таким образом, каждый поток должен выполнить операцию 50/32 = 1 (фактически на самом деле 1,56), что даст общее число 32 операций для всех потоков.
Я использую простую конструкцию “new Thread()” для многопоточности и AutoResetEvent с конструкцией WaitHandle.WaitAll для синхронизации выполнения и измерения общего времени.
Я попробовал Parallel.For с ParallelOptions.MaxDegreeOfParallelism как число потоков, но на самом деле он не запускает тесты со всеми потоками. С числом операций 100k из threadpool с использованием ParallelOptions.MaxDegreeOfParallelism = 128 использовалось только 20 потоков.
И теперь вопрос. Как я могу разделить операции между потоками для выполнения точного числа операций в описанной ситуации?
Благодарю!
Параллельный планировщик не использует так, что многие потоки, потому что он достаточно умный, чтобы знать, когда это делает, ухудшит производительность.
Из MSDN:
Пул потоков.NET динамически адаптируется к изменяющимся рабочим нагрузкам, позволяя с течением времени изменять число рабочих потоков для параллельных задач. Во время работы система наблюдает, увеличивает ли количество потоков улучшение или ухудшает общую пропускную способность и соответственно корректирует количество рабочих потоков.
Если вы используете столько потоков для выполнения теста, вам следует пересмотреть свою реализацию. Вы снижаете свою общую производительность, так как потоки будут сражаться с каждым из циклов, и это последнее, что вы хотите, когда пытаетесь выполнять работу с учетом времени, например, бенчмаркинг.
Я нашел простой способ разделить любое количество операций между потоками. Этот метод возвращает массив из int, где каждый элемент означает количество операций для потока с индексом i.
private static int[] splitTasksForThreads(int numberOfThreads, int numberOfTasks)
{
var tasksRepeatArray = new int[numberOfThreads];
var taskPerThread = numberOfTasks / numberOfThreads;
var diff = numberOfTasks - (numberOfThreads * taskPerThread);
if (diff == 0)
{
for (int i = 0; i < numberOfThreads; i++)
tasksRepeatArray[i] = taskPerThread;
}
else if (numberOfThreads > numberOfTasks)
{
for (int i = 0; i < numberOfTasks; i++)
{
tasksRepeatArray[i]++;
}
}
else
{
for (int i = 0; i < tasksRepeatArray.Length; i++)
tasksRepeatArray[i] = taskPerThread;
for (int i = 0; i < diff; i++)
tasksRepeatArray[i]++;
}
return tasksRepeatArray;
}