С: проблемы производительности. Как я могу заставить этот код работать как положено? - PullRequest
3 голосов
/ 13 сентября 2010

Я создал эту маленькую программу для вычисления пи, используя вероятности и отношения.Чтобы он работал быстрее, я решил попробовать многопоточность с помощью pthreads.К сожалению, даже после долгих поисков я не смог решить проблему, которая у меня возникла в том, что когда я запускаю функцию threadFunc с одним потоком, будь то с помощью pthread или просто обычно вызывается из функции Calculate_pi_mt, производительность значительно возрастает.лучше (по крайней мере, в два раза, если не в 3 раза лучше), чем когда я пытаюсь запустить его с двумя потоками на моей двухъядерной машине.Я пытался отключить оптимизации безрезультатно.Насколько я вижу, когда поток работает, он использует локальные переменные, кроме того, когда я использовал блокировку мьютекса для создания суммы попаданий ...

Во-первых, есть ли какие-либо советы длясоздание кода, который будет работать лучше здесь?(то есть стиль), потому что я только учусь, пробуя это.

А во-вторых, будут ли какие-то причины для этих очевидных проблем с производительностью?При работе с числом потоков, установленным в 1, один из моих процессоров максимально достигает 100%.Когда установлено значение два, второй процессор увеличивается примерно до 80% -90%, но вся эта дополнительная работа, которую он, очевидно, выполняет, - безрезультатно!Может ли это быть использование функции rand ()?

struct arguments {
    int n_threads;
    int rays;
    int hits_in;
    pthread_mutex_t *mutex;
};


void *threadFunc(void *arg)
{
    struct arguments* args=(struct arguments*)arg;

    int n = 0;
    int local_hits_in = 0;
    double x;
    double y;
    double r;
    while (n < args->rays)
    {
        n++;
        x = ((double)rand())/((double)RAND_MAX);
        y = ((double)rand())/((double)RAND_MAX);
        r = (double)sqrt(pow(x, 2) + pow(y, 2)); 
        if (r < 1.0){
            local_hits_in++;
        }
    }

    pthread_mutex_lock(args->mutex);
    args->hits_in += local_hits_in;
    pthread_mutex_unlock(args->mutex);

    return NULL;
}


double calculate_pi_mt(int rays, int threads){
    double answer;
    int c;
    unsigned int iseed = (unsigned int)time(NULL);
    srand(iseed);

    if ( (float)(rays/threads) != ((float)rays)/((float)threads) ){
        printf("Error: number of rays is not evenly divisible by threads\n");
    }

    /* argument initialization */
    struct arguments* args = malloc(sizeof(struct arguments));
    args->hits_in = 0;
    args->rays = rays/threads;
    args->n_threads = 0;
    args->mutex = malloc(sizeof(pthread_mutex_t));
    if (pthread_mutex_init(args->mutex, NULL)){
        printf("Error creating mutex!\n");
    }


    pthread_t thread_ary[MAXTHREADS];

    c=0;
    while (c < threads){
        args->n_threads += 1;
        if (pthread_create(&(thread_ary[c]),NULL,threadFunc, args)){
            printf("Error when creating thread\n");
        }
        printf("Created Thread: %d\n", args->n_threads);
        c+=1;
    }


    c=0;
    while (c < threads){
        printf("main waiting for thread %d to terminate...\n", c+1);
        if (pthread_join(thread_ary[c],NULL)){
            printf("Error while waiting for thread to join\n");
        }
        printf("Destroyed Thread: %d\n", c+1);

        c+=1;
    }

    printf("Hits in %d\n", args->hits_in);
    printf("Rays: %d\n", rays);
    answer = 4.0 * (double)(args->hits_in)/(double)(rays);

    //freeing everything!
    pthread_mutex_destroy(args->mutex);
    free(args->mutex);
    free(args);

    return answer;
}

Ответы [ 3 ]

11 голосов
/ 13 сентября 2010

Я вижу несколько проблем:

  • rand() не является поточно-ориентированным. Используйте drand48_r() (который генерирует double в диапазоне [0.0, 1.0), что вам нужно)
  • Вы создаете только одну struct arguments структуру, а затем пытаетесь использовать ее для нескольких потоков. Вам нужно создать отдельный файл для каждого потока (просто используйте массив).

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

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include <pthread.h>

struct thread_info {
    int thread_n;
    pthread_t thread_id;
    int rays;
    int hits_in;
};

void seed_rand(int thread_n, struct drand48_data *buffer)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    srand48_r(tv.tv_sec * thread_n + tv.tv_usec, buffer);
}

void *threadFunc(void *arg)
{
    struct thread_info *thread_info = arg;
    struct drand48_data drand_buffer;

    int n = 0;
    const int rays = thread_info->rays;
    int hits_in = 0;
    double x;
    double y;
    double r;

    seed_rand(thread_info->thread_n, &drand_buffer);

    for (n = 0; n < rays; n++)
    {
        drand48_r(&drand_buffer, &x);
        drand48_r(&drand_buffer, &y);
        r = x * x + y * y;
        if (r < 1.0){
            hits_in++;
        }
    }

    thread_info->hits_in = hits_in;
    return NULL;
}


double calculate_pi_mt(int rays, int threads)
{
    int c;
    int hits_in = 0;

    if (rays % threads) {
        printf("Error: number of rays is not evenly divisible by threads\n");
        rays = (rays / threads) * threads;
    }

    /* argument initialization */
    struct thread_info *thr = malloc(threads * sizeof thr[0]);

    for (c = 0; c < threads; c++) {
        thr[c].thread_n = c;
        thr[c].rays = rays / threads;
        thr[c].hits_in = 0;
        if (pthread_create(&thr[c].thread_id, NULL, threadFunc, &thr[c])) {
            printf("Error when creating thread\n");
        }
        printf("Created Thread: %d\n", thr[c].thread_n);
    }

    for (c = 0; c < threads; c++) {
        printf("main waiting for thread %d to terminate...\n", c);
        if (pthread_join(thr[c].thread_id, NULL)) {
            printf("Error while waiting for thread to join\n");
        }
        hits_in += thr[c].hits_in;
        printf("Destroyed Thread: %d\n", c+1);
    }

    printf("Hits in %d\n", hits_in);
    printf("Rays: %d\n", rays);
    double answer = (4.0 * hits_in) / rays;

    free(thr);

    return answer;
}
8 голосов
/ 13 сентября 2010

Вы используете слишком много примитивов синхронизации. Вы должны суммировать local_hits в конце основного потока и не использовать мьютекс для его асинхронного обновления. Или, по крайней мере, вы можете использовать атомарную операцию (это просто int), чтобы сделать это вместо блокировки целого мьютекса для обновления одного int.

1 голос
/ 13 сентября 2010

Threading имеет стоимость. Может случиться так, что, поскольку ваш полезный вычислительный код выглядит очень просто, стоимость управления потоками (стоимость, оплачиваемая при изменении потока и стоимости синхронизации) намного выше, чем выгода.

...