Планировщик задач HaikuOS. Часть 2. Переделываем.

Планировщик задач HaikuOS. Часть 2. Переделываем.




Содержание:
- Место, где зарыта собака.
- Наполеоновские планы.
- Гадание на кофейной гуще.
----NewOS
----Haiku
----BeOS
----Zeta
- Остапа понесло.
- Расхлёбываем.
- SOS Emulator к вашим услугам.


Место, где зарыта собака.

Вот как планировщик Haiku выбирает очередной поток, которому отдать квант (сужу по завалявшемуся у меня куску исходника планировщика NewOS дремучего 2003 года).

Есть отсортированная по приоритетам очередь потоков жаждущих процессорного времени. В одном конце очереди 1 или более IDLE-потоков с приоритетами 0 (таких потоков в системе всегда есть как минимум столько же, сколько доступных процессоров, и хотя бы один из этих потоков гарантированно сидит в очереди в тот момент когда планировщик по ней шарит). В другом конце - поток с самым высоким приоритетом.

Итак двигаемся по упорядоченной очереди потоков. От бОльших приоритетов к меньшим, до тех пор пока выполняется некий критерий. Как только критерий выполняться перестал останавливаемся, последний рассмотренный поток и будет тем счастливчиком, которому достанется квант процессорного времени.

Собственно критерий:

// always extract real time threads
if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
	break;

// never skip last non-idle normal thread
if(nextThread->queue_next && (nextThread->queue_next->priority == B_IDLE_PRIORITY))
	break;

// skip normal threads sometimes
if (_rand() > 0x3000) break;


где:

static int _rand(void)
{
	static int next = 0;
	if (next == 0) next = system_time();
	next = next * 1103515245 + 12345;
	return((next >> 16) & 0x7FFF);
}


Гусары молчать! :) Оставаясь серьёзными разберём код.

Потокам реального времени всегда у нас дорога. Это понятно. Остановить пробег если далее только IDLE-потоки - тоже понятно. А последнее "правило-лотеря" конечно на первый взгляд вызывает недоумение, но не мешает системе делить процессорное время поровну между потоками одинакового приоритета. Всё дело в структуре очереди.

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

Очередь как бы состоит из под-очередей потоков одинакового приоритета и эти под-очереди самоперемешиваются. Вот код вставки в очередь:

void scheduler_enqueue_in_run_queue(struct thread *thread)
{
	struct thread *curr, *prev;

	for(curr = gRunQueue.head, prev = NULL; 
		curr && (curr->priority >= thread->priority); 
		curr = curr->queue_next) 
	{
		if (prev)
			prev = prev->queue_next;
		else
			prev = gRunQueue.head;
	}

	thread->queue_next = curr;

	if(prev)
		prev->queue_next = thread;
	else
		gRunQueue.head = thread;
}


Нигде не видно ничего что как-то бы опиралось на абсолютное значение номера приоритета. Не важно какие там разрывы по приоритетам между потоками в очереди, идут они с промежутком в 10 или в 1, результат выборки будет одинаковым.

Почему я так уверенно гребу NewOS и Haiku в одну кучу? Да потому что запуская SOS Tester'а на Haiku имею на выходе картинку замечательно согласующуюся с данным кодом из NewOS, т.е. разработчики Haiku ничего радикально там не меняли, хотя кое-что всё-таки изменили чтобы свести ситуацию к ещё более некрасивой.


Наполеоновские планы.

В голову стали приходить всякие идеи улучшения планировщика, например:
1) Давайте не тупо выбирать по всей очереди, а так чтобы в "кольцах" (так я обозвал под-очереди, в данном случае аналогия верная) рассматривать только по одному кандидату, если он не подошёл то прыгаем сразу на следующее кольцо.
2) На границах колец сравним приоритеты потоков и вероятность того состоится ли прыжок в следующее кольцо будет зависеть от ширины пропасти между ними.
3) Потвикаем условия ширины пропасти так чтобы прыжки в определённых диапазонах были равновероятностными. Не знаю правда насколько это будет хорошо, но можно побаловаться...

И много других идей. Правда для их проверки опять потребовался какой-то инструмент. У меня снова зачесались руки и вышла из под них примитивнейшая программка SOS Emulator. О её назначении думаю все уже догадались по названию :)

Программа до безобразия простая и немного по-своему делает всё тоже самое что SOS Tester.

Вместо пачки рабочих потоков фактически порождается лишь один поток нового класса - поток эмулятора планировщика.

