我写了四个不同的程序来计算两个文件中的总字数。这四个版本看起来大致相同。前三个版本使用两个线程进行计数,只有三个语句的顺序不同。最后一个版本使用一个线程来计算。我将首先列出每个版本的不同部分和公共部分,然后列出每个版本的输出和我的问题。
不同部分:
// version 1
count_words(&file1);
pthread_create(&new_thread, NULL, count_words, &file2);
pthread_join(new_thread, NULL);
// version 2
pthread_create(&new_thread, NULL, count_words, &file2);
count_words(&file1);
pthread_join(new_thread, NULL);
// version 3
pthread_create(&new_thread, NULL, count_words, &file2);
pthread_join(new_thread, NULL);
count_words(&file1);
// version 4
count_words(&file1);
count_words(&file2);
常见部分:( 将不同的部分插入此公共部分以制作完整版)
#include <stdio.h>
#include <pthread.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#define N 2000
typedef struct file_t {
char *name;
int words;
} file_t;
double time_diff(struct timespec *, struct timespec *);
void *count_words(void *);
// Usage: progname file1 file2
int main(int argc, char *argv[]) {
pthread_t new_thread;
file_t file1, file2;
file1.name = argv[1];
file1.words = 0;
file2.name= argv[2];
file2.words = 0;
// Insert different part here
printf("Total words: %d\n", file1.words+file2.words);
return 0;
}
void *count_words(void *arg) {
FILE *fp;
file_t *file = (file_t *)arg;
int i, c, prevc = '\0';
struct timespec process_beg, process_end;
struct timespec thread_beg, thread_end;
double process_diff, thread_diff;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_beg);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thread_beg);
fp = fopen(file->name, "r");
for (i = 0; i < N; i++) {
while ((c = getc(fp)) != EOF) {
if (!isalnum(c) && isalnum(prevc))
file->words++;
prevc = c;
}
fseek(fp, 0, SEEK_SET);
}
fclose(fp);
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_end);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thread_end);
process_diff = time_diff(&process_beg, &process_end);
thread_diff = time_diff(&thread_beg, &thread_end);
printf("count_words() in %s takes %.3fs process time and"
"%.3fs thread time\n", file->name, process_diff, thread_diff);
return NULL;
}
double time_diff(struct timespec *beg, struct timespec *end) {
return ((double)end->tv_sec + (double)end->tv_nsec*1.0e-9)
- ((double)beg->tv_sec + (double)beg->tv_nsec*1.0e-9);
}
请注意
程序计算单词并使用clock_gettime()计算进程cpu时间和例程count_words()的线程cpu时间,然后输出时间和单词数。以下是输出和我的问题评论。如果有人能解释加班时间的原因,我将非常感激。
// version 1
count_words() in file1 takes 2.563s process time and 2.563s thread time
count_words() in file2 takes 8.374s process time and 8.374s thread time
Total words: 40000000
注释:原始线程完成count_words()并等待新线程死亡。当count_words()在新线程中运行时,不会发生上下文切换(因为进程时间==线程时间)。 为什么需要这么多时间?新线程中count_words()会发生什么?
// version 2
count_words() in file1 takes 16.755s process time and 8.377s thread time
count_words() in file2 takes 16.753s process time and 8.380s thread time
Total words: 40000000
评论:这里有两个并行线程。发生上下文切换,因此处理时间>线程时间。
// version 3
count_words() in file2 takes 8.374s process time and 8.374s thread time
count_words() in file1 takes 8.365s process time and 8.365s thread time
Total words: 40000000
注释:新线程先计数,原始线程等待它。加入新线程后,原始线程开始计数。 他们都没有上下文切换,为什么花了这么多时间,特别是新线程加入后的计数?
// version 4
count_words() in file1 takes 2.555s process time and 2.555s thread time
count_words() in file2 takes 2.556s process time and 2.556s thread time
Total words: 40000000
评论:最快的版本。没有创建新线程。 count_words()都在一个线程中运行。
答案 0 :(得分:7)
这可能是因为创建任何线程迫使libc在getc
中使用同步。这使得此功能明显变慢。以下示例对我来说与版本3一样慢:
void *skip(void *p){ return NULL; };
pthread_create(&new_thread, NULL, skip, NULL);
count_words(&file1);
count_words(&file2);
要解决此问题,您可以使用缓冲区:
for (i = 0; i < N; i++) {
char buffer[BUFSIZ];
int read;
do {
read = fread(buffer, 1, BUFSIZ, fp);
int j;
for(j = 0; j < read; j++) {
if (!isalnum(buffer[j]) && isalnum(prevc))
file->words++;
prevc = buffer[j];
}
} while(read == BUFSIZ);
fseek(fp, 0, SEEK_SET);
}
在此解决方案中,很少调用IO函数以使同步开销无关紧要。这不仅解决了奇怪的时间问题,而且使其快几倍。对我来说,它从0.54s
(没有线程)或0.85s
(带线程)减少到0.15s
(在这两种情况下)。