HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Low-lock Multi-threading Implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
multilowthreadingimplementationlock

Problem

I'm designing in my spare time a game engine (for fun, not so much for profit, haha). I wanted to design the 'core pipeline' as efficiently as possible. Having a quad-core CPU, I decided to take advantage of parallel processing.

I wanted to implement a lock-free algorithm (or at least, low-locking) to make the pipeline as quick as possible (and to avoid expensive things like lock contention, kernel-mode locking, and context switching as much as possible).

Without further preamble, here is my implementation (slightly shortened):

EnginePipeline.cs

```
public static partial class EnginePipeline {
private static EngineComponent[] componentPipeline;
private static volatile bool isRunning = false;
private static volatile bool exitFlag = false;

private static Thread[] threadPool;
private static EngineComponent currentComponent;
private static WorkloadSet currentWorkloads = new WorkloadSet(100);

private static void InitThreadPool() {
int numLogicalCores = Environment.ProcessorCount;
int poolSize = numLogicalCores;
if (PipelineConfig.MaxThreads > 0 && numLogicalCores > PipelineConfig.MaxThreads) poolSize = PipelineConfig.MaxThreads;

// One less than the number of cores because the master thread will be used too
poolSize -= 1;
if (poolSize
/// Run the pipeline. This method will block indefinitely until something calls
/// or .
///
public static void Run() {
isRunning = true;

SpinWait completionWaiter = new SpinWait();
PipelineWorkload workload = new PipelineWorkload();

while (!exitFlag) {
for (int i = 0; i < componentPipeline.Length; ++i) {
// Set current component
Volatile.Write(ref currentComponent, componentPipeline[i]);

// Calculate the workloads
int range = currentComponent.GetRange();
int blockSize = currentComponent.actualBlockSize;

Solution

you have a little bit of rewriting that can be done on this chunk of code

private static void InitThreadPool() {
    int numLogicalCores = Environment.ProcessorCount;
    int poolSize = numLogicalCores;
    if (PipelineConfig.MaxThreads > 0 && numLogicalCores > PipelineConfig.MaxThreads) poolSize = PipelineConfig.MaxThreads;

    // One less than the number of cores because the master thread will be used too
    poolSize -= 1;
    if (poolSize < 0) poolSize = 0;

    threadPool = new Thread[poolSize];
    for (int i = 0; i < poolSize; i++) {
        threadPool[i] = new Thread(ThreadWaitForWork) {
            Name = "OphSlave-" + i,
            IsBackground = true
        };
        threadPool[i].Start();
    }

    Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
    Thread.CurrentThread.Name = "OphMaster";
}


I want to change a Variable name, and I am sure that there are more than should be changed as well. but I have only singled out this method so far.

so this

private static Thread[] threadPool;


should be this

private static Thread[] threads;


and then instead of counting the logical cores/processors and changing the poolSize variable so many times, just use a ternary statement to assign the value you want to the poolSize variable like this

// One less than the number of cores because the master thread will be used too
int poolSize = (PipelineConfig.MaxThreads > 0 && Environment.ProcessorCount > PipelineConfig.MaxThreads) ? PipelinConfig.MaxThreads - 1 : Environment.ProcessorCount - 1;
if (poolSize < 0) poolSize = 0;


And then following the logic of that ternary statement MaxThreads is greater than 0 meaning 1 or more and ProcessorCount has to be greater than MaxThreads, so you could get rid of this:

if (poolSize < 0) poolSize = 0;


but, I think there will be issues. What if PipelineConfig.MaxThreads is the same as Environment.ProcessorCount ?

Code Snippets

private static void InitThreadPool() {
    int numLogicalCores = Environment.ProcessorCount;
    int poolSize = numLogicalCores;
    if (PipelineConfig.MaxThreads > 0 && numLogicalCores > PipelineConfig.MaxThreads) poolSize = PipelineConfig.MaxThreads;

    // One less than the number of cores because the master thread will be used too
    poolSize -= 1;
    if (poolSize < 0) poolSize = 0;

    threadPool = new Thread[poolSize];
    for (int i = 0; i < poolSize; i++) {
        threadPool[i] = new Thread(ThreadWaitForWork) {
            Name = "OphSlave-" + i,
            IsBackground = true
        };
        threadPool[i].Start();
    }

    Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
    Thread.CurrentThread.Name = "OphMaster";
}
private static Thread[] threadPool;
private static Thread[] threads;
// One less than the number of cores because the master thread will be used too
int poolSize = (PipelineConfig.MaxThreads > 0 && Environment.ProcessorCount > PipelineConfig.MaxThreads) ? PipelinConfig.MaxThreads - 1 : Environment.ProcessorCount - 1;
if (poolSize < 0) poolSize = 0;
if (poolSize < 0) poolSize = 0;

Context

StackExchange Code Review Q#38604, answer score: 3

Revisions (0)

No revisions yet.