В этом потоке в бесконечном цикле последовательно:
- запускается функция scheduler_reschedule(), которая выбирает из очереди фиктивных рабочих потоков (особого вида структур данных) один и помечает его текущим
- увеличивается на 1 счётчик текущего фиктивного потока (поток как бы отрабатывает свой квант процессорного времени)

А главный поток всё также проснувшись однажды по таймеру прекращает работу эмулятора планировщика и распечатывает статистику.

Исполняемый потоком эмулятора планировщика код - местами закомментированный и чуточку модифицированный для удобства внесения дальнейших изменений код планировщика задач NewOS.


Гадание на кофейной гуще.

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

1) NewOS без модификаций, используем его как базу:

// без изменений
void scheduler_enqueue_in_run_queue(struct thread *thread)
{
	struct thread *curr, *prev;

	for(curr = gRunQueue.head, prev = NULL; 
		curr && (curr->priority >= thread->priority); 
		curr = curr->queue_next) 
	{
		if (prev)
			prev = prev->queue_next;
		else
			prev = gRunQueue.head;
	}

	thread->queue_next = curr;

	if(prev)
		prev->queue_next = thread;
	else
		gRunQueue.head = thread;
}

// этой функции в оригинале нет, я её сделал разбив более крупную конструкцию
// на несколько функций, остальной код одинаков для всех случаев
struct thread* select_next_thread_to_run()
{
	struct thread *nextThread;

	nextThread = gRunQueue.head;

	while (nextThread && (nextThread->priority > B_IDLE_PRIORITY)) 
	{
		// always extract real time threads
		if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY)
			break;

		// never skip last non-idle normal thread
		if(nextThread->queue_next 
			&& (nextThread->queue_next->priority == B_IDLE_PRIORITY))
			break;

		// skip normal threads sometimes
		if (_rand() > 0x3000) break;

		nextThread = nextThread->queue_next;
	}

	return nextThread;
}

// без изменений
static int _rand(void)
{
	static int next = 0;
	if (next == 0) next = system_time();
	next = next * 1103515245 + 12345;
	return((next >> 16) & 0x7FFF);
}


Settings: work time 1000000 microseconds, 20 working threads.
Using NewOS scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                0     0.000000
       1        33                0     0.000000
       2        37                0     0.000000
       3        40                0     0.000000
       4        44                2     0.000047
       5        48                5     0.000117
       6        51                6     0.000140
       7        55               19     0.000444
       8        59               65     0.001518
       9        62              137     0.003200
      10        66              361     0.008432
      11        69              999     0.023335
      12        73             2850     0.066570
      13        77             7466     0.174390
      14        80            19795     0.462370
      15        84            52923     1.236170
      16        88           141490     3.304909
      17        91           375453     8.769793
      18        95          1003684    23.443949
      19        99          2675952    62.504616


Settings: work time 1000000 microseconds, 20 working threads.
Using NewOS scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        80                0     0.000000
       1        81                0     0.000000
       2        82                0     0.000000
       3        83                0     0.000000
       4        84                1     0.000023
       5        85                5     0.000117
       6        86                8     0.000187
       7        87               21     0.000490
       8        88               51     0.001190
       9        89              133     0.003102
      10        90              432     0.010076
      11        91             1027     0.023953
      12        92             2812     0.065586
      13        93             7503     0.174996
      14        94            19735     0.460290
      15        95            52956     1.235121
      16        96           141705     3.305061
      17        97           376948     8.791759
      18        98          1006840    23.483067
      19        99          2677338    62.444983



2) А-ля Haiku:

// skip normal threads sometimes
if (_rand() > 0x1999) break;


Вообще-то я не знаю точной константы которая в Haiku а эту подобрал прогоняя SOS Tester и SOS Emulator подставляя разные значения и ища закономерность. Но то что вы тут видете очень сильно похоже на реальное положение дел на моей машине в последних альфа-сборках Haiku.

Settings: work time 1000000 microseconds, 20 working threads.
Using Haiku scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                0     0.000000
       1        33                0     0.000000
       2        37                0     0.000000
       3        40                0     0.000000
       4        44                0     0.000000
       5        48                0     0.000000
       6        51                0     0.000000
       7        55                0     0.000000
       8        59                0     0.000000
       9        62                0     0.000000
      10        66                1     0.000020
      11        69               12     0.000240
      12        73               53     0.001062
      13        77              270     0.005409
      14        80             1215     0.024342
      15        84             6442     0.129065
      16        88            31624     0.633582
      17        91           159905     3.203674
      18        95           799013    16.008114
      19        99          3992765    79.994490



