我编写了四个不同的程序来计算两个文件中的单词总数。这四个版本看起来大致相同。前三个版本使用两个线程进行计数,仅三个语句的顺序不同。最新版本使用一个线程进行计数。我将首先列出每个版本的不同部分和公共(public)部分,然后列出每个版本的输出和我的问题。

不同部分:

// 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);
}

笔记
  • file1是一个包含10000个单词的“单词”文件。 file2是由cp命令创建的file1的副本。
  • 为了使执行时间足够长,程序会重复计算字数。 N是循环数。因此,结果不是总单词的准确数量,而是乘以N.
  • 请不要过分强调计数算法。在此示例中,我只是担心执行时间。
  • 重要信息:机器为Intel®Celeron(R)CPU 420 @ 1.60GHz。一个核心。操作系统是Linux 3.2.0。 也许是核心的原因,就像其他人所说的那样。但是我仍然想弄清楚。

  • 该程序对单词计数,并使用clock_gettime()计算例程count_words()的进程cpu时间和线程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()都在单个线程中运行。

    最佳答案

    可能是因为任何线程的创建都迫使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(在两种情况下)。

    10-04 22:01
    查看更多