From 23d846e1c2eb891e793aa9e7e0b3d406f5068986 Mon Sep 17 00:00:00 2001 From: Alex Wulms Date: Sat, 2 Jan 2010 14:26:01 +0100 Subject: [PATCH] More performance optimizations in crcsync --- ccan/crcsync/crcsync.c | 111 +++++++++++---------- ...crcsync_trailing_bytes_merged_in_last_block.ods | Bin 13464 -> 14235 bytes 2 files changed, 60 insertions(+), 51 deletions(-) rewrite crccache/doc/benchmark_crcsync_trailing_bytes_merged_in_last_block.ods (92%) diff --git a/ccan/crcsync/crcsync.c b/ccan/crcsync/crcsync.c index 80d802426..6df7836cd 100644 --- a/ccan/crcsync/crcsync.c +++ b/ccan/crcsync/crcsync.c @@ -35,7 +35,13 @@ struct crc_hash_record { int value; }; +struct crc_hash_table { + unsigned mask; + struct crc_hash_record *records; +}; + struct crc_context { + const uint64_t *crc64_iso_tab; size_t normal_block_size; size_t tail_block_size; size_t max_block_size; @@ -61,8 +67,7 @@ struct crc_context { uint64_t tail_crc; /* This doesn't count the last CRC. */ unsigned int num_crcs; - unsigned crc_hashtable_size; - struct crc_hash_record *crc_hashtable; + struct crc_hash_table crcs; }; static uint64_t crc64_over_zeros(const uint64_t *crc64_iso_tab, uint64_t crc, int size) @@ -76,9 +81,8 @@ static uint64_t crc64_over_zeros(const uint64_t *crc64_iso_tab, uint64_t crc, in /* Initialize one table that is used to calculate how the crc changes when we take a give * char out of the crc'd area. This function is to be used when there is no tail block */ -static void init_uncrc_tab(uint64_t uncrc_tab[], unsigned int wsize) +static void init_uncrc_tab(const uint64_t *crc64_iso_tab, uint64_t uncrc_tab[], unsigned int wsize) { - const uint64_t *crc64_iso_tab = crc64_iso_table(); unsigned int i; uint64_t part_crc; @@ -91,9 +95,8 @@ static void init_uncrc_tab(uint64_t uncrc_tab[], unsigned int wsize) * char out of the crc'd area. This function is to be used when there is a tail block. * The function initializes one table for the tail block and another one for the normal block. * You must pass the params for the smalles block first followed by the params for the largest block */ -static void init_uncrc_tabs(uint64_t small_uncrc_tab[], unsigned int small_wsize, uint64_t large_uncrc_tab[], unsigned int large_wsize) +static void init_uncrc_tabs(const uint64_t *crc64_iso_tab, uint64_t small_uncrc_tab[], unsigned int small_wsize, uint64_t large_uncrc_tab[], unsigned int large_wsize) { - const uint64_t *crc64_iso_tab = crc64_iso_table(); unsigned int i; unsigned int delta_wsize = large_wsize - small_wsize; uint64_t small_part_crc; @@ -110,32 +113,33 @@ static void init_uncrc_tabs(uint64_t small_uncrc_tab[], unsigned int small_wsize } } -static void crc_hashtable_put(struct crc_hash_record *crc_hashtable, unsigned hash_tmask, uint64_t crc, int value) +static unsigned crc_hashtable_getpos(const struct crc_hash_table *crcs, uint64_t crc) { - unsigned pos = (crc >> 32) & hash_tmask; // Use highest 32 bits of the checksum as start position + unsigned mask = crcs->mask; + struct crc_hash_record *records = crcs->records; + unsigned pos = (crc >> 32) & mask; // Use highest 32 bits of the checksum as start position unsigned step = (1 + (crc & 0x1e)); // Step with an odd-number of steps, exact value depends on crc lowest 5 bits - while (crc_hashtable[pos].value != -1 && crc_hashtable[pos].crc != crc) + while (records[pos].value != -1 && records[pos].crc != crc) { // This position is already taken by another crc record. Go to next position - pos = (pos + step) & hash_tmask; + pos = (pos + step) & mask; } - crc_hashtable[pos].value = value; - crc_hashtable[pos].crc = crc; + return pos; } -static int crc_hashtable_get(const struct crc_hash_record crc_hashtable[], unsigned hash_tmask, uint64_t crc) +static void crc_hashtable_put(struct crc_hash_table *crcs, uint64_t crc, int value) { - unsigned pos = (crc >> 32) & hash_tmask; - unsigned step = (1 + (crc & 0x1e)); - - while (crc_hashtable[pos].value != -1 && crc_hashtable[pos].crc != crc) - { - // This position is taken by another CRC record. Go to next position - pos = (pos + step) & hash_tmask; - } + unsigned pos = crc_hashtable_getpos(crcs, crc); + crcs->records[pos].value = value; + crcs->records[pos].crc = crc; +} + +static int crc_hashtable_get(const struct crc_hash_table *crcs, uint64_t crc) +{ + unsigned pos = crc_hashtable_getpos(crcs, crc); // Found an empty position (with value -1) or found the entry for the requested CRC - return crc_hashtable[pos].value; + return crcs->records[pos].value; } struct crc_context *crc_context_new(size_t normal_block_size, unsigned crcbits, @@ -150,6 +154,7 @@ struct crc_context *crc_context_new(size_t normal_block_size, unsigned crcbits, ctx = malloc(sizeof(*ctx) + sizeof(crc[0])*num_crcs); if (ctx) { + ctx->crc64_iso_tab = crc64_iso_table(); ctx->normal_block_size = normal_block_size; if (tail_block_size == normal_block_size) { @@ -163,20 +168,21 @@ struct crc_context *crc_context_new(size_t normal_block_size, unsigned crcbits, ctx->crcmask = mask_of(crcbits); ctx->num_crcs = num_crcs; - ctx->crc_hashtable_size = 4; - while (ctx->crc_hashtable_size < 2*num_crcs) + unsigned crc_hashtable_size = 4; + while (crc_hashtable_size < 2*num_crcs) { - ctx->crc_hashtable_size <<= 1; + crc_hashtable_size <<= 1; } - ctx->crc_hashtable = malloc(sizeof(struct crc_hash_record)*ctx->crc_hashtable_size); + ctx->crcs.mask = crc_hashtable_size-1; + ctx->crcs.records = malloc(sizeof(struct crc_hash_record)*crc_hashtable_size); unsigned cnt; - for (cnt=0; cnt != ctx->crc_hashtable_size; cnt++) + for (cnt=0; cnt != crc_hashtable_size; cnt++) { - ctx->crc_hashtable[cnt].value = -1; + ctx->crcs.records[cnt].value = -1; } for (cnt=0; cnt != num_crcs; cnt++) { - crc_hashtable_put(ctx->crc_hashtable, ctx->crc_hashtable_size-1, crc[cnt], cnt); + crc_hashtable_put(&ctx->crcs, crc[cnt], cnt); } // memcpy(ctx->crc, crc, sizeof(crc[0])*num_crcs); ctx->running_normal_crc = 0; @@ -186,13 +192,13 @@ struct crc_context *crc_context_new(size_t normal_block_size, unsigned crcbits, if (tail_block_size) { if (tail_block_size < normal_block_size) - init_uncrc_tabs(ctx->tail_uncrc_tab, tail_block_size, ctx->normal_uncrc_tab, normal_block_size); + init_uncrc_tabs(ctx->crc64_iso_tab, ctx->tail_uncrc_tab, tail_block_size, ctx->normal_uncrc_tab, normal_block_size); else - init_uncrc_tabs(ctx->normal_uncrc_tab, normal_block_size, ctx->tail_uncrc_tab, tail_block_size); + init_uncrc_tabs(ctx->crc64_iso_tab, ctx->normal_uncrc_tab, normal_block_size, ctx->tail_uncrc_tab, tail_block_size); } else { - init_uncrc_tab(ctx->normal_uncrc_tab, normal_block_size); + init_uncrc_tab(ctx->crc64_iso_tab, ctx->normal_uncrc_tab, normal_block_size); } ctx->buffer = malloc(ctx->max_block_size); @@ -212,7 +218,7 @@ struct crc_context *crc_context_new(size_t normal_block_size, unsigned crcbits, /* Only invoke once you have read enough literal bytes! */ static int crc_matches(const struct crc_context *ctx) { - return crc_hashtable_get(ctx->crc_hashtable,ctx->crc_hashtable_size-1, ctx->running_normal_crc & ctx->crcmask); + return crc_hashtable_get(&ctx->crcs, ctx->running_normal_crc & ctx->crcmask); } /* Return -1 or index of tail crc */ @@ -222,9 +228,9 @@ static int tail_matches(const struct crc_context *ctx) return (ctx->running_tail_crc & ctx->crcmask) == ctx->tail_crc ? ctx->num_crcs : -1; } -static uint64_t crc_add_byte(uint64_t crc, uint8_t newbyte) +static uint64_t crc_add_byte(const uint64_t *crc64_iso_tab, uint64_t crc, uint8_t newbyte) { - return crc64_iso(crc, &newbyte, 1); + return crc64_iso_tab[(crc ^ newbyte) & 0xFFL] ^ (crc >> 8); } static uint64_t crc_remove_byte(uint64_t crc, uint8_t oldbyte, @@ -233,13 +239,14 @@ static uint64_t crc_remove_byte(uint64_t crc, uint8_t oldbyte, return crc ^ uncrc_tab[oldbyte]; } -static uint64_t crc_roll(uint64_t crc, uint8_t oldbyte, uint8_t newbyte, +static uint64_t crc_roll(const uint64_t *crc64_iso_tab, uint64_t crc, uint8_t oldbyte, uint8_t newbyte, const uint64_t uncrc_tab[]) { - return crc_add_byte(crc_remove_byte(crc, oldbyte, uncrc_tab), newbyte); + return crc_add_byte(crc64_iso_tab, crc_remove_byte(crc, oldbyte, uncrc_tab), newbyte); } enum RB_PHASE { non_rolling, only_tail_rolling, only_normal_rolling, both_rolling }; +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) size_t crc_read_block(struct crc_context *ctx, long *result, const void *buf, size_t buflen) @@ -282,12 +289,12 @@ size_t crc_read_block(struct crc_context *ctx, long *result, switch (phase) { case non_rolling: - while (consumed != buflen) { - consumed++; - ctx->literal_bytes++; - ctx->running_tail_crc = ctx->running_normal_crc = - crc_add_byte(ctx->running_normal_crc, *get_pos++); + size_t nbytes = MIN(buflen - consumed, MIN(ctx->normal_block_size - ctx->literal_bytes, ctx->tail_block_size - ctx->literal_bytes)); + ctx->running_tail_crc = ctx->running_normal_crc = crc64_iso(ctx->running_normal_crc, get_pos, nbytes); + consumed += nbytes; + ctx->literal_bytes += nbytes; + get_pos += nbytes; if (ctx->literal_bytes == ctx->normal_block_size) { /* Reached the end of a normal block. Check CRC and start rolling the CRC at next iteration */ @@ -295,16 +302,14 @@ size_t crc_read_block(struct crc_context *ctx, long *result, break; normal_old = (ctx->buffer_size != 0) ? ctx->buffer : buf; phase = only_normal_rolling; - break; } - if (ctx->literal_bytes == ctx->tail_block_size) { + else if (ctx->literal_bytes == ctx->tail_block_size) { /* Reached the end of a tail block. Check tail CRC and start rolling the CRC at next iteration */ if ((crcmatch = tail_matches(ctx)) != -1) break; tail_old = (ctx->buffer_size != 0) ? ctx->buffer : buf; phase = only_tail_rolling; - break; } } break; @@ -313,7 +318,8 @@ size_t crc_read_block(struct crc_context *ctx, long *result, { consumed++; ctx->literal_bytes++; - ctx->running_normal_crc = crc_roll(ctx->running_normal_crc, + ctx->running_normal_crc = crc_roll(ctx->crc64_iso_tab, + ctx->running_normal_crc, *normal_old, *get_pos, ctx->normal_uncrc_tab); if ((crcmatch = crc_matches(ctx)) != -1) @@ -322,7 +328,7 @@ size_t crc_read_block(struct crc_context *ctx, long *result, if (++normal_old == ctx->buffer_end) normal_old = buf; if (ctx->tail_block_size) { - ctx->running_tail_crc = crc_add_byte(ctx->running_tail_crc, *get_pos++); + ctx->running_tail_crc = crc_add_byte(ctx->crc64_iso_tab, ctx->running_tail_crc, *get_pos++); if (ctx->literal_bytes == ctx->tail_block_size) { if ((crcmatch = tail_matches(ctx)) != -1) @@ -341,7 +347,8 @@ size_t crc_read_block(struct crc_context *ctx, long *result, { consumed++; ctx->literal_bytes++; - ctx->running_tail_crc = crc_roll(ctx->running_tail_crc, + ctx->running_tail_crc = crc_roll(ctx->crc64_iso_tab, + ctx->running_tail_crc, *tail_old, *get_pos, ctx->tail_uncrc_tab); if ((crcmatch = tail_matches(ctx)) != -1) @@ -349,7 +356,7 @@ size_t crc_read_block(struct crc_context *ctx, long *result, /* Advance trailing pointer for tail CRC */ if (++tail_old == ctx->buffer_end) tail_old = buf; - ctx->running_normal_crc = crc_add_byte(ctx->running_normal_crc, *get_pos++); + ctx->running_normal_crc = crc_add_byte(ctx->crc64_iso_tab, ctx->running_normal_crc, *get_pos++); if (ctx->literal_bytes == ctx->normal_block_size) { if ((crcmatch = crc_matches(ctx)) != -1) @@ -364,7 +371,8 @@ size_t crc_read_block(struct crc_context *ctx, long *result, while (consumed != buflen) { consumed++; - ctx->running_normal_crc = crc_roll(ctx->running_normal_crc, + ctx->running_normal_crc = crc_roll(ctx->crc64_iso_tab, + ctx->running_normal_crc, *normal_old, *get_pos, ctx->normal_uncrc_tab); if ((crcmatch = crc_matches(ctx)) != -1) @@ -372,7 +380,8 @@ size_t crc_read_block(struct crc_context *ctx, long *result, /* Advance trailing pointer for normal CRC */ if (++normal_old == ctx->buffer_end) normal_old = buf; - ctx->running_tail_crc = crc_roll(ctx->running_tail_crc, + ctx->running_tail_crc = crc_roll(ctx->crc64_iso_tab, + ctx->running_tail_crc, *tail_old, *get_pos, ctx->tail_uncrc_tab); if ((crcmatch = tail_matches(ctx)) != -1) diff --git a/crccache/doc/benchmark_crcsync_trailing_bytes_merged_in_last_block.ods b/crccache/doc/benchmark_crcsync_trailing_bytes_merged_in_last_block.ods dissimilarity index 92% index 7c556ab20a005b97ad9121d7ec4323257c39ff2a..7cd1ab183bb19bb15cc6bb2df0fd636378a4312b 100644 GIT binary patch delta 11746 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 qpc8hQf2#VQD~R~Ns^VkB(^IQ@ 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 -- 2.11.4.GIT