Лично мне картинка для NewOS нравилась больше.


3) По канонам Be Newsletter и Be Book, т.е.

- каждый следующий приоритет в 2 раза больше шансов на исполнение:

struct thread* select_next_thread_to_run()
{
	struct thread *nextThread, *nextLower;
	uint32 n;

	nextThread = gRunQueue.head;
	
	// always extract real time threads
	if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY) {
		return nextThread;
	}
	
	if (nextThread->priority == B_IDLE_PRIORITY) {
		return nextThread;
	}
	while (1) {
		// find thread with next lower priority
		nextLower = nextThread->queue_next;
		while (nextLower->priority >= nextThread->priority)
			nextLower = nextLower->queue_next;

		// never skip last non-idle normal subqueue of threads
		if (nextLower->priority == B_IDLE_PRIORITY)
			break;
		
		// skip subqueue of normal threads sometimes
		n = (uint32)(nextThread->priority - nextLower->priority);
		if(rand()%(int)(pow(2,n))) break;

		nextThread = nextLower;
	}
	return nextThread;
}


Settings: work time 1000000 microseconds, 20 working threads.
Using BeOS scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                0     0.000000
       1        33                0     0.000000
       2        37                0     0.000000
       3        40                0     0.000000
       4        44                0     0.000000
       5        48                0     0.000000
       6        51                0     0.000000
       7        55                0     0.000000
       8        59                0     0.000000
       9        62                0     0.000000
      10        66                0     0.000000
      11        69                0     0.000000
      12        73                0     0.000000
      13        77                1     0.000036
      14        80                8     0.000291
      15        84               72     0.002619
      16        88             1256     0.045680
      17        91             9424     0.342744
      18        95           160241     5.827842
      19        99          2578575    93.780789


Settings: work time 1000000 microseconds, 20 working threads.
Using BeOS scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        80                2     0.000111
       1        81                3     0.000166
       2        82               10     0.000553
       3        83               10     0.000553
       4        84               33     0.001826
       5        85               52     0.002877
       6        86               96     0.005312
       7        87              234     0.012947
       8        88              482     0.026669
       9        89              903     0.049963
      10        90             1768     0.097823
      11        91             3503     0.193820
      12        92             6970     0.385647
      13        93            14157     0.783302
      14        94            28208     1.560738
      15        95            56373     3.119097
      16        96           113241     6.265582
      17        97           225863    12.496915
      18        98           451578    24.985642
      19        99           903864    50.010457



4) Или такая модификация (3) варианта если надо чтобы правило было без привязки к абсолютным значениям приоритетов:

// skip subqueue of normal threads sometimes
if(rand()%2) break;


Settings: work time 1000000 microseconds, 20 working threads.
Using BeOS2 scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                4     0.000127
       1        33                6     0.000191
       2        37               17     0.000540
       3        40               25     0.000794
       4        44               59     0.001873
       5        48               97     0.003080
       6        51              177     0.005620
       7        55              410     0.013018
       8        59              841     0.026703
       9        62             1585     0.050326
      10        66             3069     0.097444
      11        69             6194     0.196667
      12        73            12280     0.389905
      13        77            24661     0.783016
      14        80            49173     1.561302
      15        84            98059     3.113491
      16        88           197068     6.257146
      17        91           393759    12.502322
      18        95           786623    24.976226
      19        99          1575380    50.020210



5) А-ля Zeta 1.51, где

- каждые седующие ДВА приоритета имеют в 2 раза больше шансов на исполнение (т.е. фактически подочереди "склеиваются" парами) и жёсткая привязка к номерам приоритетов:

void scheduler_enqueue_in_run_queue(struct thread *thread)
{
	struct thread *curr, *prev;

	for(curr = gRunQueue.head, prev = NULL; 
		curr && (curr->priority >= thread->priority - thread->priority%2); 
		curr = curr->queue_next) 
	{
		if (prev)
			prev = prev->queue_next;
		else
			prev = gRunQueue.head;
	}

	thread->queue_next = curr;

	if(prev)
		prev->queue_next = thread;
	else
		gRunQueue.head = thread;
}

struct thread* select_next_thread_to_run()
{
	struct thread *nextThread, *nextLower;
	uint32 n;

