LINUX.ORG.RU

Продолжение: кооперативная многозадачность


0

0

  Удалось сделать в 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;
Ответ на: комментарий от anonymous

>а если на Си переписать может получиться быстрее?

Быстрее не получится (итак критическая по времени часть на асме), но зато можно будет использовать с программами на Си.

ivan_gur
() автор топика

а контекст процесса мы где сохранять будем? А? Вобщем история повторяется Just for Fun ;)

anonymous
()
Ответ на: комментарий от anonymous

>а контекст процесса мы где сохранять будем? А? Вобщем история повторяется Just for Fun ;)

Вы имеете в виду обработчики сигналов и всё такое? Нет, их я не сохраняю (тем более, что пока проверял только под ДОСом).

Я сохраняю только указатель инструкции, указатель стека и указатель кадра стека.

ivan_gur
() автор топика
Ответ на: комментарий от vasirck

>Он имеет ввиду ВСЕ регистры процессора.

Нет проблем: если очень надо, их можно затолкнуть в стек. Просто в большинстве случаев хватает сохранить EIP, ESP и EBP.

ivan_gur
() автор топика
Ответ на: комментарий от idle

>Может быть, вам будет интересно: http://www.goron.de/~froese/coro/ маленькая и простая реализация coroutine на C.

По-моему, многопоточность в моей библиотеке - более общий случай. На её основе легко реализуется аналогичная coroutine функциональность.

ivan_gur
() автор топика
Ответ на: комментарий от ivan_gur

> если очень надо, их можно затолкнуть в стек.
Надо затолкнуть. Возможно их паскаль сохраняет при вызове asm функции.
Надо дизасемблировать и посмотреть вызов.

> Просто в большинстве случаев хватает сохранить EIP, ESP и EBP.
Я думаю, что надо чтобы работало во всех случаях.

vasirck
()
Ответ на: комментарий от vasirck

>Надо затолкнуть. Возможно их паскаль сохраняет при вызове asm функции.

Нет, не сохраняет, но ничего страшного в этом нет.
Основная программа
program tst;
uses threads;
var
        i:longint;
procedure task;
begin
        while true do begin
                schedule;
        end;
end;
begin
        thr_new(@task,nil);
        for i:=1 to 100000000 do
                schedule;
end.

 Превратилась в:
program_init:
	pushl	%ebp
	movl	%esp,%ebp
	call	FPC_INITIALIZEUNITS
	pushl	$0
	leal	_TASK,%eax
	pushl	%eax
	call	_THREADS$$_THR_NEW$POINTER$POINTER
	movl	$1,_I
	.balign 4,144
.L14:
	call	_THREADS$$_SCHEDULE
	cmpl	$100000000,_I
	jge	.L13
	incl	_I
	jmp	.L14
.L13:
	call	FPC_DO_EXIT
	leave
	ret

  В библиотеке:
_THREADS$$_HELPER:
	movl	4084(%eax),%esp
	subl	$8,%esp
	ret
	.balign 16
.globl	_THREADS$$_SCHEDULE
_THREADS$$_SCHEDULE:
	movl	U_THREADS_THR_CUR,%eax
	movl	%esp,4084(%eax)
	movl	4088(%eax),%eax
	movl	%eax,U_THREADS_THR_CUR
	pushl	%ebp
	call	_THREADS$$_HELPER
	popl	%ebp
	ret
  Всё точь-в-точь как я писал.

>Я думаю, что надо чтобы работало во всех случаях.

  Не вижу большой необходимости, так как при кооперативной многозадачности места переключения на другую задачу определяет сам программист.

  Если очень нужно, то я сделал вариант, который сохраняет все РОН: http://www.gursky.nm.ru/threads2.txt

  Он переключает задачи за 70 тактов.

ivan_gur
() автор топика
Ответ на: комментарий от ivan_gur

> Не вижу большой необходимости, так как при кооперативной многозадачности места переключения на другую задачу определяет сам программист. Места переключения определяет программист, но чаще всего он не знает, какие регистры сохраняются, а какие нет и не имеет возможности их сохранить, т.к. пишет не на ассемблере. Напиши тест посложнее: сделай побольше локальных переменных и добавь вызовов функций в циклы.

vasirck
()
Ответ на: комментарий от ivan_gur

> Не вижу большой необходимости, так как при кооперативной > многозадачности места переключения на другую задачу определяет сам > программист.

Места переключения определяет программист, но чаще всего он не знает, какие регистры сохраняются, а какие нет и не имеет возможности их сохранить, т.к. пишет не на ассемблере. Напиши тест посложнее: сделай побольше локальных переменных и добавь вызовов функций в циклы.

vasirck
()
Ответ на: комментарий от vasirck

>Напиши тест посложнее: сделай побольше локальных переменных и добавь вызовов функций в циклы.

Писал демонстрационную программу:

http://www.gursky.nm.ru/usecon.txt

http://www.gursky.nm.ru/conman.txt

Всё работает, даже если поставить все возможные оптимизации!

ivan_gur
() автор топика
Ответ на: комментарий от ivan_gur

Добавь проверку всех результатов вычислений. Например до переключения складывай, а после вычитай и сравнивай, если не сходится выдавай ошибку. В оба цикла.

vasirck
()
Ответ на: комментарий от ivan_gur

Так ты проверил?
Не знаю как паскаль, а C компилятор от iar для msp430 оставлял
свободными только 4 из 16-ти регистров, остальные приходилось
сохранять.

vasirck
()
Ответ на: комментарий от vasirck

>C компилятор от iar для msp430 оставлял свободными только 4 из 16-ти регистров, остальные приходилось сохранять.

Значит, в нём надо сохранять. Если у этого процессора короткий конвейер и есть специальная команда для сохранения регистров, то переключение будет всё равно очень быстрым.

Но я для начала сделал быструю рабочую версию для FPC на x86, а уже потом по мере надобности буду делать другие.

ivan_gur
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.