Удалось сделать в userspace переключение задач за 44 такта (процессор Pentium IV Celeron). Это уже соизмеримо со временем простого выхова процедуры. Могло бы быть и меньше (порядка 11 тактов), если бы конвейер процессора не сбрасывался. Может, кому-нибудь будет полезно.
Вот структуры данных (язык - Free Pascal):
unit threads;
interface
const
//размер стека в 32-х битных словах
thr_st=1020;
type
//контекст задачи
thr_task=record
//место для стека
st:array[1..thr_st] of pointer;
//параметр
param:pointer;
//сохранённый указатель стека
sp:pointer;
//поддержка списка задач
next,
pred:^thr_task;
end;
var
//выполняющаяся в данный момент задача
thr_cur:^thr_task;
Вот само переключение:
procedure helper;assembler;
asm
mov esp,[eax+4084] //загружаем сохранённый указатель стека
sub esp,8 //корректируем его
end; //возврат произойдёт в новом стеке
procedure schedule;assembler;
asm
mov eax,thr_cur //загружаем указатель на контекст текущей задачи
mov [eax+4084],esp //сохраняем указатель стека текущей задачи. Потом в стек будет помещено содержимое регистра EBP и адрес возврата -> потом корректировка ESP на 8
mov eax,[eax+4088] //загружаем указатель на следующую активную задачу
mov thr_cur,eax //следующая выбранная задача становится текущей
push ebp //сохраняем указатель кадра стека
call helper //выполняем собственно переключение задач
pop ebp //восстанавливаем указатель кадра стека. Когда другая задача передаст управление этой задаче, она передаст его сюда.
end;