	nextThread = gRunQueue.head;
	
	// always extract real time threads
	if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY) {
		return nextThread;
	}
	
	// in case we don't have any non-idle threads to run
	if (nextThread->priority == B_IDLE_PRIORITY) {
		return nextThread;
	}
	while (1) {
		// find thread with next lower priority
		nextLower = nextThread->queue_next;
		while (nextLower->priority >=
			nextThread->priority - nextThread->priority%2)
			nextLower = nextLower->queue_next;

		// never skip last non-idle normal subqueue of threads
		if (nextLower->priority == B_IDLE_PRIORITY)
			break;
		
		// skip subqueue of normal threads sometimes
		n = (uint32)(nextThread->priority - nextLower->priority);
		if(rand()%n) break;

		nextThread = nextLower;
	}
	return nextThread;
}


Settings: work time 1000000 microseconds, 20 working threads.
Using Zeta scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                0     0.000000
       1        33                0     0.000000
       2        37                0     0.000000
       3        40                0     0.000000
       4        44                0     0.000000
       5        48                0     0.000000
       6        51                0     0.000000
       7        55                2     0.000060
       8        59                2     0.000060
       9        62                6     0.000179
      10        66               18     0.000537
      11        69               60     0.001792
      12        73              273     0.008152
      13        77             1091     0.032577
      14        80             2853     0.085191
      15        84            13076     0.390452
      16        88            52442     1.565929
      17        91           139728     4.172308
      18        95           626695    18.713246
      19        99          2512692    75.029517


Settings: work time 1000000 microseconds, 20 working threads.
Using Zeta scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        80             4288     0.188195
       1        81             4287     0.188151
       2        82             2997     0.131535
       3        83             2996     0.131491
       4        84             6612     0.290193
       5        85             6611     0.290149
       6        86            12131     0.532415
       7        87            12131     0.532415
       8        88            22660     0.994520
       9        89            22660     0.994520
      10        90            42744     1.875983
      11        91            42744     1.875983
      12        92            80298     3.524184
      13        93            80298     3.524184
      14        94           150685     6.613386
      15        95           150685     6.613386
      16        96           283300    12.433700
      17        97           283300    12.433700
      18        98           533529    23.415954
      19        99           533529    23.415954



6) И аналогично если нам хочется чтобы в (5) правило не было абсолютно привязано к номерам:

// skip subqueue of normal threads sometimes
if(rand()%2) break;


Settings: work time 1000000 microseconds, 20 working threads.
Using Zeta2 scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        30                3     0.000100
       1        33                6     0.000200
       2        37               16     0.000532
       3        40               22     0.000732
       4        44               58     0.001929
       5        48               87     0.002894
       6        51              165     0.005488
       7        55              394     0.013106
       8        59              814     0.027076
       9        62             1512     0.050293
      10        66             2933     0.097560
      11        69             5926     0.197116
      12        73            11703     0.389275
      13        77            23565     0.783840
      14        80            46914     1.560494
      15        84            93709     3.117030
      16        88           188112     6.257145
      17        91           375602    12.493601
      18        95           751049    24.982046
      19        99          1503765    50.019542



Правда у вариантов (5) и (6) в моём исполнении есть баг - несовсем корректно отрабатываются случаи когда потоков всего 2-3 и их приоритеты соседние. Хех, а ведь у настоящей неэмулированной Zeta похожая проблема.
И ещё косяк - когда всего 1 не IDLE-поток и он разделяемого времени, то ему вообще не выдаются кванты. Есть подобная проблема в самой Zeta или нет - не знаю, но очень даже может быть.
Исправление этих проблемок оставляю читателям в качестве домашнего задания ;)


Остапа понесло.

Ну а самым удачным и в меру продвинутым планировщиком на мой взгляд является примерно такой:

/* Skip gaps -- Begin */

#define REAL_TIME_SKIP_GAP 20
#define MIN_REAL_TIME_SKIP_GAP 0
#define MAX_REAL_TIME_SKIP_GAP 20

#define SKIP_GAP 99
#define MIN_SKIP_GAP 0
#define MAX_SKIP_GAP 99

#define IS_IN_RANGE(i,min,max) (i >= min && i <= max)

typedef struct scheduler_skip_gap_info {
	uint32	real_time;
	uint32	normal;
} scheduler_skip_gap_info;

