Home | History | Annotate | Line # | Download | only in nbperf
nbperf-chm.c revision 1.3
      1  1.3  joerg /*	$NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 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.3  joerg __RCSID("$NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 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 #ifdef BUILD_CHM3
     49  1.1  joerg #include "graph3.h"
     50  1.1  joerg #else
     51  1.1  joerg #include "graph2.h"
     52  1.1  joerg #endif
     53  1.1  joerg 
     54  1.1  joerg /*
     55  1.1  joerg  * A full description of the algorithm can be found in:
     56  1.1  joerg  * "An optimal algorithm for generating minimal perfect hash functions"
     57  1.1  joerg  * by Czech, Havas and Majewski in Information Processing Letters,
     58  1.1  joerg  * 43(5):256-264, October 1992.
     59  1.1  joerg  */
     60  1.1  joerg 
     61  1.1  joerg /*
     62  1.1  joerg  * The algorithm is based on random, acyclic graphs.
     63  1.1  joerg  *
     64  1.1  joerg  * Each edge in the represents a key.  The vertices are the reminder of
     65  1.1  joerg  * the hash function mod n.  n = cm with c > 2, otherwise the propability
     66  1.1  joerg  * of finding an acyclic graph is very low (for 2-graphs).  The constant
     67  1.1  joerg  * for 3-graphs is 1.24.
     68  1.1  joerg  *
     69  1.1  joerg  * After the hashing phase, the graph is checked for cycles.
     70  1.1  joerg  * A cycle-free graph is either empty or has a vertex of degree 1.
     71  1.1  joerg  * Removing the edge for this vertex doesn't change this property,
     72  1.1  joerg  * so applying this recursively reduces the size of the graph.
     73  1.1  joerg  * If the graph is empty at the end of the process, it was acyclic.
     74  1.1  joerg  *
     75  1.1  joerg  * The assignment step now sets g[i] := 0 and processes the edges
     76  1.1  joerg  * in reverse order of removal.  That ensures that at least one vertex
     77  1.1  joerg  * is always unvisited and can be assigned.
     78  1.1  joerg  */
     79  1.1  joerg 
     80  1.1  joerg struct state {
     81  1.1  joerg #ifdef BUILD_CHM3
     82  1.1  joerg 	struct graph3 graph;
     83  1.1  joerg #else
     84  1.1  joerg 	struct graph2 graph;
     85  1.1  joerg #endif
     86  1.1  joerg 	uint32_t *g;
     87  1.1  joerg 	uint8_t *visited;
     88  1.1  joerg };
     89  1.1  joerg 
     90  1.1  joerg static void
     91  1.1  joerg assign_nodes(struct state *state)
     92  1.1  joerg {
     93  1.1  joerg #ifdef BUILD_CHM3
     94  1.1  joerg 	struct edge3 *e;
     95  1.1  joerg #else
     96  1.1  joerg 	struct edge2 *e;
     97  1.1  joerg #endif
     98  1.1  joerg 	size_t i;
     99  1.1  joerg 	uint32_t e_idx;
    100  1.1  joerg 
    101  1.1  joerg 	for (i = 0; i < state->graph.e; ++i) {
    102  1.1  joerg 		e_idx = state->graph.output_order[i];
    103  1.1  joerg 		e = &state->graph.edges[e_idx];
    104  1.1  joerg 
    105  1.1  joerg #ifdef BUILD_CHM3
    106  1.1  joerg 		if (!state->visited[e->left]) {
    107  1.1  joerg 			state->g[e->left] = (2 * state->graph.e + e_idx
    108  1.1  joerg 			    - state->g[e->middle] - state->g[e->right])
    109  1.1  joerg 			    % state->graph.e;
    110  1.1  joerg 		} else if (!state->visited[e->middle]) {
    111  1.1  joerg 			state->g[e->middle] = (2 * state->graph.e + e_idx
    112  1.1  joerg 			    - state->g[e->left] - state->g[e->right])
    113  1.1  joerg 			    % state->graph.e;
    114  1.1  joerg 		} else {
    115  1.1  joerg 			state->g[e->right] = (2 * state->graph.e + e_idx
    116  1.1  joerg 			    - state->g[e->left] - state->g[e->middle])
    117  1.1  joerg 			    % state->graph.e;
    118  1.1  joerg 		}
    119  1.1  joerg 		state->visited[e->left] = 1;
    120  1.1  joerg 		state->visited[e->middle] = 1;
    121  1.1  joerg 		state->visited[e->right] = 1;
    122  1.1  joerg #else
    123  1.1  joerg 		if (!state->visited[e->left]) {
    124  1.1  joerg 			state->g[e->left] = (state->graph.e + e_idx
    125  1.1  joerg 			    - state->g[e->right]) % state->graph.e;
    126  1.1  joerg 		} else {
    127  1.1  joerg 			state->g[e->right] = (state->graph.e + e_idx
    128  1.1  joerg 			    - state->g[e->left]) % state->graph.e;
    129  1.1  joerg 		}
    130  1.1  joerg 		state->visited[e->left] = 1;
    131  1.1  joerg 		state->visited[e->right] = 1;
    132  1.1  joerg #endif
    133  1.1  joerg 	}
    134  1.1  joerg }
    135  1.1  joerg 
    136  1.1  joerg static void
    137  1.1  joerg print_hash(struct nbperf *nbperf, struct state *state)
    138  1.1  joerg {
    139  1.2  joerg 	uint32_t i, per_line;
    140  1.1  joerg 	const char *g_type;
    141  1.2  joerg 	int g_width;
    142  1.1  joerg 
    143  1.1  joerg 	fprintf(nbperf->output, "#include <stdlib.h>\n\n");
    144  1.1  joerg 
    145  1.1  joerg 	fprintf(nbperf->output, "%suint32_t\n",
    146  1.1  joerg 	    nbperf->static_hash ? "static " : "");
    147  1.1  joerg 	fprintf(nbperf->output,
    148  1.1  joerg 	    "%s(const void * __restrict key, size_t keylen)\n",
    149  1.1  joerg 	    nbperf->hash_name);
    150  1.1  joerg 	fprintf(nbperf->output, "{\n");
    151  1.2  joerg 	if (state->graph.v >= 65536) {
    152  1.1  joerg 		g_type = "uint32_t";
    153  1.2  joerg 		g_width = 8;
    154  1.2  joerg 		per_line = 4;
    155  1.2  joerg 	} else if (state->graph.v >= 256) {
    156  1.1  joerg 		g_type = "uint16_t";
    157  1.2  joerg 		g_width = 4;
    158  1.2  joerg 		per_line = 8;
    159  1.2  joerg 	} else {
    160  1.1  joerg 		g_type = "uint8_t";
    161  1.2  joerg 		g_width = 2;
    162  1.2  joerg 		per_line = 10;
    163  1.2  joerg 	}
    164  1.1  joerg 	fprintf(nbperf->output, "\tstatic const %s g[%" PRId32 "] = {\n",
    165  1.1  joerg 	    g_type, state->graph.v);
    166  1.1  joerg 	for (i = 0; i < state->graph.v; ++i) {
    167  1.2  joerg 		fprintf(nbperf->output, "%s0x%0*" PRIx32 ",%s",
    168  1.2  joerg 		    (i % per_line == 0 ? "\t    " : " "),
    169  1.2  joerg 		    g_width, state->g[i],
    170  1.2  joerg 		    (i % per_line == per_line - 1 ? "\n" : ""));
    171  1.1  joerg 	}
    172  1.2  joerg 	if (i % per_line != 0)
    173  1.1  joerg 		fprintf(nbperf->output, "\n\t};\n");
    174  1.1  joerg 	else
    175  1.1  joerg 		fprintf(nbperf->output, "\t};\n");
    176  1.1  joerg 	fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size);
    177  1.1  joerg 	(*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h");
    178  1.1  joerg #ifdef BUILD_CHM3
    179  1.1  joerg 	fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + "
    180  1.1  joerg 	    "g[h[1] %% %" PRIu32 "] + "
    181  1.1  joerg 	    "g[h[2] %% %" PRIu32"]) %% %" PRIu32 ";\n",
    182  1.1  joerg 	    state->graph.v, state->graph.v, state->graph.v, state->graph.e);
    183  1.1  joerg #else
    184  1.1  joerg 	fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + "
    185  1.1  joerg 	    "g[h[1] %% %" PRIu32"]) %% %" PRIu32 ";\n",
    186  1.1  joerg 	    state->graph.v, state->graph.v, state->graph.e);
    187  1.1  joerg #endif
    188  1.1  joerg 	fprintf(nbperf->output, "}\n");
    189  1.1  joerg 
    190  1.1  joerg 	if (nbperf->map_output != NULL) {
    191  1.1  joerg 		for (i = 0; i < state->graph.e; ++i)
    192  1.1  joerg 			fprintf(nbperf->map_output, "%" PRIu32 "\n", i);
    193  1.1  joerg 	}
    194  1.1  joerg }
    195  1.1  joerg 
    196  1.1  joerg int
    197  1.1  joerg #ifdef BUILD_CHM3
    198  1.1  joerg chm3_compute(struct nbperf *nbperf)
    199  1.1  joerg #else
    200  1.1  joerg chm_compute(struct nbperf *nbperf)
    201  1.1  joerg #endif
    202  1.1  joerg {
    203  1.1  joerg 	struct state state;
    204  1.1  joerg 	int retval = -1;
    205  1.1  joerg 	uint32_t v, e;
    206  1.1  joerg 
    207  1.1  joerg #ifdef BUILD_CHM3
    208  1.1  joerg 	if (nbperf->c == 0)
    209  1.1  joerg 		nbperf-> c = 1.24;
    210  1.1  joerg 
    211  1.1  joerg 	if (nbperf->c < 1.24)
    212  1.1  joerg 		errx(1, "The argument for option -c must be at least 1.24");
    213  1.1  joerg 
    214  1.1  joerg 	if (nbperf->hash_size < 3)
    215  1.1  joerg 		errx(1, "The hash function must generate at least 3 values");
    216  1.1  joerg #else
    217  1.1  joerg 	if (nbperf->c == 0)
    218  1.1  joerg 		nbperf-> c = 2;
    219  1.1  joerg 
    220  1.1  joerg 	if (nbperf->c < 2)
    221  1.1  joerg 		errx(1, "The argument for option -c must be at least 2");
    222  1.1  joerg 
    223  1.1  joerg 	if (nbperf->hash_size < 2)
    224  1.1  joerg 		errx(1, "The hash function must generate at least 2 values");
    225  1.1  joerg #endif
    226  1.1  joerg 
    227  1.1  joerg 	(*nbperf->seed_hash)(nbperf);
    228  1.1  joerg 	e = nbperf->n;
    229  1.1  joerg 	v = nbperf->c * nbperf->n;
    230  1.1  joerg #ifdef BUILD_CHM3
    231  1.1  joerg 	if (v == 1.24 * nbperf->n)
    232  1.1  joerg 		++v;
    233  1.1  joerg 	if (v < 10)
    234  1.1  joerg 		v = 10;
    235  1.1  joerg #else
    236  1.1  joerg 	if (v == 2 * nbperf->n)
    237  1.1  joerg 		++v;
    238  1.1  joerg #endif
    239  1.1  joerg 
    240  1.1  joerg 	state.g = calloc(sizeof(uint32_t), v);
    241  1.1  joerg 	state.visited = calloc(sizeof(uint8_t), v);
    242  1.1  joerg 	if (state.g == NULL || state.visited == NULL)
    243  1.1  joerg 		err(1, "malloc failed");
    244  1.1  joerg 
    245  1.1  joerg #ifdef BUILD_CHM3
    246  1.1  joerg 	graph3_setup(&state.graph, v, e);
    247  1.1  joerg 	if (graph3_hash(nbperf, &state.graph))
    248  1.1  joerg 		goto failed;
    249  1.1  joerg 	if (graph3_output_order(&state.graph))
    250  1.1  joerg 		goto failed;
    251  1.1  joerg #else
    252  1.1  joerg 	graph2_setup(&state.graph, v, e);
    253  1.1  joerg 	if (graph2_hash(nbperf, &state.graph))
    254  1.1  joerg 		goto failed;
    255  1.1  joerg 	if (graph2_output_order(&state.graph))
    256  1.1  joerg 		goto failed;
    257  1.1  joerg #endif
    258  1.1  joerg 	assign_nodes(&state);
    259  1.1  joerg 	print_hash(nbperf, &state);
    260  1.1  joerg 
    261  1.1  joerg 	retval = 0;
    262  1.1  joerg 
    263  1.1  joerg failed:
    264  1.1  joerg #ifdef BUILD_CHM3
    265  1.1  joerg 	graph3_free(&state.graph);
    266  1.1  joerg #else
    267  1.1  joerg 	graph2_free(&state.graph);
    268  1.1  joerg #endif
    269  1.1  joerg 	free(state.g);
    270  1.1  joerg 	free(state.visited);
    271  1.1  joerg 	return retval;
    272  1.1  joerg }
    273