Подтвердить что ты не робот

Процессоры 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
4b9b3361

Ответ 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

Для меня это похоже на реализацию планировщика ОС. Само по себе не проблема в коде. ОС решает, какой поток будет выполняться на каком ядре, и если будут соблюдаться правила привязки потоков/процессоров, он будет каждый раз прикреплять этот поток к одному и тому же ЦП.

Это простое объяснение довольно сложного вопроса.