/* Program to query a column file (created by columncuits.c) to find * the person with a given CUIT by multithreaded sequential search. * This is written as a microbenchmark to see how fast this computer * is, not a useful program. Searches the 325-megabyte * 6.2-million-record database in 11–18 milliseconds on this MicroPC * with its quad-core 1.1-GHz Celeron N4120 and its 2133 MHz LPDDR4 * RAM. This is My Very First Pthreads Program ♥ */ #include #include #include #include #include #include #include #include #include enum { recsize = 52, n_threads = 16 }; /* XXX hardcoded thread count */ typedef struct { char *buf; size_t len; } region; region mmap_filename(char *name) { region result; int fd = open(name, O_RDONLY); if (fd < 0) { perror(name); exit(1); } result.len = lseek(fd, 0, SEEK_END); result.buf = mmap(NULL, result.len, PROT_READ, MAP_SHARED, fd, (off_t)0); if (result.buf == MAP_FAILED) { perror("mmap"); exit(1); } close(fd); return result; } /* The information each thread needs to do its job: */ typedef struct { region records, cuits; long cuit; } job; static void* search_cuits(void *r) { job *me = (job*)r; long ncuits = me->cuits.len / 8; long *cuitp = (long*)me->cuits.buf; long cuit = me->cuit; for (size_t i = 0; i < ncuits; i++) { if (cuitp[i] != cuit) continue; if (!write(1, me->records.buf + i * recsize, recsize)) { perror("write"); exit(1); } } return NULL; } int main(int argc, char **argv) { long cuit; if (argc != 3 || !(cuit = atol(argv[1]))) { fprintf(stderr, "Usage: %s 23304663359 utlfile/padr/SELE-SAL-CONSTA.p20out1.20230415.tmp\n", argv[0]); return 1; } int bufsize = strlen(argv[2]) + 6; char filenamebuf[bufsize]; strcpy(filenamebuf, argv[2]); strcat(filenamebuf, ".cuit"); region records = mmap_filename(argv[2]); region cuits = mmap_filename(filenamebuf); long ncuits = cuits.len / 8; long *cuitp = (long*)cuits.buf; long cuits_per_thread = ncuits / n_threads; job jobs[n_threads]; for (int i = 0; i < n_threads; i++) { jobs[i] = (job) { .cuit = cuit, .cuits = { .buf = (char*)&cuitp[i * cuits_per_thread], .len = 8 * cuits_per_thread, }, .records = { .buf = records.buf + recsize * i * cuits_per_thread, .len = recsize * cuits_per_thread, }, }; } /* There may be some left over: */ job *last = &jobs[n_threads - 1]; last->cuits.len = (char*)&cuitp[ncuits] - last->cuits.buf; last->records.len = records.buf + records.len - last->records.buf; pthread_t threads[n_threads]; for (int i = 0; i < n_threads; i++) { int err = pthread_create(&threads[i], NULL, /* null attributes */ search_cuits, (void*)&jobs[i]); if (0 != err) { fprintf(stderr, "pthread_create: error %d\n", err); return 1; } } /* We could just call pthread_exit from the main thread here, but * here’s an explicit wait. I mean arguably it would be better to * just fork. */ for (int i = 0; i < n_threads; i++) { int err = pthread_join(threads[i], NULL); if (0 != err) { fprintf(stderr, "pthread_create: error %d\n", err); return 1; } } return 0; }