运行此代码时出现分段错误。令人惊讶的是,当我将thread_count
设置为 16 或更低时,它不会给出任何错误。当我使用 gdb 调试代码时,代码在Calculate()
线程函数中的第local_answer += vec_1[j] * vec_2[j];
行出现错误。这种行为的原因是什么?我该如何解决这个问题?
我正在使用这个 gcc 命令编译:
gcc test.c -o DP -lpthread -lm -mcmodel=large -g
这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
double *vec_1 = NULL;
double *vec_2 = NULL;
int vec_length = 0;
int thread_count = 0;
double answer = 0;
double *partial_results = NULL;
pthread_mutex_t mutex;
void *Calculate(void *arg) {
int myId = (int) arg;
int myStart = myId * vec_length / thread_count;
int myEnd = (myId + 1) * vec_length / thread_count;
double local_answer = 0;
int j;
for(j = myStart; j < myEnd; j++) {
local_answer += vec_1[j] * vec_2[j];
}
pthread_mutex_lock(&mutex);
partial_results[myId] = local_answer;
pthread_mutex_unlock(&mutex);
}
int main(int argc, const char *argv[]) {
srand((unsigned int) time(NULL));
pthread_mutex_init(&mutex, NULL);
int num_iterations = 5;
vec_length = 1000000000;
thread_count = 25;
partial_results = (double*) malloc(thread_count * sizeof(double));
double avg_time = 0;
int i;
vec_1 = (double*) malloc(vec_length * sizeof(double));
vec_2 = (double*) malloc(vec_length * sizeof(double));
if(vec_1==NULL || vec_2==NULL){
printf("Memory Allocation failed");
exit(0);
}
int j;
for (j = 0; j < vec_length; j++) {
vec_1[j] = ((double) rand() / (double) (RAND_MAX)) + 1;
vec_2[j] = ((double) rand() / (double) (RAND_MAX)) + 1;
}
for (i = 0; i < num_iterations; i++) {
pthread_t threads[thread_count];
pthread_attr_t attr;
void* status;
struct timeval t1, t2;
gettimeofday(&t1, NULL);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
int t;
for (t = 0; t < thread_count; t++) {
if (pthread_create(&threads[t], NULL, Calculate, (void*)(t))) {
printf("ERROR in pthread_create()");
exit(-1);
}
}
pthread_attr_destroy(&attr);
answer = 0;
for (t = 0; t < thread_count; t++) {
if (pthread_join(threads[t], &status)) {
printf("ERROR in pthread_join()");
exit(-1);
}
answer += partial_results[t];
}
gettimeofday(&t2, NULL);
avg_time += (t2.tv_sec - t1.tv_sec) * 1000.0 + (t2.tv_usec - t1.tv_usec) / 1000.0;
}
printf("Average time Spent : %lf n", avg_time / num_iterations);
pthread_mutex_destroy(&mutex);
return 0;
}
您的vec_length
具有int
类型。 在 Linux x86 或 x86_64 上使用 gcc,int
以 32 位二进制补码格式表示。 这足以容纳您用于vec_length
、1,000,000,000 的值,但不能容纳该值的大多数整数倍。 您计算了几个这样的倍数,并且生成的有符号整数溢出正式产生未定义的行为。
在实践中,gcc 在有符号整数溢出时的实际行为可能是可重现的。 在这种情况下,您可以编写一个程序来亲自证明结果对于向量长度的几个小整数倍是负数。 如果发生这种情况,您的程序将尝试在两个向量中的每一个的边界之外访问,在确实指示错误的行处,并且可能出现段错误。 (即使溢出结果不可重现,为其中一些未定义的乘法行为获得负结果仍然在可能的范围内。
您有几种选择,其中包括:
-
使用更广泛的数据类型进行索引计算
int myStart = myId * (int64_t) vec_length / thread_count;
-
仅使用
thread_count
均匀划分vec_length
的值,并使用括号以确保在索引计算中首先执行除法int myStart = myId * (vec_length / thread_count); // ... vec_length = 1000000000; thread_count = 32; // or 10 or 8 or 1000
其他一些事情:
- 提供的代码不使用任何 math.h 函数。 因此,它不需要
#include
math.h,也不需要在libm中链接。 - 要使用 GCC 编译 Pthreads 程序,您应该使用
-pthreads
标志,在这种情况下,您也不需要在 libpthread 中显式链接。 - 如评论中所述,您不需要
pthread_attr_t
的复杂性。 - 如注释中所述,您对互斥锁的特定使用是不必要的性能消耗。