cmd
cmd

Reputation: 525

Split operations between multiple threads in C#

I'm writing benchmark application which has Number of threads and number of operations to perform as input parameters. Each benchmark is created as separate class which has Execute(int numberOfRepeats) method. numberOfRepeats is actually number of repeats in each thread.

I create benchmarks the following way:

For example I have 32 threads, and 50 long-lasting benchmark operations. So each thread must execute 50/32 = 1 operation (1.56 actually), which will give total number of 32 operations for all threads.

I use simple "new Thread()" construction for multithreading and AutoResetEvent with WaitHandle.WaitAll construction to synchronize execution and measure total time.

I tried Parallel.For with ParallelOptions.MaxDegreeOfParallelism as number of threads, but it doesn't actually run benchmark with all threads. With number of operations 100k only 20 threads were used from threadpool with ParallelOptions.MaxDegreeOfParallelism=128.

And now the question. How can i split operations between threads to execute exact number of operations in situation i described?

Thanks!

Upvotes: 0

Views: 1789

Answers (3)

user7791584
user7791584

Reputation:

This is a version of cmd's function that will reduce the amount of threads if the actions exceed the number of threads including comments. I take no credit for this. Couldn't comment because my reputation isn't high enough.

internal static IEnumerable<int> GetActionsPerThreads(int numberOfThreads, int numberOfActions)
    {
        // We have too many threads for the requested amount of actions
        while (numberOfActions < numberOfThreads)
        {
            numberOfThreads--;
        }

        int[] actionsPerThread = new int[numberOfThreads];
        // Intentional loss of data
        int perThread = numberOfActions / numberOfThreads;
        int actionRemainder = numberOfActions - (numberOfThreads * perThread);

        // No need to split anything. The threads can perform an equal amount of actions
        if (actionRemainder == 0)
        {
            for (int i = 0; i < numberOfThreads; i++)
            {
                actionsPerThread[i] = perThread;
            }
        }
        // We have more actions than we have threads. Time to reduce our thread count to the amount of actions
        else if (numberOfThreads > numberOfActions)
        {
            for (int i = 0; i < numberOfActions; i++)
            {
                actionsPerThread[i]++;
            }
        }
        // We have an unequal amount of actions per thread, time to split them
        else
        {
            // All threads perform the calculated amount of actions (rounded down)
            for (int i = 0; i < actionsPerThread.Length; i++)
            {
                actionsPerThread[i] = perThread;
            }

            // Some tasks will have to do more work
            for (int i = 0; i < actionRemainder; i++)
            {
                actionsPerThread[i]++;
            }
        }
        return actionsPerThread;
    }

Upvotes: 0

cmd
cmd

Reputation: 525

I found easy way to split any number of operations between threads. This method will return array of int's, where each element means number of operations for thread with index 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;
    }

Upvotes: 0

Scorpion
Scorpion

Reputation: 793

The Parallel scheduler doesn't use so that many threads because it's clever enough to know when doing so would degrade performance.

From MSDN:

The .NET thread pool adapts dynamically to changing workloads by allowing the number of worker threads for parallel tasks to change over time. At run time, the system observes whether increasing the number of threads improves or degrades overall throughput and adjusts the number of worker threads accordingly.

If you're using so many threads to perform a benchmark, you should rethink your implementation. You're going to degrade your overall performance since threads will be fighting with each for cycles, and this is the last thing you want when you're trying to do timing sensitive work like benchmarking.

Upvotes: 1

Related Questions