-
-
Save clausecker/a1fd7df7bfac39d6da4d2710551c6712 to your computer and use it in GitHub Desktop.
parallel wc example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* parallel wc(1) demo program */ | |
/* cc -O3 -fopenmp -o wc wc.c */ | |
#include <ctype.h> | |
#include <unistd.h> | |
#include <omp.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <sys/stat.h> | |
#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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment