2013-11-21 5 views
0

У меня есть простой вопрос для вас. Я сделал этот код для вычисления факториала числа без рекурсии.Нерекурсивный факториал в C

int fact2(int n){ 
    int aux=1, total = 1; 
    int i; 
    int limit = n - 1; 
    for (i=1; i<=limit; i+=2){ 
     aux = i*(i+1); 
     total = total*aux; 
    } 
    for (;i<=n;i++){ 
     total = total*i; 
    } 
return total; 

} 

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

+0

n! = n * (n-1) * ... * (n/2) * ... * 1. У вас может быть один процессор, который выполняет первые n/2-умножения, другой процессор сделает все остальное, затем умножьте 2 результата вместе. –

ответ

2

Вы можете использовать библиотеку ptherads для создания двух отдельных потоков. Каждый поток должен выполнять половину умножений. Я мог бы собрать следующее решение.

#include <pthread.h> 

typedef struct { 
    int id; 
    int num; 
    int *result; 
} thread_arg_t; 

void* thread_func(void *arg) { 
    int i; 
    thread_arg_t *th_arg = (thread_arg_t *)arg; 
    int start, end; 
    if(th_arg->id == 0) { 
     start = 1; 
     end = th_arg->num/2; 
    } else if (th_arg->id == 1) { 
     start = th_arg->num/2; 
     end = th_arg->num + 1; 
    } else { 
     return NULL; 
    } 
    for(i=start; i < end; i++) { 
      th_arg->result[th_arg->id] *= i; 
    } 
    return NULL; 
} 

int factorial2(int n) { 
    pthread_t threads[2]; 
    int rc; 
    int result[2]; 
    thread_arg_t th_arg[2]; 
    for(i=0; i<2; i++) { 
     th_arg[i].id = i; 
     th_arg[i].num = n; 
     th_arg[i].result = result; 
     rc = pthread_create(&threads[i], NULL, thread_func, (void *)&th_arg[i]); 
     if (rc){ 
     printf("pthread_create() failed, rc = %d\n", rc); 
     exit(1); 
     } 
    } 

    /* wait for threads to finish */ 
    for(i=0; i<2; i++) { 
     pthread_join(thread[i], NULL); 

    /* compute final one multiplication */ 
    return (result[0] * result[1]); 
} 

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

+0

Насколько медленнее, чем обычное старое последовательное решение? Это не ваша вина, но количество умножений, которое вы можете сделать на факториале, а не на переполнение простой «int», составляет около 12 (включая умножение на 1), а накладные расходы на создание и уничтожение потоков астрономически сопоставляются. Диапазон составляет до 20 для 64-битного типа 'int'; все еще недостаточно, чтобы компенсировать затраты на создание потоков. –

+0

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

+0

См. [Закон Амдала] (http://en.wikipedia.org/wiki/Amdahl's_law) для некоторого объяснения. Накладные расходы на установку потока (и разрывы) не являются незначительными, поэтому объем вычислений, который должен делать поток, заслуживающий внимания, также значителен. Если вы использовали точный арифметический пакет «большого числа» и вычисляли N! для значений N в сотнях, то вы получите некоторую выгоду. Для арифметики с плавающей запятой вы можете увидеть некоторую выгоду, если у вас есть достаточно большие значения N, но мое подозрение заключается в том, что вы не увидите никакой выгоды; вычисление просто недостаточно тяжелое. –