#include #include #include "lib/mlr_globals.h" #include "lib/mlrutil.h" #include "input/file_reader_stdio.h" #include "input/file_reader_mmap.h" #include "input/lrec_readers.h" #include "lib/string_builder.h" #include "input/byte_readers.h" #include "input/line_readers.h" #include "input/peek_file_reader.h" #include "containers/parse_trie.h" #define PEEK_BUF_LEN 32 #define STRING_BUILDER_INIT_SIZE 1024 #define FIXED_LINE_LEN 1024 // ================================================================ static FILE* fopen_or_die(char* filename) { FILE* fp = fopen(filename, "r"); if (fp == NULL) { perror("fopen"); fprintf(stderr, "Couldn't open \"%s\" for read; exiting.\n", filename); exit(1); } return fp; } // ================================================================ static int read_file_mlr_get_line(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); int bc = 0; while (1) { char* line = mlr_get_cline(fp, '\n'); if (line == NULL) break; bc += strlen(line); if (do_write) { fputs(line, stdout); fputc('\n', stdout); } free(line); } fclose(fp); return bc; } // ================================================================ static int read_file_mlr_getcdelim(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); char irs = '\n'; int bc = 0; while (1) { char* line = mlr_get_cline2(fp, irs); if (line == NULL) break; //bc += linelen; // available by API, but make a fair comparison bc += strlen(line); if (do_write) { fputs(line, stdout); fputc('\n', stdout); } free(line); } fclose(fp); return bc; } // ================================================================ static int read_file_mlr_getsdelim(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); char* irs = "\r\n"; int irslen = strlen(irs); int bc = 0; while (1) { char* line = mlr_get_sline(fp, irs, irslen); if (line == NULL) break; //bc += linelen; // available by API, but make a fair comparison bc += strlen(line); if (do_write) { fputs(line, stdout); fputc('\n', stdout); } free(line); } fclose(fp); return bc; } // ================================================================ static int popen_file_mlr_getsdelim(char* reader, char* filename, int do_write) { char* command = mlr_malloc_or_die(strlen(reader) + 1 + strlen(filename) + 1); strcpy(command, reader); strcat(command, " "); strcat(command, filename); FILE* fp = popen(command, "r"); if (fp == NULL) { perror("popen"); exit(1); } char* irs = "\r\n"; int irslen = strlen(irs); int bc = 0; while (1) { char* line = mlr_get_sline(fp, irs, irslen); if (line == NULL) break; //bc += linelen; // available by API, but make a fair comparison bc += strlen(line); if (do_write) { fputs(line, stdout); fputc('\n', stdout); } free(line); } pclose(fp); return bc; } // ================================================================ static char* read_line_fgetc(FILE* fp, char* irs) { char* line = mlr_malloc_or_die(FIXED_LINE_LEN); char* p = line; while (TRUE) { int c = fgetc(fp); if (c == EOF) { if (p == line) { return NULL; } else { *(p++) = 0; return line; } } else if (c == irs[0]) { *(p++) = 0; return line; } else { *(p++) = c; } } } static int read_file_fgetc_fixed_len(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); char* irs = "\n"; int bc = 0; while (TRUE) { char* line = read_line_fgetc(fp, irs); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); free(line); } fclose(fp); return bc; } // ================================================================ static char* read_line_getc_unlocked(FILE* fp, char* irs) { char* line = mlr_malloc_or_die(FIXED_LINE_LEN); char* p = line; while (TRUE) { int i = getc_unlocked(fp); char c = i; if (i == EOF) { if (p == line) { return NULL; } else { *(p++) = 0; return line; } } else if (c == irs[0]) { *(p++) = 0; return line; } else { *(p++) = c; } } } static int read_file_getc_unlocked_fixed_len(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); char* irs = "\n"; int bc = 0; while (TRUE) { char* line = read_line_getc_unlocked(fp, irs); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); free(line); } fclose(fp); return bc; } // ================================================================ static char* read_line_getc_unlocked_psb(FILE* fp, string_builder_t* psb, char* irs) { while (TRUE) { int c = getc_unlocked(fp); if (c == EOF) { if (sb_is_empty(psb)) return NULL; else return sb_finish(psb); } else if (c == irs[0]) { return sb_finish(psb); } else { sb_append_char(psb, c); } } } static int read_file_getc_unlocked_psb(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); string_builder_t* psb = sb_alloc(STRING_BUILDER_INIT_SIZE); char* irs = "\n"; int bc = 0; while (TRUE) { char* line = read_line_getc_unlocked_psb(fp, psb, irs); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); free(line); } sb_free(psb); fclose(fp); return bc; } // ================================================================ static char* read_line_fgetc_psb(FILE* fp, string_builder_t* psb, char* irs) { while (TRUE) { int c = fgetc(fp); if (c == EOF) { if (sb_is_empty(psb)) return NULL; else return sb_finish(psb); } else if (c == irs[0]) { return sb_finish(psb); } else { sb_append_char(psb, c); } } } static int read_file_fgetc_psb(char* filename, int do_write) { FILE* fp = fopen_or_die(filename); string_builder_t* psb = sb_alloc(STRING_BUILDER_INIT_SIZE); char* irs = "\n"; int bc = 0; while (TRUE) { char* line = read_line_fgetc_psb(fp, psb, irs); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); free(line); } sb_free(psb); fclose(fp); return bc; } // ================================================================ static char* read_line_mmap_psb(file_reader_mmap_state_t* ph, string_builder_t* psb, char* irs) { char *p = ph->sol; while (TRUE) { if (p == ph->eof) { ph->sol = p; if (sb_is_empty(psb)) return NULL; else return sb_finish(psb); } else if (*p == irs[0]) { ph->sol = p+1; return sb_finish(psb); } else { sb_append_char(psb, *p); p++; } } } static int read_file_mmap_psb(char* filename, int do_write) { file_reader_mmap_state_t* ph = file_reader_mmap_open(NULL, filename); string_builder_t* psb = sb_alloc(STRING_BUILDER_INIT_SIZE); char* irs = "\n"; int bc = 0; while (TRUE) { char* line = read_line_mmap_psb(ph, psb, irs); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); } sb_free(psb); file_reader_mmap_close(ph, NULL); return bc; } // ================================================================ #define IRS_STRIDX 11 #define EOF_STRIDX 22 #define IRSEOF_STRIDX 33 static char* read_line_pfr_psb(peek_file_reader_t* pfr, string_builder_t* psb, parse_trie_t* ptrie) { int rc, stridx, matchlen; while (TRUE) { pfr_buffer_by(pfr, ptrie->maxlen); rc = parse_trie_ring_match(ptrie, pfr->peekbuf, pfr->sob, pfr->npeeked, pfr->peekbuflenmask, &stridx, &matchlen); if (rc) { pfr_advance_by(pfr, matchlen); switch(stridx) { case IRS_STRIDX: return sb_finish(psb); break; case IRSEOF_STRIDX: return sb_finish(psb); break; case EOF_STRIDX: return NULL; break; } } else { sb_append_char(psb, pfr_read_char(pfr)); } } } static int read_file_pfr_psb(char* filename, int do_write) { byte_reader_t* pbr = stdio_byte_reader_alloc(); string_builder_t* psb = sb_alloc(STRING_BUILDER_INIT_SIZE); pbr->popen_func(pbr, NULL, filename); peek_file_reader_t* pfr = pfr_alloc(pbr, PEEK_BUF_LEN); parse_trie_t* ptrie = parse_trie_alloc(); parse_trie_add_string(ptrie, "\n", IRS_STRIDX); parse_trie_add_string(ptrie, "\xff", EOF_STRIDX); parse_trie_add_string(ptrie, "\n\xff", IRSEOF_STRIDX); int bc = 0; while (TRUE) { char* line = read_line_pfr_psb(pfr, psb, ptrie); if (line == NULL) break; if (do_write) { fputs(line, stdout); fputc('\n', stdout); } bc += strlen(line); free(line); } sb_free(psb); pbr->pclose_func(pbr, NULL); return bc; } // ================================================================ static void usage(char* argv0) { fprintf(stderr, "Usage: %s {filename}\n", argv0); exit(1); } int main(int argc, char** argv) { int nreps = 1; int do_write = 0; if (argc != 2 && argc != 3 && argc != 4) usage(argv[0]); char* filename = argv[1]; if (argc >= 3) (void)sscanf(argv[2], "%d", &nreps); if (argc >= 4) (void)sscanf(argv[3], "%d", &do_write); double s, e, t; int bc; for (int i = 0; i < nreps; i++) { s = get_systime(); bc = read_file_mlr_get_line(filename, do_write); e = get_systime(); t = e - s; printf("type=getdelim,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_mlr_getcdelim(filename, do_write); e = get_systime(); t = e - s; printf("type=mlr_getcdelim,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_mlr_getsdelim(filename, do_write); e = get_systime(); t = e - s; printf("type=mlr_getsdelim,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = popen_file_mlr_getsdelim("zcat -cf < ", filename, do_write); e = get_systime(); t = e - s; printf("type=mlr_popen_getsdelim,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_fgetc_fixed_len(filename, do_write); e = get_systime(); t = e - s; printf("type=fgetc_fixed_len,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_getc_unlocked_fixed_len(filename, do_write); e = get_systime(); t = e - s; printf("type=getc_unlocked_fixed_len,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_getc_unlocked_psb(filename, do_write); e = get_systime(); t = e - s; printf("type=getc_unlocked_psb,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_fgetc_psb(filename, do_write); e = get_systime(); t = e - s; printf("type=fgetc_psb,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_mmap_psb(filename, do_write); e = get_systime(); t = e - s; printf("type=mmap_psb,t=%.6lf,n=%d\n", t, bc); fflush(stdout); s = get_systime(); bc = read_file_pfr_psb(filename, do_write); e = get_systime(); t = e - s; printf("type=pfr_psb,t=%.6lf,n=%d\n", t, bc); fflush(stdout); } return 0; } // ================================================================ // $ ./getl ../data/big.csv 5|tee x // $ mlr --opprint cat then sort -n t x // type t n type t n // getdelim 0.118618 55888899 getdelim 0.118057 55888899 // getdelim 0.121467 55888899 getdelim 0.118727 55888899 // getdelim 0.121943 55888899 getdelim 0.119609 55888899 // getdelim 0.124756 55888899 getdelim 0.122506 55888899 // getdelim 0.127039 55888899 getdelim 0.123099 55888899 // getc_unlocked_fixed_len 0.167563 55888899 getc_unlocked_fixed_len 0.168109 55888899 // getc_unlocked_fixed_len 0.167803 55888899 getc_unlocked_fixed_len 0.168392 55888899 // getc_unlocked_fixed_len 0.168808 55888899 getc_unlocked_fixed_len 0.169387 55888899 // getc_unlocked_fixed_len 0.168980 55888899 getc_unlocked_fixed_len 0.178484 55888899 // getc_unlocked_fixed_len 0.176187 55888899 getc_unlocked_fixed_len 0.182793 55888899 // getc_unlocked_psb 0.238986 55888899 getc_unlocked_psb 0.293240 55888899 // getc_unlocked_psb 0.241325 55888899 getc_unlocked_psb 0.298449 55888899 // getc_unlocked_psb 0.246466 55888899 getc_unlocked_psb 0.298508 55888899 // getc_unlocked_psb 0.247592 55888899 getc_unlocked_psb 0.301125 55888899 // getc_unlocked_psb 0.248112 55888899 mmap_psb 0.313239 55888899 // mmap_psb 0.250021 55888899 mmap_psb 0.315061 55888899 // mmap_psb 0.254118 55888899 mmap_psb 0.315517 55888899 // mmap_psb 0.257428 55888899 mmap_psb 0.316790 55888899 // mmap_psb 0.261807 55888899 mmap_psb 0.320654 55888899 // mmap_psb 0.264367 55888899 getc_unlocked_psb 0.326494 55888899 // pfr_psb 0.760035 55888900 pfr_psb 0.417141 55888899 // pfr_psb 0.765121 55888900 pfr_psb 0.439269 55888899 // pfr_psb 0.768731 55888900 pfr_psb 0.439342 55888899 // pfr_psb 0.771937 55888900 pfr_psb 0.447218 55888899 // pfr_psb 0.780460 55888900 pfr_psb 0.453839 55888899 // fgetc_fixed_len 2.516459 55888899 fgetc_psb 2.476543 55888899 // fgetc_fixed_len 2.522877 55888899 fgetc_psb 2.477130 55888899 // fgetc_fixed_len 2.587373 55888899 fgetc_psb 2.484007 55888899 // fgetc_psb 2.590090 55888899 fgetc_psb 2.484495 55888899 // fgetc_psb 2.590536 55888899 fgetc_fixed_len 2.493730 55888899 // fgetc_fixed_len 2.608356 55888899 fgetc_fixed_len 2.528333 55888899 // fgetc_psb 2.623930 55888899 fgetc_fixed_len 2.533535 55888899 // fgetc_fixed_len 2.624310 55888899 fgetc_fixed_len 2.555377 55888899 // fgetc_psb 2.637269 55888899 fgetc_fixed_len 2.736391 55888899 // fgetc_psb 2.743828 55888899 // $ mlr --opprint cat then stats1 -a min,max,stddev,mean -f t -g type then sort -n t_mean x // type t_min t_max t_stddev t_mean // getdelim 0.118618 0.127039 0.003232 0.122765 // getc_unlocked_fixed_len 0.167563 0.176187 0.003585 0.169868 // getc_unlocked_psb 0.238986 0.248112 0.004091 0.244496 // mmap_psb 0.250021 0.264367 0.005768 0.257548 // pfr_psb 0.760035 0.780460 0.007667 0.769257 // fgetc_fixed_len 2.516459 2.624310 0.049478 2.571875 // fgetc_psb 2.590090 2.680364 0.037489 2.624438 // type t_min t_max t_stddev t_mean // getdelim 0.118057 0.123099 0.002271 0.120400 // getc_unlocked_fixed_len 0.168109 0.182793 0.006768 0.173433 // getc_unlocked_psb 0.293240 0.326494 0.013134 0.303563 // mmap_psb 0.313239 0.320654 0.002771 0.316252 // pfr_psb 0.417141 0.453839 0.013830 0.439362 // fgetc_psb 2.476543 2.743828 0.117803 2.533201 // fgetc_fixed_len 2.493730 2.736391 0.095892 2.569473 // ---------------------------------------------------------------- // Analysis: // * getdelim is good; fatal flaw is single-char line-terminator // o maybe i could cobble up a line-stacked iterator which // consumes usually 1, sometimes (double-quote case) multiple // delim lines to make up the data for a given record // * as before, maybe a 5-10% improvement mmap over stdio. // worth doing as a second-level refinement. // * getc_unlocked vs. fgetc, no-brainer for this single-threaded code. // * string-builder is a little than fixed-length malloc, as expected // -- it's adding value. // ! peek_file_reader is where the optimization opportunities are