/* parallel wc(1) demo program */ /* cc -O3 -fopenmp -o wc wc.c */ #include #include #include #include #include #include #include #define BUFSIZE (128 * 1024) /* * Count chars, words, and lines for one segment of the input file. * Track if the first and last characters of the segment were * spaces. We count words on a blank-nonblank transition. If the * first character of a segment is not a blank, it does not count * as a word. */ struct counter { unsigned long chars, words, lines; bool firstblank, lastblank; }; static void reduce(struct counter *restrict totals, const struct counter *restrict segments, size_t nthreads) { size_t i; totals->chars = 0; totals->words = 0; totals->lines = 0; totals->lastblank = true; for (i = 0; i < nthreads; i++) { if (segments[i].chars == 0) continue; totals->chars += segments[i].chars; totals->words += segments[i].words + (totals->lastblank & !segments[i].firstblank); totals->lines += segments[i].lines; totals->lastblank = segments[i].lastblank; } } static void dosegment(struct counter *segments, int fd, int tid, int nthreads, off_t filesize) { struct counter *seg = segments + tid; off_t start, end; size_t want, i; ssize_t got; unsigned long chars = 0, words = 0, lines = 0; char *buf; bool first = true, blank = false, nblank; buf = malloc(BUFSIZE); if (buf == NULL) { perror("malloc"); exit(EXIT_FAILURE); } start = filesize * ((double)tid / nthreads); end = filesize * ((tid + 1.0) / nthreads); while (start < end) { want = end - start < BUFSIZE ? end - start : BUFSIZE; got = pread(fd, buf, want, start); if (got < 0) { perror("pread"); exit(EXIT_FAILURE); } if (got == 0) { fprintf(stderr, "file truncated during reading\n"); exit(EXIT_FAILURE); } if (first) { seg->firstblank = isspace(buf[0]); first = 0; } chars += got; start += got; for (i = 0; i < got; i++) { lines += buf[i] == '\n'; nblank = isspace(buf[i]); words += blank & !nblank; blank = nblank; } } seg->chars = chars; seg->lines = lines; seg->words = words; seg->lastblank = blank; } extern int main(void) { struct stat st; struct counter totals, *segments; size_t nthreads; /* check if we can seek the input */ if (fstat(STDIN_FILENO, &st) != 0) { perror("fstat"); return (EXIT_FAILURE); } if (!S_ISREG(st.st_mode)) { fprintf(stderr, "not a regular file\n"); return (EXIT_FAILURE); } nthreads = omp_get_max_threads(); segments = calloc(nthreads, sizeof *segments); if (segments == NULL) { perror("calloc"); return (EXIT_FAILURE); } # pragma omp parallel dosegment(segments, STDIN_FILENO, omp_get_thread_num(), omp_get_num_threads(), st.st_size); reduce(&totals, segments, nthreads); printf("%lu %lu %lu\n", totals.lines, totals.words, totals.chars); return (EXIT_SUCCESS); }