Home | History | Annotate | Line # | Download | only in nbperf
      1  1.5  joerg /*	$NetBSD: nbperf-chm.c,v 1.5 2021/01/26 21:25:55 joerg Exp $	*/
      2  1.1  joerg /*-
      3  1.1  joerg  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      4  1.1  joerg  * All rights reserved.
      5  1.1  joerg  *
      6  1.1  joerg  * This code is derived from software contributed to The NetBSD Foundation
      7  1.1  joerg  * by Joerg Sonnenberger.
      8  1.1  joerg  *
      9  1.1  joerg  * Redistribution and use in source and binary forms, with or without
     10  1.1  joerg  * modification, are permitted provided that the following conditions
     11  1.1  joerg  * are met:
     12  1.1  joerg  *
     13  1.1  joerg  * 1. Redistributions of source code must retain the above copyright
     14  1.1  joerg  *    notice, this list of conditions and the following disclaimer.
     15  1.1  joerg  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1  joerg  *    notice, this list of conditions and the following disclaimer in
     17  1.1  joerg  *    the documentation and/or other materials provided with the
     18  1.1  joerg  *    distribution.
     19  1.1  joerg  *
     20  1.1  joerg  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  1.1  joerg  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  1.1  joerg  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     23  1.1  joerg  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
     24  1.1  joerg  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  1.1  joerg  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
     26  1.1  joerg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27  1.1  joerg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     28  1.1  joerg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     29  1.1  joerg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     30  1.1  joerg  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  1.1  joerg  * SUCH DAMAGE.
     32  1.1  joerg  */
     33  1.3  joerg #if HAVE_NBTOOL_CONFIG_H
     34  1.3  joerg #include "nbtool_config.h"
     35  1.3  joerg #endif
     36  1.1  joerg 
     37  1.1  joerg #include <sys/cdefs.h>
     38  1.5  joerg __RCSID("$NetBSD: nbperf-chm.c,v 1.5 2021/01/26 21:25:55 joerg Exp $");
     39  1.1  joerg 
     40  1.1  joerg #include <err.h>
     41  1.1  joerg #include <inttypes.h>
     42  1.1  joerg #include <stdlib.h>
     43  1.1  joerg #include <stdio.h>
     44  1.1  joerg #include <string.h>
     45  1.1  joerg 
     46  1.1  joerg #include "nbperf.h"
     47  1.1  joerg 
     48  1.1  joerg #include "graph2.h"
     49  1.1  joerg 
     50  1.1  joerg /*
     51  1.1  joerg  * A full description of the algorithm can be found in:
     52  1.1  joerg  * "An optimal algorithm for generating minimal perfect hash functions"
     53  1.1  joerg  * by Czech, Havas and Majewski in Information Processing Letters,
     54  1.1  joerg  * 43(5):256-264, October 1992.
     55  1.1  joerg  */
     56  1.1  joerg 
     57  1.1  joerg /*
     58  1.1  joerg  * The algorithm is based on random, acyclic graphs.
     59  1.1  joerg  *
     60  1.1  joerg  * Each edge in the represents a key.  The vertices are the reminder of
     61  1.1  joerg  * the hash function mod n.  n = cm with c > 2, otherwise the propability
     62  1.1  joerg  * of finding an acyclic graph is very low (for 2-graphs).  The constant
     63  1.1  joerg  * for 3-graphs is 1.24.
     64  1.1  joerg  *
     65  1.1  joerg  * After the hashing phase, the graph is checked for cycles.
     66  1.1  joerg  * A cycle-free graph is either empty or has a vertex of degree 1.
     67  1.1  joerg  * Removing the edge for this vertex doesn't change this property,
     68  1.1  joerg  * so applying this recursively reduces the size of the graph.
     69  1.1  joerg  * If the graph is empty at the end of the process, it was acyclic.
     70  1.1  joerg  *
     71  1.1  joerg  * The assignment step now sets g[i] := 0 and processes the edges
     72  1.1  joerg  * in reverse order of removal.  That ensures that at least one vertex
     73  1.1  joerg  * is always unvisited and can be assigned.
     74  1.1  joerg  */
     75  1.1  joerg 
     76  1.1  joerg struct state {
     77  1.4  joerg 	struct SIZED(graph) graph;
     78  1.1  joerg 	uint32_t *g;
     79  1.1  joerg 	uint8_t *visited;
     80  1.1  joerg };
     81  1.1  joerg 
     82  1.4  joerg #if GRAPH_SIZE == 3
     83  1.1  joerg static void
     84  1.1  joerg assign_nodes(struct state *state)
     85  1.1  joerg {
     86  1.4  joerg 	struct SIZED(edge) *e;
     87  1.1  joerg 	size_t i;
     88  1.4  joerg 	uint32_t e_idx, v0, v1, v2, g;
     89  1.1  joerg 
     90  1.1  joerg 	for (i = 0; i < state->graph.e; ++i) {
     91  1.1  joerg 		e_idx = state->graph.output_order[i];
     92  1.1  joerg 		e = &state->graph.edges[e_idx];
     93  1.4  joerg 		if (!state->visited[e->vertices[0]]) {
     94  1.4  joerg 			v0 = e->vertices[0];
     95  1.4  joerg 			v1 = e->vertices[1];
     96  1.4  joerg 			v2 = e->vertices[2];
     97  1.4  joerg 		} else if (!state->visited[e->vertices[1]]) {
     98  1.4  joerg 			v0 = e->vertices[1];
     99  1.4  joerg 			v1 = e->vertices[0];
    100  1.4  joerg 			v2 = e->vertices[2];
    101  1.1  joerg 		} else {
    102  1.4  joerg 			v0 = e->vertices[2];
    103  1.4  joerg 			v1 = e->vertices[0];
    104  1.4  joerg 			v2 = e->vertices[1];
    105  1.4  joerg 		}
    106  1.4  joerg 		g = e_idx - state->g[v1] - state->g[v2];
    107  1.4  joerg 		if (g >= state->graph.e) {
    108  1.4  joerg 			g += state->graph.e;
    109  1.4  joerg 			if (g >= state->graph.e)
    110  1.4  joerg 				g += state->graph.e;
    111  1.1  joerg 		}
    112  1.4  joerg 		state->g[v0] = g;
    113  1.4  joerg 		state->visited[v0] = 1;
    114  1.4  joerg 		state->visited[v1] = 1;
    115  1.4  joerg 		state->visited[v2] = 1;
    116  1.4  joerg 	}
    117  1.4  joerg }
    118  1.1  joerg #else
    119  1.4  joerg static void
    120  1.4  joerg assign_nodes(struct state *state)
    121  1.4  joerg {
    122  1.4  joerg 	struct SIZED(edge) *e;
    123  1.4  joerg 	size_t i;
    124  1.4  joerg 	uint32_t e_idx, v0, v1, g;
    125  1.4  joerg 
    126  1.4  joerg 	for (i = 0; i < state->graph.e; ++i) {
    127  1.4  joerg 		e_idx = state->graph.output_order[i];
    128  1.4  joerg 		e = &state->graph.edges[e_idx];
    129  1.4  joerg 		if (!state->visited[e->vertices[0]]) {
    130  1.4  joerg 			v0 = e->vertices[0];
    131  1.4  joerg 			v1 = e->vertices[1];
    132  1.1  joerg 		} else {
    133  1.4  joerg 			v0 = e->vertices[1];
    134  1.4  joerg 			v1 = e->vertices[0];
    135  1.1  joerg 		}
    136  1.4  joerg 		g = e_idx - state->g[v1];
    137  1.4  joerg 		if (g >= state->graph.e)
    138  1.4  joerg 			g += state->graph.e;
    139  1.4  joerg 		state->g[v0] = g;
    140  1.4  joerg 		state->visited[v0] = 1;
    141  1.4  joerg 		state->visited[v1] = 1;
    142  1.1  joerg 	}
    143  1.1  joerg }
    144  1.4  joerg #endif
    145  1.1  joerg 
    146  1.1  joerg static void
    147  1.1  joerg print_hash(struct nbperf *nbperf, struct state *state)
    148  1.1  joerg {
    149  1.2  joerg 	uint32_t i, per_line;
    150  1.1  joerg 	const char *g_type;
    151  1.2  joerg 	int g_width;
    152  1.1  joerg 
    153  1.1  joerg 	fprintf(nbperf->output, "#include <stdlib.h>\n\n");
    154  1.1  joerg 
    155  1.1  joerg 	fprintf(nbperf->output, "%suint32_t\n",
    156  1.1  joerg 	    nbperf->static_hash ? "static " : "");
    157  1.1  joerg 	fprintf(nbperf->output,
    158  1.1  joerg 	    "%s(const void * __restrict key, size_t keylen)\n",
    159  1.1  joerg 	    nbperf->hash_name);
    160  1.1  joerg 	fprintf(nbperf->output, "{\n");
    161  1.2  joerg 	if (state->graph.v >= 65536) {
    162  1.1  joerg 		g_type = "uint32_t";
    163  1.2  joerg 		g_width = 8;
    164  1.2  joerg 		per_line = 4;
    165  1.2  joerg 	} else if (state->graph.v >= 256) {
    166  1.1  joerg 		g_type = "uint16_t";
    167  1.2  joerg 		g_width = 4;
    168  1.2  joerg 		per_line = 8;
    169  1.2  joerg 	} else {
    170  1.1  joerg 		g_type = "uint8_t";
    171  1.2  joerg 		g_width = 2;
    172  1.2  joerg 		per_line = 10;
    173  1.2  joerg 	}
    174  1.1  joerg 	fprintf(nbperf->output, "\tstatic const %s g[%" PRId32 "] = {\n",
    175  1.1  joerg 	    g_type, state->graph.v);
    176  1.1  joerg 	for (i = 0; i < state->graph.v; ++i) {
    177  1.2  joerg 		fprintf(nbperf->output, "%s0x%0*" PRIx32 ",%s",
    178  1.2  joerg 		    (i % per_line == 0 ? "\t    " : " "),
    179  1.2  joerg 		    g_width, state->g[i],
    180  1.2  joerg 		    (i % per_line == per_line - 1 ? "\n" : ""));
    181  1.1  joerg 	}
    182  1.2  joerg 	if (i % per_line != 0)
    183  1.1  joerg 		fprintf(nbperf->output, "\n\t};\n");
    184  1.1  joerg 	else
    185  1.1  joerg 		fprintf(nbperf->output, "\t};\n");
    186  1.1  joerg 	fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size);
    187  1.1  joerg 	(*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h");
    188  1.4  joerg 
    189  1.4  joerg 	fprintf(nbperf->output, "\n\th[0] = h[0] %% %" PRIu32 ";\n",
    190  1.4  joerg 	    state->graph.v);
    191  1.4  joerg 	fprintf(nbperf->output, "\th[1] = h[1] %% %" PRIu32 ";\n",
    192  1.4  joerg 	    state->graph.v);
    193  1.4  joerg #if GRAPH_SIZE == 3
    194  1.4  joerg 	fprintf(nbperf->output, "\th[2] = h[2] %% %" PRIu32 ";\n",
    195  1.4  joerg 	    state->graph.v);
    196  1.4  joerg #endif
    197  1.4  joerg 
    198  1.4  joerg 	if (state->graph.hash_fudge & 1)
    199  1.4  joerg 		fprintf(nbperf->output, "\th[1] ^= (h[0] == h[1]);\n");
    200  1.4  joerg 
    201  1.4  joerg #if GRAPH_SIZE == 3
    202  1.4  joerg 	if (state->graph.hash_fudge & 2) {
    203  1.4  joerg 		fprintf(nbperf->output,
    204  1.4  joerg 		    "\th[2] ^= (h[0] == h[2] || h[1] == h[2]);\n");
    205  1.4  joerg 		fprintf(nbperf->output,
    206  1.4  joerg 		    "\th[2] ^= 2 * (h[0] == h[2] || h[1] == h[2]);\n");
    207  1.4  joerg 	}
    208  1.4  joerg #endif
    209  1.4  joerg 
    210  1.4  joerg #if GRAPH_SIZE == 3
    211  1.4  joerg 	fprintf(nbperf->output, "\treturn (g[h[0]] + g[h[1]] + g[h[2]]) %% "
    212  1.4  joerg 	    "%" PRIu32 ";\n", state->graph.e);
    213  1.1  joerg #else
    214  1.4  joerg 	fprintf(nbperf->output, "\treturn (g[h[0]] + g[h[1]]) %% "
    215  1.4  joerg 	    "%" PRIu32 ";\n", state->graph.e);
    216  1.1  joerg #endif
    217  1.1  joerg 	fprintf(nbperf->output, "}\n");
    218  1.1  joerg 
    219  1.1  joerg 	if (nbperf->map_output != NULL) {
    220  1.1  joerg 		for (i = 0; i < state->graph.e; ++i)
    221  1.1  joerg 			fprintf(nbperf->map_output, "%" PRIu32 "\n", i);
    222  1.1  joerg 	}
    223  1.1  joerg }
    224  1.1  joerg 
    225  1.1  joerg int
    226  1.4  joerg #if GRAPH_SIZE == 3
    227  1.1  joerg chm3_compute(struct nbperf *nbperf)
    228  1.1  joerg #else
    229  1.1  joerg chm_compute(struct nbperf *nbperf)
    230  1.1  joerg #endif
    231  1.1  joerg {
    232  1.1  joerg 	struct state state;
    233  1.1  joerg 	int retval = -1;
    234  1.1  joerg 	uint32_t v, e;
    235  1.1  joerg 
    236  1.4  joerg #if GRAPH_SIZE == 3
    237  1.1  joerg 	if (nbperf->c == 0)
    238  1.1  joerg 		nbperf-> c = 1.24;
    239  1.1  joerg 
    240  1.1  joerg 	if (nbperf->c < 1.24)
    241  1.1  joerg 		errx(1, "The argument for option -c must be at least 1.24");
    242  1.1  joerg 
    243  1.1  joerg 	if (nbperf->hash_size < 3)
    244  1.1  joerg 		errx(1, "The hash function must generate at least 3 values");
    245  1.1  joerg #else
    246  1.1  joerg 	if (nbperf->c == 0)
    247  1.1  joerg 		nbperf-> c = 2;
    248  1.1  joerg 
    249  1.1  joerg 	if (nbperf->c < 2)
    250  1.1  joerg 		errx(1, "The argument for option -c must be at least 2");
    251  1.1  joerg 
    252  1.1  joerg 	if (nbperf->hash_size < 2)
    253  1.1  joerg 		errx(1, "The hash function must generate at least 2 values");
    254  1.1  joerg #endif
    255  1.1  joerg 
    256  1.1  joerg 	(*nbperf->seed_hash)(nbperf);
    257  1.1  joerg 	e = nbperf->n;
    258  1.1  joerg 	v = nbperf->c * nbperf->n;
    259  1.4  joerg #if GRAPH_SIZE == 3
    260  1.1  joerg 	if (v == 1.24 * nbperf->n)
    261  1.1  joerg 		++v;
    262  1.1  joerg 	if (v < 10)
    263  1.1  joerg 		v = 10;
    264  1.4  joerg 	if (nbperf->allow_hash_fudging)
    265  1.5  joerg 		v = (v + 3) & ~3;
    266  1.1  joerg #else
    267  1.1  joerg 	if (v == 2 * nbperf->n)
    268  1.1  joerg 		++v;
    269  1.4  joerg 	if (nbperf->allow_hash_fudging)
    270  1.5  joerg 		v = (v + 1) & ~1;
    271  1.1  joerg #endif
    272  1.1  joerg 
    273  1.1  joerg 	state.g = calloc(sizeof(uint32_t), v);
    274  1.1  joerg 	state.visited = calloc(sizeof(uint8_t), v);
    275  1.1  joerg 	if (state.g == NULL || state.visited == NULL)
    276  1.1  joerg 		err(1, "malloc failed");
    277  1.1  joerg 
    278  1.4  joerg 	SIZED2(_setup)(&state.graph, v, e);
    279  1.4  joerg 	if (SIZED2(_hash)(nbperf, &state.graph))
    280  1.1  joerg 		goto failed;
    281  1.4  joerg 	if (SIZED2(_output_order)(&state.graph))
    282  1.1  joerg 		goto failed;
    283  1.1  joerg 	assign_nodes(&state);
    284  1.1  joerg 	print_hash(nbperf, &state);
    285  1.1  joerg 
    286  1.1  joerg 	retval = 0;
    287  1.1  joerg 
    288  1.1  joerg failed:
    289  1.4  joerg 	SIZED2(_free)(&state.graph);
    290  1.1  joerg 	free(state.g);
    291  1.1  joerg 	free(state.visited);
    292  1.1  joerg 	return retval;
    293  1.1  joerg }
    294