Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src-draft/trunk]: src/lib/libc/cdb Optimize CPU and memory use of cdbw(3)
details: https://anonhg.NetBSD.org/src-all/rev/7aa6ef2acb5f
branches: trunk
changeset: 949258:7aa6ef2acb5f
user: Joerg Sonnenberger <joerg%bec.de@localhost>
date: Tue Jan 05 23:53:56 2021 +0100
description:
Optimize CPU and memory use of cdbw(3)
Reduce memory footprint and processing time by dropping the vertex parts
of the edges kept during the peeling. Hook up the
division-by-multiplication logic to help older platforms.
diffstat:
lib/libc/cdb/cdbw.c | 244 +++++++++++++++++++++++++++++++++------------------
1 files changed, 159 insertions(+), 85 deletions(-)
diffs (truncated from 357 to 300 lines):
diff -r 88478e8cd931 -r 7aa6ef2acb5f lib/libc/cdb/cdbw.c
--- a/lib/libc/cdb/cdbw.c Mon Jan 04 03:00:46 2021 +0100
+++ b/lib/libc/cdb/cdbw.c Tue Jan 05 23:53:56 2021 +0100
@@ -40,6 +40,7 @@
#include "namespace.h"
+
#if !HAVE_NBTOOL_CONFIG_H || HAVE_SYS_ENDIAN_H
#include <sys/endian.h>
#endif
@@ -49,6 +50,74 @@
#include <string.h>
#include <unistd.h>
+#if !HAVE_NBTOOL_CONFIG_H
+#include <sys/bitops.h>
+#else
+static inline int
+my_fls32(uint32_t n)
+{
+ int v;
+
+ if (!n)
+ return 0;
+
+ v = 32;
+ if ((n & 0xFFFF0000U) == 0) {
+ n <<= 16;
+ v -= 16;
+ }
+ if ((n & 0xFF000000U) == 0) {
+ n <<= 8;
+ v -= 8;
+ }
+ if ((n & 0xF0000000U) == 0) {
+ n <<= 4;
+ v -= 4;
+ }
+ if ((n & 0xC0000000U) == 0) {
+ n <<= 2;
+ v -= 2;
+ }
+ if ((n & 0x80000000U) == 0) {
+ n <<= 1;
+ v -= 1;
+ }
+ return v;
+}
+
+static inline void
+fast_divide32_prepare(uint32_t div, uint32_t * m,
+ uint8_t *s1, uint8_t *s2)
+{
+ uint64_t mt;
+ int l;
+
+ l = my_fls32(div - 1);
+ mt = (uint64_t)(0x100000000ULL * ((1ULL << l) - div));
+ *m = (uint32_t)(mt / div + 1);
+ *s1 = (l > 1) ? 1U : (uint8_t)l;
+ *s2 = (l == 0) ? 0 : (uint8_t)(l - 1);
+}
+
+static inline uint32_t
+fast_divide32(uint32_t v, uint32_t div, uint32_t m, uint8_t s1,
+ uint8_t s2)
+{
+ uint32_t t;
+
+ t = (uint32_t)(((uint64_t)v * m) >> 32);
+ return (t + ((v - t) >> s1)) >> s2;
+}
+
+static inline uint32_t
+fast_remainder32(uint32_t v, uint32_t div, uint32_t m, uint8_t s1,
+ uint8_t s2)
+{
+
+ return v - div * fast_divide32(v, div, m, s1, s2);
+}
+#endif
+
#ifdef __weak_alias
__weak_alias(cdbw_close,_cdbw_close)
__weak_alias(cdbw_open,_cdbw_open)
@@ -279,30 +348,29 @@
}
/*
- * The algorithm below is based on paper
- * Cache-Oblivious Peeling of Random Hypergraphs by Djamal Belazzougui,
- * Paolo Boldi, Giuseppe Ottaviano, Rossano Venturini, and Sebastiano
- * Vigna.
- * http://zola.di.unipi.it/rossano/wp-content/papercite-data/pdf/dcc14.pdf
+ * For each vertex in the 3-graph, the incidence lists needs to be kept.
+ * Avoid storing the full list by just XORing the indices of the still
+ * incident edges and remember 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.
*/
-
-/*
- * Data type for a valid oriented edge (v0, v1, v2), v1 < v2.
- * The first vertex v0 is implicit and is determined by an index
- * of the corresponding element in the state->oedges array.
- * If the degree of v0 is greater than 1, other members don't
- * make sense because they're a result of XORing multiple values.
- */
-struct oedge {
- uint32_t degree; /* Degree of v0. */
- uint32_t verts[2]; /* v1 and v2 */
- uint32_t edge;
+struct vertex {
+ uint32_t degree;
+ uint32_t edges;
};
struct edge {
+ uint32_t vertices[3];
uint32_t idx;
-
- uint32_t left, middle, right;
};
struct state {
@@ -314,48 +382,40 @@
uint32_t *g;
char *visited;
- struct oedge *oedges;
+ struct vertex *vertices;
struct edge *edges;
uint32_t output_index;
uint32_t *output_order;
};
/*
- * Add (delta == 1) or remove (delta == -1) the edge e from vertex v0.
+ * Add (delta == 1) or remove (delta == -1) the edge e
+ * from the incidence lists.
*/
static inline void
-add_remove_edge(struct oedge *o, int delta, uint32_t e,
- uint32_t v0, uint32_t v1, uint32_t v2)
+change_edge(struct state *state, int delta, uint32_t e)
{
+ int i;
+ struct vertex *v;
+ struct edge *e_ = &state->edges[e];
- o[v0].verts[v1 < v2 ? 0 : 1] ^= v1;
- o[v0].verts[v1 < v2 ? 1 : 0] ^= v2;
- o[v0].degree += delta;
- o[v0].edge ^= e;
+ for (i = 0; i < 3; ++i) {
+ v = &state->vertices[e_->vertices[i]];
+ v->edges ^= e;
+ v->degree += delta;
+ }
}
static inline void
-add_edge(struct oedge *o, uint32_t e,
- uint32_t v0, uint32_t v1, uint32_t v2)
-{
-
- add_remove_edge(o, 1, e, v0, v1, v2);
-}
-
-static inline void
-remove_vertex(struct state *state, uint32_t v0)
+remove_vertex(struct state *state, uint32_t v)
{
- uint32_t e, v1, v2;
- struct oedge *o = state->oedges;
+ struct vertex *v_ = &state->vertices[v];
+ uint32_t e;
- if (o[v0].degree == 1) {
- e = o[v0].edge;
- v1 = o[v0].verts[0];
- v2 = o[v0].verts[1];
- o[v0].degree = 0;
- add_remove_edge(o, -1, e, v1, v0, v2);
- add_remove_edge(o, -1, e, v2, v0, v1);
+ if (v_->degree == 1) {
+ e = v_->edges;
state->output_order[--state->output_index] = e;
+ change_edge(state, -1, e);
}
}
@@ -365,39 +425,48 @@
struct key_hash_head *head;
struct key_hash *key_hash;
struct edge *e;
+ uint32_t entries_m;
+ uint8_t entries_s1, entries_s2;
uint32_t hashes[3];
size_t i;
+ int j;
- memset(state->oedges, 0, sizeof(struct oedge) * state->entries);
+ memset(state->vertices, 0, sizeof(*state->vertices) * state->entries);
e = state->edges;
+ fast_divide32_prepare(state->entries, &entries_m, &entries_s1, &entries_s2);
+
for (i = 0; i < cdbw->hash_size; ++i) {
head = &cdbw->hash[i];
SLIST_FOREACH(key_hash, head, link) {
- e->idx = key_hash->idx;
mi_vector_hash(key_hash->key, key_hash->keylen,
state->seed, hashes);
- e->left = hashes[0] % state->entries;
- e->middle = hashes[1] % state->entries;
- e->right = hashes[2] % state->entries;
- if (e->left == e->middle)
+ for (j = 0; j < 3; ++j) {
+ e->vertices[j] = fast_remainder32(hashes[j],
+ state->entries, entries_m, entries_s1,
+ entries_s2);
+ }
+
+ if (e->vertices[0] == e->vertices[1])
return -1;
- add_edge(state->oedges, e - state->edges,
- e->right, e->left, e->middle);
- if (e->left == e->right)
+ if (e->vertices[0] == e->vertices[2])
return -1;
- add_edge(state->oedges, e - state->edges,
- e->middle, e->left, e->right);
- if (e->middle == e->right)
+ if (e->vertices[1] == e->vertices[2])
return -1;
- add_edge(state->oedges, e - state->edges,
- e->left, e->middle, e->right);
-
+ e->idx = key_hash->idx;
++e;
}
}
+ /*
+ * Do the edge processing separately as there is a good chance for
+ * the degraded edge case above to happen, so avoid unnecessary
+ * work.
+ */
+ for (i = 0; i < state->keys; ++i)
+ change_edge(state, 1, i);
+
state->output_index = state->keys;
for (i = 0; i < state->entries; ++i)
remove_vertex(state, i);
@@ -406,9 +475,8 @@
while (i > 0 && i > state->output_index) {
--i;
e = state->edges + state->output_order[i];
- remove_vertex(state, e->left);
- remove_vertex(state, e->middle);
- remove_vertex(state, e->right);
+ for (j = 0; j < 3; ++j)
+ remove_vertex(state, e->vertices[j]);
}
return state->output_index == 0 ? 0 : -1;
@@ -420,28 +488,34 @@
struct edge *e;
size_t i;
+ uint32_t v0, v1, v2, entries_m;
+ uint8_t entries_s1, entries_s2;
+
+ fast_divide32_prepare(state->data_entries, &entries_m, &entries_s1, &entries_s2);
+
for (i = 0; i < state->keys; ++i) {
e = state->edges + state->output_order[i];
-
- if (!state->visited[e->left]) {
- state->g[e->left] =
- (2 * state->data_entries + e->idx
- - state->g[e->middle] - state->g[e->right])
- % state->data_entries;
- } else if (!state->visited[e->middle]) {
- state->g[e->middle] =
- (2 * state->data_entries + e->idx
- - state->g[e->left] - state->g[e->right])
- % state->data_entries;
Home |
Main Index |
Thread Index |
Old Index