Чтение онлайн

ЖАНРЫ

QNX/UNIX: Анатомия параллелизма
Шрифт:

• Весь объем этой работы делится поровну (или почти поровну) между несколькими (ключ

t
) потоками.

• Сравниваем усредненное время единичного выполнения рабочей функции для разного числа выполняющих потоков (в выводе

"calculating"
— это время эталонного вычисления в одном главном потоке, a
"evaluation"
— время того же вычисления, но во многих потоках).

• Для того чтобы иметь еще большую гибкость, предоставляется возможность переопределять приоритет, под которым в системе все это происходит (ключ

p
).

Вот самая краткая сводка результатов (1-я строка вывода переносится для удобства чтения):

# t1 -n1 -t1000 -a2000

Multi-thread evaluation, thread number = 1000, priority level: 10

rescheduling = 3.99939

calculating = 1.04144

evaluation = 1.08001

# t1 -n1 -t10000 -a20000

Multi-thread evaluation, thread number = 10000, priority level: 10

rescheduling = 3.99939

calculating = 1.04378

evaluation = 1.61946

# t1 -n5 -a2000 -t1

Single-thread evaluation, priority level: 10

rescheduling = 3.99939

calculating = 5.07326

evaluation = 5.04726

# t1 -n5 -a2000 -t2

Multi-thread evaluation, thread number = 2, priority level: 10

rescheduling = 3.99939

calculating = 5.06309

evaluation = 5.04649

# t1 -n5 -a2000 -t20

Multi-thread evaluation, thread number = 20, priority level: 10

rescheduling = 3.99939

calculating = 5.06343

evaluation = 4.96956

# t1 -n5 -p51 -a512 -t1

Single-thread evaluation, priority level: 51

rescheduling = 3.99939

calculating = 4.94502

evaluation = 4.94511

# t1 -n5 -р51 -a512 -t11

Multi-thread evaluation, thread number = 11, priority level: 51

rescheduling = 3.99939

calculating = 4.94554

evaluation = 4.94549

# t1 -n5 -p51 -a512 -t111

Multi-thread evaluation, thread number = 111, priority level: 51

rescheduling = 3.99939

calculating = 5.02755

evaluation = 4.94487

# t1 -n5 -p51 -a30000 -t10000

Multi-thread evaluation, thread number = 10000, priority level: 51

rescheduling = 3.99939

calculating = 4.94575

evaluation = 5.31224

Краткий

и, возможно, несколько парадоксальный итог этого теста может звучать так: при достаточно высоком уровне приоритета (выше 12–13, когда на его выполнение не влияют
процессы обслуживания клавиатуры, мыши и др.) время выполнения в «классическом» последовательном коде и в многопоточном коде (где несколько тысяч потоков!) практически не различаются. Различия не более 8%, причем в обе стороны, что мы склонны считать «статистикой эксперимента». К обсуждению этого якобы противоречащего здравому смыслу феномена мы еще вернемся.

А пока посмотрим на текст примера, что и является нашей главной дачей. Обсуждаемое приложение вполне работоспособно в QNX с большой вероятностью в большинстве других UNIX-систем, но в Linux оно завершится аварийно. Причина этого кроется в операторах

int id = pthread_self - 2;

trtime[id].s = ...

Это дает повод лишний раз обратиться к вопросу «POSIX-совместимости». POSIX описывает, что TID потока присваивается: а) в рамках процесса, которому принадлежит поток; б) начиная со значения 1, соответствующего главному потоку приложения. В Linux, выполняющем и

pthread_create
, и
fork
через единый системный вызов
_clone
сделано небольшое «упрощение», навязанное в том числе и гонкой за повышением производительности: TID присваиваются из единого ряда PID. И сразу же «вылезает» несовместимость, ведущая к аварийному завершению показанного выше приложения. В последних редакциях ядра Linux делаются изменения по приведению механизмов параллельности к общей POSIX-модели.

Этот момент сам по себе достаточно интересен, поэтому остановимся на нем подробнее, для чего создадим простейший программный тест [22] :

#define TCNT 10

void * test(void *in) {

printf("pid %ld, tid %ld\n", getpid, pthread_self);

return NULL;

}

int main(int argc, char **argv, char **envp) {

22

Этот тест и его результаты для Linux подсказаны одним из участников (имя нам неизвестно) обсуждений на http://qnxclub.net.forum.

pthread_t tld[TCNT];

int i, status;

for (i=0; i < TCNT; i++) {

status = pthread_create(&tid[i], NULL, test, NULL);

if (status != 0)

err(EXIT_FAILURE, "pthread_create");

}

return(EXIT_SUCCESS);

}

Результаты выполнения этого теста в нескольких POSIX-совместимых ОС различны и весьма красноречивы:

$ uname -sr Linux 2.4.21-0.13mdk

$ ./test_pthread

pid 2008, tid 16386

pid 2009, tid 32771

pid 2010, tid 49156

pid 2011, tid 65541

pid 2012, tid 81926

pid 2013, tid 98311

pid 2014, tid 114696

pid 2015, tid 131081

pid 2016, tid 147466

pid 2017, tid 163851

Поделиться с друзьями: