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