static scheduler_skip_gap_info gSkipGaps = { REAL_TIME_SKIP_GAP, SKIP_GAP };

/* Skip gaps -- End */

scheduler_skip_gap_info get_skip_gaps()
{
	return gSkipGaps;
}

status_t set_skip_gaps(scheduler_skip_gap_info skip_gaps)
{
	if (IS_IN_RANGE(skip_gaps.real_time, MIN_REAL_TIME_SKIP_GAP, MAX_REAL_TIME_SKIP_GAP) &&
		IS_IN_RANGE(skip_gaps.normal, MIN_SKIP_GAP, MAX_SKIP_GAP))
	{
		gSkipGaps = skip_gaps;
		return B_OK;
	}
	return B_ERROR;
}

struct thread* select_next_thread_to_run()
{
	struct thread *nextThread, *nextLower;
	uint32 n, n_candidate;

	nextThread = gRunQueue.head;

	// in case we don't have any non-idle threads to run
	if (nextThread->priority == B_IDLE_PRIORITY) {
		return nextThread;
	}

	// select only from real-time threads if any
	if (nextThread->priority >= B_FIRST_REAL_TIME_PRIORITY) {

		while (1) {
			// find thread with next lower priority
			nextLower = nextThread->queue_next;
			while (nextLower->priority >= nextThread->priority)
				nextLower = nextLower->queue_next;

	     	// never skip last and only real-time subqueue of threads
	     	if (nextLower->priority < B_FIRST_REAL_TIME_PRIORITY
	     		&& nextThread == gRunQueue.head)
				break;

			if (nextLower->priority < B_FIRST_REAL_TIME_PRIORITY) {
				n_candidate = (uint32)(nextThread->priority - nextLower->priority);
				if (n_candidate < n) n = n_candidate;
			} else {
				n = (uint32)(nextThread->priority - nextLower->priority);
			}

			// never skip too much at once
			if (n > gSkipGaps.real_time) break;

			// skip subqueue of real-time threads sometimes
			if (rand()%((int)pow(2,n))) break;

			// return to head if no more real-time subqueues of threads
			if (nextLower->priority < B_FIRST_REAL_TIME_PRIORITY) {
				nextThread = gRunQueue.head;
			} else {
				// othrewise continue
				nextThread = nextLower;
			}
		}
		return nextThread;
		
	}

	// select from normal threads
	while (1) {
		// find thread with next lower priority
		nextLower = nextThread->queue_next;
		while (nextLower->priority >= nextThread->priority)
			nextLower = nextLower->queue_next;

     	// never skip last and only non-idle normal subqueue of threads
     	if (nextLower->priority == B_IDLE_PRIORITY
     		&& nextThread == gRunQueue.head)
			break;

		if (nextLower->priority == B_IDLE_PRIORITY) {
			n_candidate = (uint32)(nextThread->priority - nextLower->priority);
			if (n_candidate < n) n = n_candidate;
		} else {
			n = (uint32)(nextThread->priority - nextLower->priority);
		}
		
		// never skip too much at once
		if (n > gSkipGaps.normal) break;

		// skip subqueue of normal threads sometimes
		if (rand()%((int)pow(2,n))) break;

		// return to head if no more non-idle normal subqueues of threads
		if (nextLower->priority == B_IDLE_PRIORITY) {
			nextThread = gRunQueue.head;
		} else {
			// othrewise continue
			nextThread = nextLower;
		}
	}
	return nextThread;
}


И вот какие у меня на то основания:

- Имитирует оригинала (BeOS) соблюдением на мой взгляд весьма разумного правила со степенями двойки.

- Расширяет этот принцип на потоки реального времени. Они по-прежнему вытесняют обычные потоки, но при этом становятся более учтивыми по отношению друг к другу и делятся процессором.

- Имеет настраиваемый пользователем режим "форсажа" приоритетных задач.

Для тех кто хочет дополнительно обезопасить высокоприоритетные задачи, включить "форсаж" в ущерб всему прочему, есть т.н. "ограничители прыжка". По-умолчанию их значения таковы что они ни на что не влияют, но уменьшая их можно регулировать степень игнорирования системой менее приоритетных задач когда есть что-то более приоритетное также в очереди на выполнение.

Как я это себе вижу - файл настроек и/или особое приложение-настройщик с двумя бегунками, которые продвинутый пользователь может двигать регулируя степень "форсажа" в той или иной зоне, разделяемого и реального времени, или кнопочка-чекбоксик которая переключает между некими наперёд заданными установками. Желательно иметь возможность переключения "на лету", но и необходимость перезагрузки не так сильно страшна.

