Module Name: src Committed By: joerg Date: Thu Jan 7 16:03:08 UTC 2021
Modified Files: src/tests/usr.bin/nbperf: t_nbperf.sh src/usr.bin/nbperf: graph2.c graph2.h graph3.c nbperf-bdz.c nbperf-chm.c nbperf-chm3.c nbperf.1 nbperf.c nbperf.h Removed Files: src/usr.bin/nbperf: graph3.h Log Message: Optimize nbperf - add fudge mode which gives a slightly slower hash function, but works almost always in the first iteration by avoiding degenerate edges - avoid keeping incidence lists around reducing the memory foot print by 30% - split edge processing from hashing as in the non-fudge case it is a reasonable costly part that often gets thrown away - merge graph2 and graph3 routines now that they are mostly the same To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/tests/usr.bin/nbperf/t_nbperf.sh cvs rdiff -u -r1.4 -r1.5 src/usr.bin/nbperf/graph2.c \ src/usr.bin/nbperf/graph3.c src/usr.bin/nbperf/nbperf.h cvs rdiff -u -r1.1 -r1.2 src/usr.bin/nbperf/graph2.h \ src/usr.bin/nbperf/nbperf-chm3.c cvs rdiff -u -r1.1 -r0 src/usr.bin/nbperf/graph3.h cvs rdiff -u -r1.9 -r1.10 src/usr.bin/nbperf/nbperf-bdz.c cvs rdiff -u -r1.3 -r1.4 src/usr.bin/nbperf/nbperf-chm.c cvs rdiff -u -r1.7 -r1.8 src/usr.bin/nbperf/nbperf.1 cvs rdiff -u -r1.5 -r1.6 src/usr.bin/nbperf/nbperf.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/usr.bin/nbperf/t_nbperf.sh diff -u src/tests/usr.bin/nbperf/t_nbperf.sh:1.3 src/tests/usr.bin/nbperf/t_nbperf.sh:1.4 --- src/tests/usr.bin/nbperf/t_nbperf.sh:1.3 Wed Apr 30 21:04:21 2014 +++ src/tests/usr.bin/nbperf/t_nbperf.sh Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -# $NetBSD: t_nbperf.sh,v 1.3 2014/04/30 21:04:21 joerg Exp $ +# $NetBSD: t_nbperf.sh,v 1.4 2021/01/07 16:03:08 joerg Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -27,7 +27,7 @@ cleanup() { - rm -f reference.txt hash.c hash.map testprog + rm -f reference.txt input.txt hash.c hash.map testprog } atf_test_case chm @@ -38,7 +38,7 @@ chm_head() atf_set "require.progs" "cc" } chm_body() -{ +{ for n in 4 32 128 1024 65536; do seq 0 $(($n - 1)) > reference.txt atf_check -o file:reference.txt \ @@ -54,6 +54,32 @@ chm_clean() cleanup } +atf_test_case chm_fudged +chm_fudged_head() +{ + atf_set "descr" "Checks chm algorithm with fudged hash" + atf_set "require.progs" "cc" +} +chm_fudged_body() +{ + seq 0 9 > reference.txt + seq 1 10 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm -f -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^=' hash.c +} +chm_fudged_clean() +{ + cleanup +} + atf_test_case chm3 chm3_head() { @@ -78,26 +104,102 @@ chm3_clean() cleanup } -atf_test_case bdz -bdz_head() +atf_test_case chm3_fudged +chm3_fudged_head() +{ + atf_set "descr" "Checks chm3 algorithm with fudged hash" + atf_set "require.progs" "cc" +} +chm3_fudged_body() +{ + seq 0 9 > reference.txt + seq 1 10 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm3 -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "chm3 -f -p" cat \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^= (' hash.c + atf_check -s exit:0 fgrep -q '^= 2' hash.c +} +chm3_fudged_clean() +{ + cleanup +} + +atf_test_case bpz +bpz_head() { - atf_set "descr" "Checks bdz algorithm" + atf_set "descr" "Checks bpz algorithm" atf_set "require.files" "/usr/share/dict/web2" atf_set "require.progs" "cc" } -bdz_body() -{ +bpz_body() +{ for n in 4 32 128 1024 65536 131072; do seq 0 $(($n - 1)) > reference.txt atf_check -o file:reference.txt \ - $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bdz "sort -n" \ + $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bpz "sort -n" \ $n $(atf_get_srcdir)/hash_driver.c atf_check -o file:hash.map \ - $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bdz cat \ + $(atf_get_srcdir)/h_nbperf /usr/share/dict/web2 bpz cat \ $n $(atf_get_srcdir)/hash_driver.c done } -bdz_clean() +bpz_clean() +{ + cleanup +} + +atf_test_case bpz_fudged +bpz_fudged_head() +{ + atf_set "descr" "Checks bpz algorithm with fudged hash" + atf_set "require.progs" "cc" +} +bpz_fudged_body() +{ + seq 0 9 > reference.txt + seq 1 10 > input.txt + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "bpz -p" "sort -n" \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:1 fgrep -q '^=' hash.c + + atf_check -o file:reference.txt \ + $(atf_get_srcdir)/h_nbperf input.txt "bpz -f -p" "sort -n" \ + 10 $(atf_get_srcdir)/hash_driver.c + atf_check -s exit:0 fgrep -q '^= (' hash.c + atf_check -s exit:0 fgrep -q '^= 2' hash.c +} +bpz_fudged_clean() +{ + cleanup +} + +atf_test_case handle_dup +handle_dup_head() +{ + atf_set "descr" "Checks different algorithms deal with duplicates" + atf_set "require.progs" "cc" +} +handle_dup_body() +{ + seq 0 9 > reference.txt + echo 0 >> reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a chm < reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a chm3 < reference.txt + atf_check -s exit:1 -e match:"nbperf: Duplicate keys detected" \ + nbperf -a bpz < reference.txt +} +handle_dup_clean() { cleanup } @@ -105,6 +207,10 @@ bdz_clean() atf_init_test_cases() { atf_add_test_case chm + atf_add_test_case chm_fudged atf_add_test_case chm3 - atf_add_test_case bdz + atf_add_test_case chm3_fudged + atf_add_test_case bpz + atf_add_test_case bpz_fudged + atf_add_test_case handle_dup } Index: src/usr.bin/nbperf/graph2.c diff -u src/usr.bin/nbperf/graph2.c:1.4 src/usr.bin/nbperf/graph2.c:1.5 --- src/usr.bin/nbperf/graph2.c:1.4 Fri Oct 21 23:47:11 2011 +++ src/usr.bin/nbperf/graph2.c Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: graph2.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */ +/* $NetBSD: graph2.c,v 1.5 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. @@ -36,7 +36,7 @@ #endif #include <sys/cdefs.h> -__RCSID("$NetBSD: graph2.c,v 1.4 2011/10/21 23:47:11 joerg Exp $"); +__RCSID("$NetBSD: graph2.c,v 1.5 2021/01/07 16:03:08 joerg Exp $"); #include <err.h> #include <inttypes.h> @@ -47,16 +47,14 @@ __RCSID("$NetBSD: graph2.c,v 1.4 2011/10 #include "nbperf.h" #include "graph2.h" -static const uint32_t unused = 0xffffffffU; - void -graph2_setup(struct graph2 *graph, uint32_t v, uint32_t e) +SIZED2(_setup)(struct SIZED(graph) *graph, uint32_t v, uint32_t e) { graph->v = v; graph->e = e; - graph->verts = calloc(sizeof(struct vertex2), v); - graph->edges = calloc(sizeof(struct edge2), e); + graph->verts = calloc(sizeof(*graph->verts), v); + graph->edges = calloc(sizeof(*graph->edges), e); graph->output_order = calloc(sizeof(uint32_t), e); if (graph->verts == NULL || graph->edges == NULL || @@ -65,7 +63,7 @@ graph2_setup(struct graph2 *graph, uint3 } void -graph2_free(struct graph2 *graph) +SIZED2(_free)(struct SIZED(graph) *graph) { free(graph->verts); free(graph->edges); @@ -76,136 +74,180 @@ graph2_free(struct graph2 *graph) graph->output_order = NULL; } +static struct nbperf *sorting_nbperf; +static struct SIZED(graph) *sorting_graph; +static int sorting_found; + +static int sorting_cmp(const void *a_, const void *b_) +{ + const uint32_t *a = a_, *b = b_; + int i; + const struct SIZED(edge) *ea = &sorting_graph->edges[*a], + *eb = &sorting_graph->edges[*b]; + for (i = 0; i < GRAPH_SIZE; ++i) { + if (ea->vertices[i] < eb->vertices[i]) + return -1; + if (ea->vertices[i] > eb->vertices[i]) + return 1; + } + if (sorting_nbperf->keylens[*a] < sorting_nbperf->keylens[*b]) + return -1; + if (sorting_nbperf->keylens[*a] > sorting_nbperf->keylens[*b]) + return 1; + i = memcmp(sorting_nbperf->keys[*a], sorting_nbperf->keys[*b], + sorting_nbperf->keylens[*a]); + if (i == 0) + sorting_found = 1; + return i; +} + static int -graph2_check_duplicates(struct nbperf *nbperf, struct graph2 *graph) +SIZED2(_check_duplicates)(struct nbperf *nbperf, struct SIZED(graph) *graph) { - struct vertex2 *v; - struct edge2 *e, *e2; - uint32_t i, j; + size_t i; + uint32_t *key_index = calloc(sizeof(*key_index), graph->e); - for (i = 0; i < graph->e; ++i) { - e = &graph->edges[i]; - v = &graph->verts[e->left]; - j = v->l_edge; - e2 = &graph->edges[j]; - for (;;) { - if (i < j && e->right == e2->right && - nbperf->keylens[i] == nbperf->keylens[j] && - memcmp(nbperf->keys[i], nbperf->keys[j], - nbperf->keylens[i]) == 0) { - nbperf->has_duplicates = 1; - return -1; - } - if (e2->l_next == unused) - break; - j = e2->l_next; - e2 = &graph->edges[j]; - } - } + if (key_index == NULL) + err(1, "malloc failed"); + for (i = 0; i < graph->e; ++i) + key_index[i] = i; + + sorting_nbperf = nbperf; + sorting_graph = graph; + sorting_found = 0; + qsort(key_index, graph->e, sizeof(*key_index), sorting_cmp); + if (sorting_found) + goto found_dups; + /* + * Any duplicate must have been found as part of the qsort, + * as it can't sort consecutive elements in the output without + * comparing them at least once. + */ + + free(key_index); return 0; + found_dups: + nbperf->has_duplicates = 1; + return -1; } -int -graph2_hash(struct nbperf *nbperf, struct graph2 *graph) +static inline void +SIZED2(_add_edge)(struct SIZED(graph) *graph, uint32_t edge) { - struct vertex2 *v; - uint32_t hashes[NBPERF_MAX_HASH_SIZE]; + struct SIZED(edge) *e = graph->edges + edge; + struct SIZED(vertex) *v; size_t i; - for (i = 0; i < graph->e; ++i) { - (*nbperf->compute_hash)(nbperf, - nbperf->keys[i], nbperf->keylens[i], hashes); - graph->edges[i].left = hashes[0] % graph->v; - graph->edges[i].right = hashes[1] % graph->v; - if (graph->edges[i].left == graph->edges[i].right) - return -1; + for (i = 0; i < GRAPH_SIZE; ++i) { + v = graph->verts + e->vertices[i]; + v->edges ^= edge; + ++v->degree; } +} - for (i = 0; i < graph->v; ++i) { - graph->verts[i].l_edge = unused; - graph->verts[i].r_edge = unused; - } +static inline void +SIZED2(_remove_edge)(struct SIZED(graph) *graph, uint32_t edge) +{ + struct SIZED(edge) *e = graph->edges + edge; + struct SIZED(vertex) *v; + size_t i; - for (i = 0; i < graph->e; ++i) { - v = &graph->verts[graph->edges[i].left]; - if (v->l_edge != unused) - graph->edges[v->l_edge].l_prev = i; - graph->edges[i].l_next = v->l_edge; - graph->edges[i].l_prev = unused; - v->l_edge = i; - - v = &graph->verts[graph->edges[i].right]; - if (v->r_edge != unused) - graph->edges[v->r_edge].r_prev = i; - graph->edges[i].r_next = v->r_edge; - graph->edges[i].r_prev = unused; - v->r_edge = i; - } - - if (nbperf->first_round) { - nbperf->first_round = 0; - return graph2_check_duplicates(nbperf, graph); + for (i = 0; i < GRAPH_SIZE; ++i) { + v = graph->verts + e->vertices[i]; + v->edges ^= edge; + --v->degree; } +} - return 0; +static inline void +SIZED2(_remove_vertex)(struct SIZED(graph) *graph, uint32_t vertex) +{ + struct SIZED(vertex) *v = graph->verts + vertex; + uint32_t e; + + if (v->degree == 1) { + e = v->edges; + graph->output_order[--graph->output_index] = e; + SIZED2(_remove_edge)(graph, e); + } } -static void -graph2_remove_vertex(struct graph2 *graph, struct vertex2 *v) +int +SIZED2(_hash)(struct nbperf *nbperf, struct SIZED(graph) *graph) { - struct edge2 *e; - struct vertex2 *v2; + struct SIZED(edge) *e; + uint32_t hashes[NBPERF_MAX_HASH_SIZE]; + size_t i, j; - for (;;) { - if (v->l_edge != unused && v->r_edge != unused) - break; - if (v->l_edge == unused && v->r_edge == unused) - break; - - if (v->l_edge != unused) { - e = &graph->edges[v->l_edge]; - if (e->l_next != unused) - break; - v->l_edge = unused; /* No other elements possible! */ - v2 = &graph->verts[e->right]; - if (e->r_prev == unused) - v2->r_edge = e->r_next; - else - graph->edges[e->r_prev].r_next = e->r_next; - if (e->r_next != unused) - graph->edges[e->r_next].r_prev = e->r_prev; - v = v2; - } else { - e = &graph->edges[v->r_edge]; - if (e->r_next != unused) - break; - v->r_edge = unused; /* No other elements possible! */ - v2 = &graph->verts[e->left]; - if (e->l_prev == unused) - v2->l_edge = e->l_next; - else - graph->edges[e->l_prev].l_next = e->l_next; - if (e->l_next != unused) - graph->edges[e->l_next].l_prev = e->l_prev; - v = v2; +#if GRAPH_SIZE == 2 + if (nbperf->allow_hash_fudging && (graph->v & 1) != 1) + errx(1, "vertex count must have lowest bit set"); +#else + if (nbperf->allow_hash_fudging && (graph->v & 3) != 3) + errx(1, "vertex count must have lowest 2 bits set"); +#endif + + + memset(graph->verts, 0, sizeof(*graph->verts) * graph->v); + graph->hash_fudge = 0; + + for (i = 0; i < graph->e; ++i) { + (*nbperf->compute_hash)(nbperf, + nbperf->keys[i], nbperf->keylens[i], hashes); + e = graph->edges + i; + for (j = 0; j < GRAPH_SIZE; ++j) { + e->vertices[j] = hashes[j] % graph->v; + if (j == 1 && e->vertices[0] == e->vertices[1]) { + if (!nbperf->allow_hash_fudging) + return -1; + e->vertices[1] ^= 1; /* toogle bit to differ */ + graph->hash_fudge |= 1; + } +#if GRAPH_SIZE == 3 + if (j == 2 && (e->vertices[0] == e->vertices[2] || + e->vertices[1] == e->vertices[2])) { + if (!nbperf->allow_hash_fudging) + return -1; + graph->hash_fudge |= 2; + e->vertices[2] ^= 1; + e->vertices[2] ^= 2 * (e->vertices[0] == e->vertices[2] || + e->vertices[1] == e->vertices[2]); + } +#endif } + } + + for (i = 0; i < graph->e; ++i) + SIZED2(_add_edge)(graph, i); - graph->output_order[--graph->output_index] = e - graph->edges; + if (nbperf->check_duplicates) { + nbperf->check_duplicates = 0; + return SIZED2(_check_duplicates)(nbperf, graph); } + + return 0; } int -graph2_output_order(struct graph2 *graph) +SIZED2(_output_order)(struct SIZED(graph) *graph) { - size_t i; + size_t i, j; + struct SIZED(edge) *e2; graph->output_index = graph->e; for (i = 0; i < graph->v; ++i) - graph2_remove_vertex(graph, &graph->verts[i]); + SIZED2(_remove_vertex)(graph, i); - if (graph->output_index != 0) + for (i = graph->e; graph->output_index > 0 && i > graph->output_index;) { + e2 = graph->edges + graph->output_order[--i]; + for (j = 0; j < GRAPH_SIZE; ++j) + SIZED2(_remove_vertex)(graph, e2->vertices[j]); + } + + if (graph->output_index != 0) { return -1; + } return 0; } Index: src/usr.bin/nbperf/graph3.c diff -u src/usr.bin/nbperf/graph3.c:1.4 src/usr.bin/nbperf/graph3.c:1.5 --- src/usr.bin/nbperf/graph3.c:1.4 Fri Oct 21 23:47:11 2011 +++ src/usr.bin/nbperf/graph3.c Thu Jan 7 16:03:08 2021 @@ -1,250 +1,4 @@ -/* $NetBSD: graph3.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */ -/*- - * Copyright (c) 2009 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Joerg Sonnenberger. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* $NetBSD*/ -#if HAVE_NBTOOL_CONFIG_H -#include "nbtool_config.h" -#endif - -#include <sys/cdefs.h> -__RCSID("$NetBSD: graph3.c,v 1.4 2011/10/21 23:47:11 joerg Exp $"); - -#include <err.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "nbperf.h" -#include "graph3.h" - -static const uint32_t unused = 0xffffffffU; - -void -graph3_setup(struct graph3 *graph, uint32_t v, uint32_t e) -{ - graph->v = v; - graph->e = e; - - graph->verts = calloc(sizeof(struct vertex3), v); - graph->edges = calloc(sizeof(struct edge3), e); - graph->output_order = calloc(sizeof(uint32_t), e); - - if (graph->verts == NULL || graph->edges == NULL || - graph->output_order == NULL) - err(1, "malloc failed"); -} - -void -graph3_free(struct graph3 *graph) -{ - free(graph->verts); - free(graph->edges); - free(graph->output_order); - - graph->verts = NULL; - graph->edges = NULL; - graph->output_order = NULL; -} - -static int -graph3_check_duplicates(struct nbperf *nbperf, struct graph3 *graph) -{ - struct vertex3 *v; - struct edge3 *e, *e2; - uint32_t i, j; - - for (i = 0; i < graph->e; ++i) { - e = &graph->edges[i]; - v = &graph->verts[e->left]; - j = v->l_edge; - e2 = &graph->edges[j]; - for (;;) { - if (i < j && e->middle == e2->middle && - e->right == e2->right && - nbperf->keylens[i] == nbperf->keylens[j] && - memcmp(nbperf->keys[i], nbperf->keys[j], - nbperf->keylens[i]) == 0) { - nbperf->has_duplicates = 1; - return -1; - } - if (e2->l_next == unused) - break; - j = e2->l_next; - e2 = &graph->edges[j]; - } - } - return 0; -} - -int -graph3_hash(struct nbperf *nbperf, struct graph3 *graph) -{ - struct vertex3 *v; - uint32_t hashes[NBPERF_MAX_HASH_SIZE]; - size_t i; - - for (i = 0; i < graph->e; ++i) { - (*nbperf->compute_hash)(nbperf, - nbperf->keys[i], nbperf->keylens[i], hashes); - graph->edges[i].left = hashes[0] % graph->v; - graph->edges[i].middle = hashes[1] % graph->v; - graph->edges[i].right = hashes[2] % graph->v; - if (graph->edges[i].left == graph->edges[i].middle) - return -1; - if (graph->edges[i].left == graph->edges[i].right) - return -1; - if (graph->edges[i].middle == graph->edges[i].right) - return -1; - } - - for (i = 0; i < graph->v; ++i) { - graph->verts[i].l_edge = unused; - graph->verts[i].m_edge = unused; - graph->verts[i].r_edge = unused; - } - - for (i = 0; i < graph->e; ++i) { - v = &graph->verts[graph->edges[i].left]; - if (v->l_edge != unused) - graph->edges[v->l_edge].l_prev = i; - graph->edges[i].l_next = v->l_edge; - graph->edges[i].l_prev = unused; - v->l_edge = i; - - v = &graph->verts[graph->edges[i].middle]; - if (v->m_edge != unused) - graph->edges[v->m_edge].m_prev = i; - graph->edges[i].m_next = v->m_edge; - graph->edges[i].m_prev = unused; - v->m_edge = i; - - v = &graph->verts[graph->edges[i].right]; - if (v->r_edge != unused) - graph->edges[v->r_edge].r_prev = i; - graph->edges[i].r_next = v->r_edge; - graph->edges[i].r_prev = unused; - v->r_edge = i; - } - - if (nbperf->first_round) { - nbperf->first_round = 0; - return graph3_check_duplicates(nbperf, graph); - } - - return 0; -} - -static void -graph3_remove_vertex(struct graph3 *graph, struct vertex3 *v) -{ - struct edge3 *e; - struct vertex3 *vl, *vm, *vr; - - if (v->l_edge != unused && v->m_edge != unused) - return; - if (v->l_edge != unused && v->r_edge != unused) - return; - if (v->m_edge != unused && v->r_edge != unused) - return; - if (v->l_edge == unused && v->m_edge == unused && v->r_edge == unused) - return; - - if (v->l_edge != unused) { - e = &graph->edges[v->l_edge]; - if (e->l_next != unused) - return; - } else if (v->m_edge != unused) { - e = &graph->edges[v->m_edge]; - if (e->m_next != unused) - return; - } else { - if (v->r_edge == unused) - abort(); - e = &graph->edges[v->r_edge]; - if (e->r_next != unused) - return; - } - - graph->output_order[--graph->output_index] = e - graph->edges; - - vl = &graph->verts[e->left]; - vm = &graph->verts[e->middle]; - vr = &graph->verts[e->right]; - - if (e->l_prev == unused) - vl->l_edge = e->l_next; - else - graph->edges[e->l_prev].l_next = e->l_next; - if (e->l_next != unused) - graph->edges[e->l_next].l_prev = e->l_prev; - - if (e->m_prev == unused) - vm->m_edge = e->m_next; - else - graph->edges[e->m_prev].m_next = e->m_next; - if (e->m_next != unused) - graph->edges[e->m_next].m_prev = e->m_prev; - - if (e->r_prev == unused) - vr->r_edge = e->r_next; - else - graph->edges[e->r_prev].r_next = e->r_next; - if (e->r_next != unused) - graph->edges[e->r_next].r_prev = e->r_prev; -} - -int -graph3_output_order(struct graph3 *graph) -{ - struct edge3 *e; - size_t i; - - graph->output_index = graph->e; - - for (i = 0; i < graph->v; ++i) - graph3_remove_vertex(graph, &graph->verts[i]); - - for (i = graph->e; i > 0 && i > graph->output_index;) { - --i; - e = &graph->edges[graph->output_order[i]]; - - graph3_remove_vertex(graph, &graph->verts[e->left]); - graph3_remove_vertex(graph, &graph->verts[e->middle]); - graph3_remove_vertex(graph, &graph->verts[e->right]); - } - - if (graph->output_index != 0) - return -1; - - return 0; -} +#define GRAPH_SIZE 3 +#include "graph2.c" Index: src/usr.bin/nbperf/nbperf.h diff -u src/usr.bin/nbperf/nbperf.h:1.4 src/usr.bin/nbperf/nbperf.h:1.5 --- src/usr.bin/nbperf/nbperf.h:1.4 Thu Jan 31 16:32:02 2013 +++ src/usr.bin/nbperf/nbperf.h Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nbperf.h,v 1.4 2013/01/31 16:32:02 joerg Exp $ */ +/* $NetBSD: nbperf.h,v 1.5 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. @@ -38,10 +38,11 @@ struct nbperf { FILE *map_output; const char *hash_name; int static_hash; + int allow_hash_fudging; size_t n; const void * __restrict * keys; const size_t *keylens; - int first_round, has_duplicates; + int check_duplicates, has_duplicates; double c; Index: src/usr.bin/nbperf/graph2.h diff -u src/usr.bin/nbperf/graph2.h:1.1 src/usr.bin/nbperf/graph2.h:1.2 --- src/usr.bin/nbperf/graph2.h:1.1 Sat Aug 15 16:21:05 2009 +++ src/usr.bin/nbperf/graph2.h Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: graph2.h,v 1.1 2009/08/15 16:21:05 joerg Exp $ */ +/* $NetBSD: graph2.h,v 1.2 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. @@ -32,32 +32,57 @@ */ /* - * Implementation of common 2-graph routines: - * - build a 2-graph with hash-pairs as edges - * - check a 2-graph for acyclicness and compute an output order + * Implementation of common 2/3-graph routines: + * - build a 2/3-graph with hash-pairs as edges + * - check a 2/3-graph for acyclicness and compute an output order +* + * For each vertex in the 2/3-graph, the incidence lists need to kept. + * Avoid storing the full list by just XORing the indices of the still + * incident edges and the number of such edges as that's all the peeling + * computation needs. This is inspired by: + * Cache-Oblivious Peeling of Random Hypergraphs by Djamal Belazzougui, + * Paolo Boldi, Giuseppe Ottaviano, Rossano Venturini, and Sebastiano + * Vigna. https://arxiv.org/abs/1312.0526 + * + * Unlike in the paper, we don't care about external storage and have + * the edge list at hand all the time. As such, no ordering is necessary + * and the vertices of the edge don't have to be copied. + * + * The core observation of the paper above is that for a degree of one, + * the incident edge can be obtained directly. */ -struct vertex2 { - uint32_t l_edge, r_edge; +#ifndef GRAPH_SIZE +#define GRAPH_SIZE 2 +#endif + +#define SIZED__(n, i) n ## i +#define SIZED_(n, i) SIZED__(n, i) +#define SIZED(n) SIZED_(n, GRAPH_SIZE) +#define SIZED2__(n, i, m) n ## i ## m +#define SIZED2_(n, i, m) SIZED2__(n, i, m) +#define SIZED2(n) SIZED2_(graph, GRAPH_SIZE, n) + +struct SIZED(vertex) { + uint32_t degree, edges; }; -struct edge2 { - uint32_t left, right; - uint32_t l_prev, l_next; - uint32_t r_prev, r_next; +struct SIZED(edge) { + uint32_t vertices[GRAPH_SIZE]; }; -struct graph2 { - struct vertex2 *verts; - struct edge2 *edges; +struct SIZED(graph) { + struct SIZED(vertex) *verts; + struct SIZED(edge) *edges; uint32_t output_index; uint32_t *output_order; uint8_t *visited; uint32_t e, v; + int hash_fudge; }; -void graph2_setup(struct graph2 *, uint32_t, uint32_t); -void graph2_free(struct graph2 *); +void SIZED2(_setup)(struct SIZED(graph) *, uint32_t, uint32_t); +void SIZED2(_free)(struct SIZED(graph) *); -int graph2_hash(struct nbperf *, struct graph2 *); -int graph2_output_order(struct graph2 *graph); +int SIZED2(_hash)(struct nbperf *, struct SIZED(graph) *); +int SIZED2(_output_order)(struct SIZED(graph) *graph); Index: src/usr.bin/nbperf/nbperf-chm3.c diff -u src/usr.bin/nbperf/nbperf-chm3.c:1.1 src/usr.bin/nbperf/nbperf-chm3.c:1.2 --- src/usr.bin/nbperf/nbperf-chm3.c:1.1 Sat Aug 15 16:21:05 2009 +++ src/usr.bin/nbperf/nbperf-chm3.c Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nbperf-chm3.c,v 1.1 2009/08/15 16:21:05 joerg Exp $ */ +/* $NetBSD: nbperf-chm3.c,v 1.2 2021/01/07 16:03:08 joerg Exp $ */ -#define BUILD_CHM3 +#define GRAPH_SIZE 3 #include "nbperf-chm.c" Index: src/usr.bin/nbperf/nbperf-bdz.c diff -u src/usr.bin/nbperf/nbperf-bdz.c:1.9 src/usr.bin/nbperf/nbperf-bdz.c:1.10 --- src/usr.bin/nbperf/nbperf-bdz.c:1.9 Wed Apr 30 21:04:58 2014 +++ src/usr.bin/nbperf/nbperf-bdz.c Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nbperf-bdz.c,v 1.9 2014/04/30 21:04:58 joerg Exp $ */ +/* $NetBSD: nbperf-bdz.c,v 1.10 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009, 2012 The NetBSD Foundation, Inc. * All rights reserved. @@ -36,7 +36,7 @@ #endif #include <sys/cdefs.h> -__RCSID("$NetBSD: nbperf-bdz.c,v 1.9 2014/04/30 21:04:58 joerg Exp $"); +__RCSID("$NetBSD: nbperf-bdz.c,v 1.10 2021/01/07 16:03:08 joerg Exp $"); #include <err.h> #include <inttypes.h> @@ -66,10 +66,11 @@ __RCSID("$NetBSD: nbperf-bdz.c,v 1.9 201 * the index of the authoritive vertex. */ -#include "graph3.h" +#define GRAPH_SIZE 3 +#include "graph2.h" struct state { - struct graph3 graph; + struct SIZED(graph) graph; uint32_t *visited; uint32_t *holes64k; uint16_t *holes64; @@ -80,7 +81,7 @@ struct state { static void assign_nodes(struct state *state) { - struct edge3 *e; + struct SIZED(edge) *e; size_t i, j; uint32_t t, r, holes; @@ -90,29 +91,29 @@ assign_nodes(struct state *state) for (i = 0; i < state->graph.e; ++i) { j = state->graph.output_order[i]; e = &state->graph.edges[j]; - if (!state->visited[e->left]) { + if (!state->visited[e->vertices[0]]) { r = 0; - t = e->left; - } else if (!state->visited[e->middle]) { + t = e->vertices[0]; + } else if (!state->visited[e->vertices[1]]) { r = 1; - t = e->middle; + t = e->vertices[1]; } else { - if (state->visited[e->right]) + if (state->visited[e->vertices[2]]) abort(); r = 2; - t = e->right; + t = e->vertices[2]; } state->visited[t] = 2 + j; - if (state->visited[e->left] == 0) - state->visited[e->left] = 1; - if (state->visited[e->middle] == 0) - state->visited[e->middle] = 1; - if (state->visited[e->right] == 0) - state->visited[e->right] = 1; + if (state->visited[e->vertices[0]] == 0) + state->visited[e->vertices[0]] = 1; + if (state->visited[e->vertices[1]] == 0) + state->visited[e->vertices[1]] = 1; + if (state->visited[e->vertices[2]] == 0) + state->visited[e->vertices[2]] = 1; - state->g[t] = (9 + r - state->g[e->left] - state->g[e->middle] - - state->g[e->right]) % 3; + state->g[t] = (9 + r - state->g[e->vertices[0]] - state->g[e->vertices[1]] + - state->g[e->vertices[2]]) % 3; } holes = 0; @@ -226,6 +227,16 @@ print_hash(struct nbperf *nbperf, struct fprintf(nbperf->output, "\th[2] = h[2] %% %" PRIu32 ";\n", state->graph.v); + if (state->graph.hash_fudge & 1) + fprintf(nbperf->output, "\th[1] ^= (h[0] == h[1]);\n"); + + if (state->graph.hash_fudge & 2) { + fprintf(nbperf->output, + "\th[2] ^= (h[0] == h[2] || h[1] == h[2]);\n"); + fprintf(nbperf->output, + "\th[2] ^= 2 * (h[0] == h[2] || h[1] == h[2]);\n"); + } + fprintf(nbperf->output, "\tidx = 9 + ((g1[h[0] >> 6] >> (h[0] & 63)) &1)\n" "\t + ((g1[h[1] >> 6] >> (h[1] & 63)) & 1)\n" @@ -273,6 +284,8 @@ bpz_compute(struct nbperf *nbperf) ++v; if (v < 10) v = 10; + if (nbperf->allow_hash_fudging) + v |= 3; graph3_setup(&state.graph, v, e); @@ -287,9 +300,9 @@ bpz_compute(struct nbperf *nbperf) state.result_map == NULL) err(1, "malloc failed"); - if (graph3_hash(nbperf, &state.graph)) + if (SIZED2(_hash)(nbperf, &state.graph)) goto failed; - if (graph3_output_order(&state.graph)) + if (SIZED2(_output_order)(&state.graph)) goto failed; assign_nodes(&state); print_hash(nbperf, &state); @@ -297,7 +310,7 @@ bpz_compute(struct nbperf *nbperf) retval = 0; failed: - graph3_free(&state.graph); + SIZED2(_free)(&state.graph); free(state.visited); free(state.g); free(state.holes64k); Index: src/usr.bin/nbperf/nbperf-chm.c diff -u src/usr.bin/nbperf/nbperf-chm.c:1.3 src/usr.bin/nbperf/nbperf-chm.c:1.4 --- src/usr.bin/nbperf/nbperf-chm.c:1.3 Fri Oct 21 23:47:11 2011 +++ src/usr.bin/nbperf/nbperf-chm.c Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 joerg Exp $ */ +/* $NetBSD: nbperf-chm.c,v 1.4 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. @@ -35,7 +35,7 @@ #endif #include <sys/cdefs.h> -__RCSID("$NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 joerg Exp $"); +__RCSID("$NetBSD: nbperf-chm.c,v 1.4 2021/01/07 16:03:08 joerg Exp $"); #include <err.h> #include <inttypes.h> @@ -45,11 +45,7 @@ __RCSID("$NetBSD: nbperf-chm.c,v 1.3 201 #include "nbperf.h" -#ifdef BUILD_CHM3 -#include "graph3.h" -#else #include "graph2.h" -#endif /* * A full description of the algorithm can be found in: @@ -78,60 +74,74 @@ __RCSID("$NetBSD: nbperf-chm.c,v 1.3 201 */ struct state { -#ifdef BUILD_CHM3 - struct graph3 graph; -#else - struct graph2 graph; -#endif + struct SIZED(graph) graph; uint32_t *g; uint8_t *visited; }; +#if GRAPH_SIZE == 3 static void assign_nodes(struct state *state) { -#ifdef BUILD_CHM3 - struct edge3 *e; -#else - struct edge2 *e; -#endif + struct SIZED(edge) *e; size_t i; - uint32_t e_idx; + uint32_t e_idx, v0, v1, v2, g; for (i = 0; i < state->graph.e; ++i) { e_idx = state->graph.output_order[i]; e = &state->graph.edges[e_idx]; - -#ifdef BUILD_CHM3 - if (!state->visited[e->left]) { - state->g[e->left] = (2 * state->graph.e + e_idx - - state->g[e->middle] - state->g[e->right]) - % state->graph.e; - } else if (!state->visited[e->middle]) { - state->g[e->middle] = (2 * state->graph.e + e_idx - - state->g[e->left] - state->g[e->right]) - % state->graph.e; + if (!state->visited[e->vertices[0]]) { + v0 = e->vertices[0]; + v1 = e->vertices[1]; + v2 = e->vertices[2]; + } else if (!state->visited[e->vertices[1]]) { + v0 = e->vertices[1]; + v1 = e->vertices[0]; + v2 = e->vertices[2]; } else { - state->g[e->right] = (2 * state->graph.e + e_idx - - state->g[e->left] - state->g[e->middle]) - % state->graph.e; + v0 = e->vertices[2]; + v1 = e->vertices[0]; + v2 = e->vertices[1]; + } + g = e_idx - state->g[v1] - state->g[v2]; + if (g >= state->graph.e) { + g += state->graph.e; + if (g >= state->graph.e) + g += state->graph.e; } - state->visited[e->left] = 1; - state->visited[e->middle] = 1; - state->visited[e->right] = 1; + state->g[v0] = g; + state->visited[v0] = 1; + state->visited[v1] = 1; + state->visited[v2] = 1; + } +} #else - if (!state->visited[e->left]) { - state->g[e->left] = (state->graph.e + e_idx - - state->g[e->right]) % state->graph.e; +static void +assign_nodes(struct state *state) +{ + struct SIZED(edge) *e; + size_t i; + uint32_t e_idx, v0, v1, g; + + for (i = 0; i < state->graph.e; ++i) { + e_idx = state->graph.output_order[i]; + e = &state->graph.edges[e_idx]; + if (!state->visited[e->vertices[0]]) { + v0 = e->vertices[0]; + v1 = e->vertices[1]; } else { - state->g[e->right] = (state->graph.e + e_idx - - state->g[e->left]) % state->graph.e; + v0 = e->vertices[1]; + v1 = e->vertices[0]; } - state->visited[e->left] = 1; - state->visited[e->right] = 1; -#endif + g = e_idx - state->g[v1]; + if (g >= state->graph.e) + g += state->graph.e; + state->g[v0] = g; + state->visited[v0] = 1; + state->visited[v1] = 1; } } +#endif static void print_hash(struct nbperf *nbperf, struct state *state) @@ -175,15 +185,34 @@ print_hash(struct nbperf *nbperf, struct fprintf(nbperf->output, "\t};\n"); fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size); (*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h"); -#ifdef BUILD_CHM3 - fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + " - "g[h[1] %% %" PRIu32 "] + " - "g[h[2] %% %" PRIu32"]) %% %" PRIu32 ";\n", - state->graph.v, state->graph.v, state->graph.v, state->graph.e); + + fprintf(nbperf->output, "\n\th[0] = h[0] %% %" PRIu32 ";\n", + state->graph.v); + fprintf(nbperf->output, "\th[1] = h[1] %% %" PRIu32 ";\n", + state->graph.v); +#if GRAPH_SIZE == 3 + fprintf(nbperf->output, "\th[2] = h[2] %% %" PRIu32 ";\n", + state->graph.v); +#endif + + if (state->graph.hash_fudge & 1) + fprintf(nbperf->output, "\th[1] ^= (h[0] == h[1]);\n"); + +#if GRAPH_SIZE == 3 + if (state->graph.hash_fudge & 2) { + fprintf(nbperf->output, + "\th[2] ^= (h[0] == h[2] || h[1] == h[2]);\n"); + fprintf(nbperf->output, + "\th[2] ^= 2 * (h[0] == h[2] || h[1] == h[2]);\n"); + } +#endif + +#if GRAPH_SIZE == 3 + fprintf(nbperf->output, "\treturn (g[h[0]] + g[h[1]] + g[h[2]]) %% " + "%" PRIu32 ";\n", state->graph.e); #else - fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + " - "g[h[1] %% %" PRIu32"]) %% %" PRIu32 ";\n", - state->graph.v, state->graph.v, state->graph.e); + fprintf(nbperf->output, "\treturn (g[h[0]] + g[h[1]]) %% " + "%" PRIu32 ";\n", state->graph.e); #endif fprintf(nbperf->output, "}\n"); @@ -194,7 +223,7 @@ print_hash(struct nbperf *nbperf, struct } int -#ifdef BUILD_CHM3 +#if GRAPH_SIZE == 3 chm3_compute(struct nbperf *nbperf) #else chm_compute(struct nbperf *nbperf) @@ -204,7 +233,7 @@ chm_compute(struct nbperf *nbperf) int retval = -1; uint32_t v, e; -#ifdef BUILD_CHM3 +#if GRAPH_SIZE == 3 if (nbperf->c == 0) nbperf-> c = 1.24; @@ -227,14 +256,18 @@ chm_compute(struct nbperf *nbperf) (*nbperf->seed_hash)(nbperf); e = nbperf->n; v = nbperf->c * nbperf->n; -#ifdef BUILD_CHM3 +#if GRAPH_SIZE == 3 if (v == 1.24 * nbperf->n) ++v; if (v < 10) v = 10; + if (nbperf->allow_hash_fudging) + v |= 3; #else if (v == 2 * nbperf->n) ++v; + if (nbperf->allow_hash_fudging) + v |= 1; #endif state.g = calloc(sizeof(uint32_t), v); @@ -242,30 +275,18 @@ chm_compute(struct nbperf *nbperf) if (state.g == NULL || state.visited == NULL) err(1, "malloc failed"); -#ifdef BUILD_CHM3 - graph3_setup(&state.graph, v, e); - if (graph3_hash(nbperf, &state.graph)) - goto failed; - if (graph3_output_order(&state.graph)) - goto failed; -#else - graph2_setup(&state.graph, v, e); - if (graph2_hash(nbperf, &state.graph)) + SIZED2(_setup)(&state.graph, v, e); + if (SIZED2(_hash)(nbperf, &state.graph)) goto failed; - if (graph2_output_order(&state.graph)) + if (SIZED2(_output_order)(&state.graph)) goto failed; -#endif assign_nodes(&state); print_hash(nbperf, &state); retval = 0; failed: -#ifdef BUILD_CHM3 - graph3_free(&state.graph); -#else - graph2_free(&state.graph); -#endif + SIZED2(_free)(&state.graph); free(state.g); free(state.visited); return retval; Index: src/usr.bin/nbperf/nbperf.1 diff -u src/usr.bin/nbperf/nbperf.1:1.7 src/usr.bin/nbperf/nbperf.1:1.8 --- src/usr.bin/nbperf/nbperf.1:1.7 Tue Jun 20 15:50:04 2017 +++ src/usr.bin/nbperf/nbperf.1 Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -.\" $NetBSD: nbperf.1,v 1.7 2017/06/20 15:50:04 abhinav Exp $ +.\" $NetBSD: nbperf.1,v 1.8 2021/01/07 16:03:08 joerg Exp $ .\" .\" Copyright (c) 2009 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd September 25, 2012 +.Dd January 5, 2021 .Dt NBPERF 1 .Os .Sh NAME @@ -35,7 +35,7 @@ .Nd compute a perfect hash function .Sh SYNOPSIS .Nm -.Op Fl ps +.Op Fl fps .Op Fl a Ar algorithm .Op Fl c Ar utilisation .Op Fl h Ar hash Index: src/usr.bin/nbperf/nbperf.c diff -u src/usr.bin/nbperf/nbperf.c:1.5 src/usr.bin/nbperf/nbperf.c:1.6 --- src/usr.bin/nbperf/nbperf.c:1.5 Thu Jan 31 16:32:02 2013 +++ src/usr.bin/nbperf/nbperf.c Thu Jan 7 16:03:08 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: nbperf.c,v 1.5 2013/01/31 16:32:02 joerg Exp $ */ +/* $NetBSD: nbperf.c,v 1.6 2021/01/07 16:03:08 joerg Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. @@ -36,7 +36,7 @@ #endif #include <sys/cdefs.h> -__RCSID("$NetBSD: nbperf.c,v 1.5 2013/01/31 16:32:02 joerg Exp $"); +__RCSID("$NetBSD: nbperf.c,v 1.6 2021/01/07 16:03:08 joerg Exp $"); #include <sys/endian.h> #include <err.h> @@ -45,6 +45,7 @@ __RCSID("$NetBSD: nbperf.c,v 1.5 2013/01 #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <time.h> #include <unistd.h> #include "nbperf.h" @@ -115,8 +116,9 @@ main(int argc, char **argv) .map_output = NULL, .output = NULL, .static_hash = 0, - .first_round = 1, + .check_duplicates = 0, .has_duplicates = 0, + .allow_hash_fudging = 0, }; FILE *input; size_t curlen = 0, curalloc = 0; @@ -132,7 +134,7 @@ main(int argc, char **argv) set_hash(&nbperf, "mi_vector_hash"); - while ((ch = getopt(argc, argv, "a:c:h:i:m:n:o:ps")) != -1) { + while ((ch = getopt(argc, argv, "a:c:fh:i:m:n:o:ps")) != -1) { switch (ch) { case 'a': /* Accept bdz as alias for netbsd-6 compat. */ @@ -152,6 +154,9 @@ main(int argc, char **argv) if (errno || eos[0] || !nbperf.c) errx(2, "Invalid argument for -c"); break; + case 'f': + nbperf.allow_hash_fudging = 1; + break; case 'h': set_hash(&nbperf, optarg); break; @@ -241,10 +246,18 @@ main(int argc, char **argv) nbperf.keylens = keylens; looped = 0; - while ((*build_hash)(&nbperf)) { - if (nbperf.has_duplicates) + int rv; + for (;;) { + rv = (*build_hash)(&nbperf); + if (!rv) + break; + if (nbperf.has_duplicates) { + fputc('\n', stderr); errx(1, "Duplicate keys detected"); + } fputc('.', stderr); + if (!looped) + nbperf.check_duplicates = 1; looped = 1; if (max_iterations == 0xffffffffU) continue;