Процессоры PThreads и MultiCore на Linux
Я пишу простое приложение, которое использует Threads для повышения производительности.
Проблема в том, что это приложение отлично работает на Windows, используя 2 ядра, которые имеет мой процессор. Но когда я выполняю на Linux, кажется, что использует только 1 Core.
Я не понимаю, почему это происходит.
Это мой код, С++:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
void* function(void*)
{
int i=0;
for(i=0; i<1110111; i++)
rand();
return 0;
}
void withOutThreads(void)
{
function(0);
function(0);
}
void withThreads(void)
{
pthread_t* h1 = new pthread_t;
pthread_t* h2 = new pthread_t;
pthread_attr_t* atr = new pthread_attr_t;
pthread_attr_init(atr);
pthread_attr_setscope(atr,PTHREAD_SCOPE_SYSTEM);
pthread_create(h1,atr,function,0);
pthread_create(h2,atr,function,0);
pthread_join(*h1,0);
pthread_join(*h2,0);
pthread_attr_destroy(atr);
delete h1;
delete h2;
delete atr;
}
int main(void)
{
int ini,tim;
ini = clock();
withOutThreads();
tim = (int) ( 1000*(clock()-ini)/CLOCKS_PER_SEC );
printf("Time Sequential: %d ms\n",tim);
fflush(stdout);
ini = clock();
withThreads();
tim = (int) ( 1000*(clock()-ini)/CLOCKS_PER_SEC );
printf("Time Concurrent: %d ms\n",tim);
fflush(stdout);
return 0;
}
Выход на Linux:
Time Sequential: 50 ms
Time Concurrent: 1610 ms
Выход в Windows:
Time Sequential: 50 ms
Time Concurrent: 30 ms
Ответы
Ответ 1
clock() работает по-разному на windows vs linux, поэтому не используйте это для измерения времени. В linux он измеряет время процессора, на окнах он измеряет время настенных часов. В идеале они будут одинаковыми в этом случае, но вы должны использовать что-то согласованное между платформами для измерения времени. например gettimeofday()
rand() сериализует ваши потоки в linux. rand() содержит внутренний замок, чтобы быть потокобезопасным. В rand() manpage состояния rand() не является потокобезопасным или реентеративным, однако, по крайней мере, код в последнем glibc блокирует вызов.
Я не уверен, как Windows обрабатывает это, либо он не является потокобезопасным вообще, либо использует локальные переменные потока.
Используйте rand_r для linux или найдите более эффективную функцию использования процессора.
void* function(void*)
{
unsigned int seed = 42;
int i=0;
for(i=0; i<1110111; i++)
rand_r(&seed);
return 0;
}
Ответ 2
Проблема заключается в том, что многопоточная версия Linux или rand()
блокирует мьютекс. Измените свою функцию на:
void* function(void*)
{
int i=0;
unsigned rand_state = 0;
for(i=0; i<1110111; i++)
rand_r(&rand_state);
return 0;
}
Вывод:
Time Sequential: 10 ms
Time Concurrent: 10 ms
Ответ 3
Linux "видит" потоки как процессы, это означает, что все процессы являются потоками одного потока.
в таблице процессов (task_struct), когда мы создаем процесс, создается PID, когда мы создаем второй поток, тогда PID становится TGID (идентификатор группы потоков), и каждый поток получает TID (идентификатор потока).
В userland мы увидим только первый поток (используя ps aux), но если мы выполним "ps -eLf", мы увидим новый столбец с именем LWP (легкий вес), который является TID.
то, например:
$ ps -eLf
UID PID PPID LWP C NLWP STIME TTY TIME CMD
корень 1356 1 1356 0 4 2014? 00:00:00/sbin/rsyslogd
корень 1356 1 1357 0 4 2014? 00:02:01/sbin/rsyslogd
корень 1356 1 1359 0 4 2014? 00:01:55/sbin/rsyslogd
корень 1356 1 1360 0 4 2014? 00:00:00/sbin/rsyslogd
dbus 1377 1 1377 0 1 2014? 00:00:00 dbus-daemon
Как мы видим, PID тот же, но реальный PID - это LWP (TID).
Когда процесс имеет только один поток (например, дБ дБ), PID = LWP (TID)
Внутренне ядро всегда использует TID, как PID.
После этого ядро сможет использовать расписание для каждого потока с помощью реального parallelism.
Ответ 4
Для меня это похоже на реализацию планировщика ОС. Само по себе не проблема в коде. ОС решает, какой поток будет выполняться на каком ядре, и если будут соблюдаться правила привязки потоков/процессоров, он будет каждый раз прикреплять этот поток к одному и тому же ЦП.
Это простое объяснение довольно сложного вопроса.