From 2a46a60510948b5005d62025fbba307a0323ed8d Mon Sep 17 00:00:00 2001 From: Bill Wendling Date: Wed, 15 May 2013 21:31:22 +0000 Subject: Try to improve performance by using a read/write buffer instead of I/O. The calls to fwrite/fread can be very expensive. GCC avoids this by using a buffer to read and write from the file, thus limiting the number of fwrite/fread calls. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@181924 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/profile/GCDAProfiling.c | 160 ++++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 52 deletions(-) (limited to 'lib/profile/GCDAProfiling.c') diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c index 653f641f7..53ecff438 100644 --- a/lib/profile/GCDAProfiling.c +++ b/lib/profile/GCDAProfiling.c @@ -47,6 +47,15 @@ typedef unsigned int uint64_t; */ static FILE *output_file = NULL; +/* + * Buffer that we write things into. + */ +#define WRITE_BUFFER_SIZE (1 << 12) +static char write_buffer[WRITE_BUFFER_SIZE]; +static int64_t cur_file_pos = 0; +static uint32_t cur_offset = 0; +static int new_file = 0; + /* * A list of functions to write out the data. */ @@ -57,8 +66,8 @@ struct writeout_fn_node { struct writeout_fn_node *next; }; -struct writeout_fn_node *writeout_fn_head = NULL; -struct writeout_fn_node *writeout_fn_tail = NULL; +static struct writeout_fn_node *writeout_fn_head = NULL; +static struct writeout_fn_node *writeout_fn_tail = NULL; /* * A list of flush functions that our __gcov_flush() function should call. @@ -70,18 +79,41 @@ struct flush_fn_node { struct flush_fn_node *next; }; -struct flush_fn_node *flush_fn_head = NULL; -struct flush_fn_node *flush_fn_tail = NULL; +static struct flush_fn_node *flush_fn_head = NULL; +static struct flush_fn_node *flush_fn_tail = NULL; + +static void flush_buffer() { + /* Flush the buffer to file. */ + fwrite(write_buffer, cur_offset, 1, output_file); + + if (new_file) { + cur_offset = 0; + return; + } + + /* Read in more of the file. */ + fread(write_buffer,1, WRITE_BUFFER_SIZE, output_file); + fseek(output_file, cur_file_pos, SEEK_SET); -static void write_int32(uint32_t i) { - fwrite(&i, 4, 1, output_file); + cur_offset = 0; } -static uint64_t write_from_buffer(uint64_t *buffer, size_t size) { - if (fwrite(buffer, 8, size, output_file) != size) - return (uint64_t)-1; +static void write_bytes(const char *s, size_t len) { + if (cur_offset + len > WRITE_BUFFER_SIZE) + flush_buffer(); + + cur_file_pos += len; + + for (; len > 0; --len) + write_buffer[cur_offset++] = *s++; +} - return 0; +static void write_32bit_value(uint32_t i) { + write_bytes((char*)&i, 4); +} + +static void write_64bit_value(uint64_t i) { + write_bytes((char*)&i, 8); } static uint32_t length_of_string(const char *s) { @@ -90,25 +122,39 @@ static uint32_t length_of_string(const char *s) { static void write_string(const char *s) { uint32_t len = length_of_string(s); - write_int32(len); - fwrite(s, strlen(s), 1, output_file); - fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file); + write_32bit_value(len); + write_bytes(s, strlen(s)); + write_bytes("\0\0\0\0", 4 - (strlen(s) % 4)); } -static uint32_t read_int32() { - uint32_t tmp; +static uint32_t read_32bit_value() { + uint32_t val; + + if (cur_offset + 4 > WRITE_BUFFER_SIZE) + flush_buffer(); - if (fread(&tmp, 1, 4, output_file) != 4) + if (new_file) return (uint32_t)-1; - return tmp; + val = *(uint32_t*)&write_buffer[cur_offset]; + cur_file_pos += 4; + cur_offset += 4; + return val; } -static uint64_t read_into_buffer(uint64_t *buffer, size_t size) { - if (fread(buffer, 8, size, output_file) != size) - return (uint64_t)-1; +static uint64_t read_64bit_value() { + uint64_t val; - return 0; + if (cur_offset + 8 > WRITE_BUFFER_SIZE) + flush_buffer(); + + if (new_file) + return (uint32_t)-1; + + val = *(uint64_t*)&write_buffer[cur_offset]; + cur_file_pos += 8; + cur_offset += 8; + return val; } static char *mangle_filename(const char *orig_filename) { @@ -118,13 +164,13 @@ static char *mangle_filename(const char *orig_filename) { int level = 0; const char *fname = orig_filename, *ptr = NULL; const char *prefix = getenv("GCOV_PREFIX"); - const char *tmp = getenv("GCOV_PREFIX_STRIP"); + const char *prefix_strip_str = getenv("GCOV_PREFIX_STRIP"); if (!prefix) return strdup(orig_filename); - if (tmp) { - prefix_strip = atoi(tmp); + if (prefix_strip_str) { + prefix_strip = atoi(prefix_strip_str); /* Negative GCOV_PREFIX_STRIP values are ignored */ if (prefix_strip < 0) @@ -180,6 +226,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { if (!output_file) { /* Try opening the file, creating it if necessary. */ + new_file = 1; output_file = fopen(filename, "w+b"); if (!output_file) { /* Try creating the directories first then opening the file. */ @@ -194,10 +241,20 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4]) { } } + setbuf(output_file, 0); + + /* Initialize the write buffer. */ + memset(write_buffer, 0, WRITE_BUFFER_SIZE); + fread(write_buffer, 1, WRITE_BUFFER_SIZE, output_file); + fseek(output_file, 0L, SEEK_SET); + cur_file_pos = 0; + cur_offset = 0; + /* gcda file, version, stamp LLVM. */ - fwrite("adcg", 4, 1, output_file); - fwrite(version, 4, 1, output_file); - fwrite("MVLL", 4, 1, output_file); + write_bytes("adcg", 4); + write_bytes(version, 4); + write_bytes("MVLL", 4); + free(filename); #ifdef DEBUG_GCDAPROFILING @@ -233,6 +290,7 @@ void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, void llvm_gcda_emit_function(uint32_t ident, const char *function_name, uint8_t use_extra_checksum) { uint32_t len = 2; + if (use_extra_checksum) len++; #ifdef DEBUG_GCDAPROFILING @@ -242,14 +300,14 @@ void llvm_gcda_emit_function(uint32_t ident, const char *function_name, if (!output_file) return; /* function tag */ - fwrite("\0\0\0\1", 4, 1, output_file); + write_bytes("\0\0\0\1", 4); if (function_name) len += 1 + length_of_string(function_name); - write_int32(len); - write_int32(ident); - write_int32(0); + write_32bit_value(len); + write_32bit_value(ident); + write_32bit_value(0); if (use_extra_checksum) - write_int32(0); + write_32bit_value(0); if (function_name) write_string(function_name); } @@ -257,13 +315,13 @@ void llvm_gcda_emit_function(uint32_t ident, const char *function_name, void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { uint32_t i; uint64_t *old_ctrs = NULL; + int64_t save_cur_file_pos = cur_file_pos; uint32_t val = 0; - long pos = 0; if (!output_file) return; - pos = ftell(output_file); - val = read_int32(); + flush_buffer(); + val = read_32bit_value(); if (val != (uint32_t)-1) { /* There are counters present in the file. Merge them. */ @@ -272,33 +330,30 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { return; } - val = read_int32(); + val = read_32bit_value(); if (val == (uint32_t)-1 || val / 2 != num_counters) { fprintf(stderr, "profiling:invalid number of counters (%d)\n", val); return; } old_ctrs = malloc(sizeof(uint64_t) * num_counters); + for (i = 0; i < num_counters; ++i) + old_ctrs[i] = read_64bit_value(); - if (read_into_buffer(old_ctrs, num_counters) == (uint64_t)-1) { - fprintf(stderr, "profiling:invalid number of counters (%d)\n", - num_counters); - return; - } - } + /* Reset the current buffer and file position. */ + memset(write_buffer, 0, WRITE_BUFFER_SIZE); + cur_file_pos = save_cur_file_pos; + cur_offset = 0; - /* Reset for writing. */ - fseek(output_file, pos, SEEK_SET); + fseek(output_file, cur_file_pos, SEEK_SET); + } /* Counter #1 (arcs) tag */ - fwrite("\0\0\xa1\1", 4, 1, output_file); - write_int32(num_counters * 2); - for (i = 0; i < num_counters; ++i) + write_bytes("\0\0\xa1\1", 4); + write_32bit_value(num_counters * 2); + for (i = 0; i < num_counters; ++i) { counters[i] += (old_ctrs ? old_ctrs[i] : 0); - - if (write_from_buffer(counters, num_counters) == (uint64_t)-1) { - fprintf(stderr, "profiling:cannot write to output file\n"); - return; + write_64bit_value(counters[i]); } free(old_ctrs); @@ -313,7 +368,8 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { void llvm_gcda_end_file() { /* Write out EOF record. */ if (!output_file) return; - fwrite("\0\0\0\0\0\0\0\0", 8, 1, output_file); + write_bytes("\0\0\0\0\0\0\0\0", 8); + flush_buffer(); fclose(output_file); output_file = NULL; -- cgit v1.2.3