Is GH_TaskCapableComponent multicore?

Hello

I have a doubt about GH_TaskCapableComponent,
I thought that this class allowed to perform different tasks on all processors.
But I have the impression that this allows to run multiple tasks in parallel on the same Thread. The main STA thread of the Grasshopper window.

Even when testing with components built into GH, such as “Shatter”, the total CPUs consumption rarely reaches 25%. Like

image

So, is there something wrong with my system, or the GH_TaskCapableComponent is a single core parallelism (or GH thread)?

jmv

They do run tasks on all of the cores, but the tasks are broken down to how many times SolveInstance is called for a given component. I would need a sample of what you are showing to be able to give you any more details of what is happening in this specific case.

Hello Steve,

The GH definition of the image does not run on the 6 cores of my processor and remains at 20/25% of use: multi-thread.gh (4.4 KB)

Then I’m asking this question because I’m trying to implement the IGH_TaskCapable interface.

It is quite difficult to extract simple and working code from my sources, but, below, a simplified version of the content of my SolveInstance:

if (!UseTasks)
{
     if (SendData(m_instance, DA))
     {
          // m_instance.EntryPoint is a delegate ("Task <object> EntryPoint ()")
          // that points to a function of an external assembly
          var task = m_instance.EntryPoint();
          task.Wait();
          if (task.Exception == null)
               ReceiveData(m_instance, DA);
     }
     else
     {
          DA.AbortComponentSolution();
     }
     return;
}
else if (InPreSolve)
{
     ScriptInstance inst;
     if (m_task_count < m_instances.Count)
     {
          inst = m_instances[m_task_count];
     }
     else
     {
          m_instances.Add(inst = new ScriptInstance());
          // "inst.Wrap" is a utility for attaching the external assembly to "inst"
          inst.Wrap(m_instance.Assembly);
     }

     if (SendData(inst, DA))
          // inst.EntryPoint is a delegate ("Task <object> EntryPoint ()")
          // that points to a function of an external assembly
          m_tasks.Add(inst.EntryPoint());
     else
          DA.AbortComponentSolution();
     return;
}
else if (DA.Iteration < m_tasks.Count)
{
     var task = m_tasks[DA.Iteration];
     if (task.Exception == null)
          ReceiveData(m_instances[DA.Iteration], DA);
}

And the external assembly contains only one class.

public class Program
{
    int steps;
    int result;

    // "inst.EntryPoint" and "m_instance.EntryPoint" point to this method.
    // Here, I do not have the choice of the signature of the method.
    // Because it's the Rolyn compiler that generates this.
     public async Task<object> <Initialize>()
     {
          if (steps < 0)
               return "Steps must be >= 0.";
          if (steps > 46)
               return $"Steps must be <= {46}.";
          
          result = ComputeFibonacci(steps);
          return null;
     }
     public int ComputeFibonacci(int n)
     {
          return n <= 1 ? n : ComputeFibonacci(n - 1) + ComputeFibonacci(n - 2);
     }
}

The SolveInstance is called several times, but it doesn’t use all processors, like the GH “Shatter” component…
jmv

If the “Shutter” component is not using all cores, that is not the cause of my system. I wrote a test in a script component and all resources are in use:

  private void RunScript(List<int> steps, ref object result)
  {
    const int max_steps = 40;
    var fibonaccis = new Fibonacci[steps.Count];
    var counter = new CountdownEvent(steps.Count);

    for (int i = 0; i < steps.Count; i++)
    {
      var n = steps[i];
      if (n < 0) throw new Exception ("Steps must be >= 0.");
      if (n > max_steps) throw new Exception ("Steps must be <= 40.");

      var f = fibonaccis[i] = new Fibonacci ();
      var state = new State { Steps = n, Counter = counter };
      ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, state);
    }

    counter.Wait();
    result = (from f in fibonaccis select f.Result).ToList();
  }

  // <Custom additional code> 
  class State
  {
    public int Steps;
    public CountdownEvent Counter;
  }
  class Fibonacci
  {
    public int Result;

    public void ThreadPoolCallback(Object state)
    {
      var tuple = (State) state;
      Result = Calculate(tuple.Steps);
      tuple.Counter.Signal();
    }

    public int Calculate(int n)
    {
      if (n <= 1)
        return n;
      return Calculate(n - 1) + Calculate(n - 2);
    }
  }
  // </Custom additional code> 

I’m seeing 100% usage on my computer with your definition. All I’m doing is spinning up a bunch of Tasks and letting the .NET runtime manage how they are threaded.

Hello Steve,

I have tried several Grasshopper multithreaded components.
they work correctly, except “Shutter”. I used ILSPy to reimplement the “Shutter” component with ThreadPool (which seems to work better on my system). The result is the same.
I disabled any external services that could have blocked CPU usage, such as Hyper-V or Docker VMs. It didn’t change anything for the “Shutter” component.
I dropped. I would stay here again if I ever understand why “Shutter” is not working on my system.

thank you, jmv