Ada minimal task synchronization (performance related)

For a hobby interpreter project I am looking for advice on a performance-related question regarding task synchronization. A scheduler must map new green tasks to real tasks, each of which holds its own linked list of green tasks.

The problem: How do I synchronize the addition of a green task to a running real task with the least overhead possible? In earlier tests I have found out that making the linked list a protected object slows down access to it from within its own real task tremendously. To give you an idea of how tight the interpreter loop is currently in the single-task version:

while not Is_Empty (Global_State.GTasks) loop
   Current_Task := Next (Global_State.GTasks);
   Global_State.Pram (Current_Task.PC).all (Global_State, Current_Task);
   Current_Task.PC := Current_Task.PC + 1;
   Update (Global_State.GTasks, Current_Task);
end loop;

(Update still copies but I can later get rid of that.)

My tests have indicated that even minimal changes to this loop can slow down interpretation tremendously. Imagine that instead of in Global_State, GTasks resides as a local variable in the task executing this loop. I only need to synchronize access to GTasks when the scheduler adds a new GTask from the outside the task which runs this loop.

What would you recommend in this situation?

Upvotes: 1

Views: 228

Answers (1)

Marc C
Marc C

Reputation: 8522

(The following is contingent on my having a reasonable understanding of the question.)

If one can go forward with the "GTasks resides as a local variable in the the task" conjecture, then perhaps incorporating a conditional accept may work--I have no idea what the performance hit would be, it's just an idea.

while not Is_Empty (GTasks) loop
   Current_Task := Next (GTasks);
   Global_State.Pram (Current_Task.PC).all (Global_State, Current_Task);
   Current_Task.PC := Current_Task.PC + 1;
   Update (GTasks, Current_Task);

   select
      accept Accept_New_Task(Green_Task : Green_Task_Type) do
         Append(GTasks, Green_Task);
      end Accept_New_Task;
   else
      null;
   end select;
end loop;

Or perhaps only periodically check for new tasks, say, every so-many PC increments? (Again, no promises on the performance hit.)

while not Is_Empty (GTasks) loop
   Current_Task := Next (GTasks);
   Global_State.Pram (Current_Task.PC).all (Global_State, Current_Task);
   Current_Task.PC := Current_Task.PC + 1;
   Update (GTasks, Current_Task);

   if PC_Increments = Check_For_New_Tasks then
      select
         accept Accept_New_Task(Green_Task : Green_Task_Type) do
            Append(GTasks, Green_Task);
         end Accept_New_Task;
      else
         null;
      end select;
      PC_Increments := 0;
   else
      PC_Increments := PC_Increments + 1;
   end if;
end loop;

Upvotes: 2

Related Questions