"Слишком сложно, BeOS - ОС с философией Best Defaults!", - скажете вы и будете правы. Но позвольте, есть же приложение настройки виртуальной памяти. Да оно только для продвинутых, но оно есть и даже иногда требует перезагрузки для того чтобы изменения вступили в силу!

А если возможность регулировки планировщика окажется очень полезной, то почему бы и нет? По-умолчанию будет стоять что-то оптимальное для большинства случаев и многие пользователи так никогда возможно и не узнают о сей фиче.

Как пример настройки по-умолчанию поведение аналогичное планировщику BeOS/Zeta:
REAL_TIME_SKIP_GAP = 0
SKIP_GAP = 99


Проиллюстрирую:

Zeta 1.51. Данные SOS Tester'а:

Settings: work time 10000000 microseconds, 20 working threads.
Real work time 10000019 microseconds, 20 working threads.
Inaccuracy if SMP and more than one CPU enabled: 0.000460%.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        90                0     0.000000
       1        91                0     0.000000
       2        92                0     0.000000
       3        93                0     0.000000
       4        94                0     0.000000
       5        95                0     0.000000
       6        96                0     0.000000
       7        97                0     0.000000
       8        98                0     0.000000
       9        99                0     0.000000
      10       100                0     0.000000
      11       101                0     0.000000
      12       102                0     0.000000
      13       103                0     0.000000
      14       104                0     0.000000
      15       105                0     0.000000
      16       106                0     0.000000
      17       107                0     0.000000
      18       108                0     0.000000
      19       109       3398375425   100.000000



BeOSrt (так я обозвал свою задумку) с полным "форсажем" в зоне реального времени ведёт себя аналогично. А вот другой пример -

BeOSrt с отключенным "форсажем" в зоне реального времени. Данные SOS Emulator'а:

Settings: work time 1000000 microseconds, 20 working threads.
Using BeOSrt2 scheduler add-on.

  Number  Priority            Value    Share (%)
--------------------------------------------------
       0        90                0     0.000000
       1        91                0     0.000000
       2        92                0     0.000000
       3        93                0     0.000000
       4        94                0     0.000000
       5        95                0     0.000000
       6        96                0     0.000000
       7        97                0     0.000000
       8        98                0     0.000000
       9        99                0     0.000000
      10       100            17655     0.097968
      11       101            35227     0.195476
      12       102            70369     0.390481
      13       103           141102     0.782981
      14       104           281507     1.562094
      15       105           563610     3.127496
      16       106          1128570     6.262482
      17       107          2252514    12.499295
      18       108          4507710    25.013472
      19       109          9022865    50.068256



Расхлёбываем.

Ну что, уважаемые, ни у кого больше руки не чешутся? (а хочется верить что это заразно, хе-хе)

Скачивайте исходники Haiku, вносите изменения в планировщик задач, собирайте и сравнивайте на реальном железе какой из вышеприведённых или ваших собственных вариантов лучше.

Не гарантирую того что всё получится прямо так сходу, возможно что-то где-то ещё придётся поправить, но направление я вам показал. Остальное - дело техники. Дерзайте.

Много планировщиков для одной ОС конечно плохо. Принцип Best Defaults никто не отменял. Но хорошо когда этот самый "best" есть из чего выбрать. Для Haiku R1 (ярко выраженная BeOS-совместимость) я бы остановился на своём самом лучшем варианте, но вот после... Кто знает.


SOS Emulator к вашим услугам.

Вам наверное уже не терпится наложить руки на эту программу. Не смею препятствовать:
http://otoko.narod.ru/files/haiku/sos_emulator.zip

Планировщики задач выполнены в качестве add-on'ов. Чтобы получить ещё один собственный просто сделайте копию какого-нибудь из готовых и модифицируйте. Не забудьте поправить строковую константу add_on_name, лучше ей быть отличной от тех что уже есть.

Для удобства сборки и запуска всего этого хозяйства как обычно прилагаются скрипты.

Но как обычно начать работу можно и с команды: sos_emulator -h

Программа не менее сырая чем SOS Tester и есть куда рости. Исходники вам в руки!


Спасибо за внимание!


(c) Михаил Панасюк aka BeAR, 2009. E-mail: otoko yandex ru
Хостинг от uCoz