Skip to content

Commit 3ea65d0

Browse files
committed
merge sparse and min-cut
2 parents 1a507ce + 3c1f093 commit 3ea65d0

26 files changed

Lines changed: 1676 additions & 337 deletions

CMakeLists.txt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,16 @@ endif()
9191
FetchContent_MakeAvailable(GutterTree StreamingUtilities VieCut tlx)
9292

9393
# AVAILABLE COMPILATION DEFINITIONS:
94-
# VERIFY_SAMPLES_F Use a deterministic connected-components
95-
# algorithm to verify post-processing.
9694
# NO_EAGER_DSU Do not use the eager DSU query optimization
9795
# if this flag is present.
9896
# L0_SAMPLING Run the CubeSketch l0 sampling algorithm
9997
# to ensure that we sample uniformly.
10098
# Otherwise, run a support finding algorithm.
99+
# L0_FULLY_DENSE Fully allocate the sketch matrix at the beginning
100+
# of the program. If this flag is not used, sketches
101+
# are allocated dynamically.
102+
# VERIFY_SAMPLES_F Use a deterministic connected-components
103+
# algorithm to verify post-processing.
101104
#
102105
# Example:
103106
# cmake -DCMAKE_CXX_FLAGS="-DL0_SAMPLING" ..
@@ -109,7 +112,8 @@ add_library(GraphZeppelin
109112
src/return_types.cpp
110113
src/driver_configuration.cpp
111114
src/cc_alg_configuration.cpp
112-
src/sketch.cpp
115+
src/sparse_sketch.cpp
116+
src/dense_sketch.cpp
113117
src/util.cpp)
114118
add_dependencies(GraphZeppelin GutterTree StreamingUtilities VieCut tlx)
115119
target_link_libraries(GraphZeppelin PUBLIC xxhash GutterTree StreamingUtilities VieCut tlx)
@@ -125,7 +129,8 @@ add_library(GraphZeppelinVerifyCC
125129
src/return_types.cpp
126130
src/driver_configuration.cpp
127131
src/cc_alg_configuration.cpp
128-
src/sketch.cpp
132+
src/sparse_sketch.cpp
133+
src/dense_sketch.cpp
129134
src/util.cpp
130135
test/util/graph_verifier.cpp)
131136
add_dependencies(GraphZeppelinVerifyCC GutterTree StreamingUtilities VieCut tlx)

include/bucket.h

Lines changed: 69 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,94 @@
11
#pragma once
2-
#include <vector>
32
#include <xxhash.h>
3+
44
#include <iostream>
5+
#include <vector>
6+
57
#include "types.h"
68

7-
#pragma pack(push,1)
9+
#pragma pack(push, 1)
810
struct Bucket {
911
vec_t alpha;
1012
vec_hash_t gamma;
1113
};
1214
#pragma pack(pop)
1315

14-
namespace Bucket_Boruvka {
15-
static constexpr size_t col_hash_bits = sizeof(col_hash_t) * 8;
16-
/**
17-
* Hashes the column index and the update index together to determine the depth of an update
18-
* This is used as a parameter to Bucket::contains.
19-
* @param update_idx Vector index to update
20-
* @param seed_and_col Combination of seed and column
21-
* @param max_depth The maximum depth to return
22-
* @return The hash of update_idx using seed_and_col as a seed.
23-
*/
24-
inline static col_hash_t get_index_depth(const vec_t update_idx, const long seed_and_col,
25-
const vec_hash_t max_depth);
16+
namespace SketchBucket {
2617

27-
/**
28-
* Hashes the index for checksumming
29-
* This is used to as a parameter to Bucket::update
30-
* @param index Vector index to update
31-
* @param seed The seed of the Sketch this Bucket belongs to
32-
* @return The depth of the bucket to update
33-
*/
34-
inline static vec_hash_t get_index_hash(const vec_t index, const long sketch_seed);
35-
36-
/**
37-
* Checks whether a Bucket is good, assuming the Bucket contains all elements.
38-
* @param bucket The bucket to check
39-
* @param sketch_seed The seed of the Sketch this Bucket belongs to.
40-
* @return true if this Bucket is good, else false.
41-
*/
42-
inline static bool is_good(const Bucket &bucket, const long sketch_seed);
43-
44-
/**
45-
* Updates a Bucket with the given update index
46-
* @param bucket The bucket to update
47-
* @param update_idx The update index
48-
* @param update_hash The hash of the update index, generated with Bucket::index_hash.
49-
*/
50-
inline static void update(Bucket& bucket, const vec_t update_idx,
51-
const vec_hash_t update_hash);
52-
} // namespace Bucket_Boruvka
18+
struct Depths {
19+
private:
20+
col_hash_t depths[2];
21+
public:
22+
col_hash_t& operator[](size_t i) { return depths[i]; }
23+
};
5324

54-
inline col_hash_t Bucket_Boruvka::get_index_depth(const vec_t update_idx, const long seed_and_col,
55-
const vec_hash_t max_depth) {
25+
static constexpr size_t col_hash_bits = sizeof(col_hash_t) * 8;
26+
/**
27+
* Hashes the column index and the update index together to determine the depth of an update
28+
* This is used as a parameter to Bucket::contains.
29+
* @param update_idx Vector index to update
30+
* @param seed_and_col Combination of seed and column
31+
* @param max_depth The maximum depth to return
32+
* @return The hash of update_idx using seed_and_col as a seed.
33+
*/
34+
inline static col_hash_t get_index_depth(const vec_t update_idx, const long seed_and_col,
35+
const vec_hash_t max_depth) {
5636
col_hash_t depth_hash = col_hash(&update_idx, sizeof(vec_t), seed_and_col);
57-
depth_hash |= (1ull << max_depth); // assert not > max_depth by ORing
37+
depth_hash |= (1ull << max_depth); // assert not > max_depth by ORing
5838
return __builtin_ctzll(depth_hash);
5939
}
6040

61-
inline vec_hash_t Bucket_Boruvka::get_index_hash(const vec_t update_idx, const long sketch_seed) {
62-
return vec_hash(&update_idx, sizeof(vec_t), sketch_seed);
41+
inline static Depths get_index_depths(vec_t update_idx, size_t seed, col_hash_t max_depth) {
42+
uint64_t depth_hash = col_hash(&update_idx, sizeof(vec_t), seed);
43+
Depths ret;
44+
45+
depth_hash |= (1ull << max_depth); // assert not > max_depth by ORing
46+
ret[0] = __builtin_ctzll(depth_hash);
47+
48+
// shift hash over and reassert max_depth
49+
depth_hash >>= 32;
50+
depth_hash |= (1ull << max_depth);
51+
ret[1] = __builtin_ctzll(depth_hash);
52+
53+
return ret;
6354
}
6455

65-
inline bool Bucket_Boruvka::is_good(const Bucket &bucket, const long sketch_seed) {
56+
/**
57+
* Hashes the index for checksumming
58+
* This is used to as a parameter to Bucket::update
59+
* @param index Vector index to update
60+
* @param seed The seed of the Sketch this Bucket belongs to
61+
* @return The depth of the bucket to update
62+
*/
63+
inline static vec_hash_t get_index_hash(const vec_t index, const long sketch_seed) {
64+
return vec_hash(&index, sizeof(vec_t), sketch_seed);
65+
}
66+
67+
/**
68+
* Checks whether a Bucket is good.
69+
* @param bucket The bucket to check
70+
* @param sketch_seed The seed of the Sketch this Bucket belongs to.
71+
* @return true if this Bucket is good, else false.
72+
*/
73+
inline static bool is_good(const Bucket &bucket, const long sketch_seed) {
6674
return bucket.gamma == get_index_hash(bucket.alpha, sketch_seed);
6775
}
6876

69-
inline void Bucket_Boruvka::update(Bucket& bucket, const vec_t update_idx,
70-
const vec_hash_t update_hash) {
77+
/**
78+
* Checks whether a Bucket is empty.
79+
* @return true if this Bucket is empty (alpha and gamma == 0), else false.
80+
*/
81+
inline static bool is_empty(const Bucket &bucket) { return bucket.alpha == 0 && bucket.gamma == 0; }
82+
83+
/**
84+
* Updates a Bucket with the given update index
85+
* @param bucket The bucket to update
86+
* @param update_idx The update index
87+
* @param update_hash The hash of the update index, generated with Bucket::index_hash.
88+
*/
89+
inline static void update(Bucket &bucket, const vec_t update_idx, const vec_hash_t update_hash) {
7190
bucket.alpha ^= update_idx;
7291
bucket.gamma ^= update_hash;
7392
}
93+
94+
} // namespace SketchBucket

include/cc_sketch_alg.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,11 @@ class CCSketchAlg {
209209
/**
210210
* Apply a batch of updates that have already been processed into a sketch delta.
211211
* Specifically, the delta is in the form of a pointer to raw bucket data.
212-
* param: src_vertex The vertex where the all edges originate.
213-
* param: raw_buckets Pointer to the array of buckets from the delta sketch
212+
* @param src_vertex The vertex where the all edges originate.
213+
* @param raw_buckets Pointer to the array of buckets from the delta sketch
214+
* @param num_buckets Size of raw_buckets array in number of buckets
214215
*/
215-
void apply_raw_buckets_update(node_id_t src_vertex, Bucket *raw_buckets);
216+
void apply_raw_buckets_update(node_id_t src_vertex, Bucket *raw_buckets, size_t num_buckets);
216217

217218
/**
218219
* The function performs a direct update to the associated sketch.

include/dense_sketch.h

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#pragma once
2+
#include <graph_zeppelin_common.h>
3+
#include <gtest/gtest_prod.h>
4+
#include <sys/mman.h>
5+
6+
#include <fstream>
7+
#include <unordered_set>
8+
#include <cmath>
9+
#include <cassert>
10+
#include <mutex>
11+
12+
#include "util.h"
13+
#include "bucket.h"
14+
#include "sketch_types.h"
15+
16+
/**
17+
* Sketch for graph processing, either CubeSketch or CameoSketch.
18+
* Sub-linear representation of a vector.
19+
*/
20+
class DenseSketch {
21+
private:
22+
const uint64_t seed; // seed for hash functions
23+
size_t num_samples; // number of samples we can perform
24+
size_t cols_per_sample; // number of columns to use on each sample
25+
size_t num_columns; // Total number of columns. (product of above 2)
26+
size_t bkt_per_col; // maximum number of buckets per column (max number of rows)
27+
size_t num_buckets; // number of total buckets product of above two
28+
size_t sample_idx = 0; // number of samples performed so far
29+
30+
// Allocated buckets
31+
Bucket* buckets;
32+
33+
inline Bucket& deterministic_bucket() {
34+
return buckets[0];
35+
}
36+
inline const Bucket& deterministic_bucket() const {
37+
return buckets[0];
38+
}
39+
40+
// return the bucket at a particular index in bucket array
41+
inline Bucket& bucket(size_t col, size_t row) {
42+
return buckets[col * bkt_per_col + row + 1];
43+
}
44+
inline const Bucket& bucket(size_t col, size_t row) const {
45+
return buckets[col * bkt_per_col + row + 1];
46+
}
47+
48+
public:
49+
/**
50+
* The below constructors use vector length as their input. However, in graph sketching our input
51+
* is the number of vertices. This function converts from number of graph vertices to vector
52+
* length.
53+
* @param num_vertices Number of graph vertices
54+
* @return The length of the vector to sketch
55+
*/
56+
static vec_t calc_vector_length(node_id_t num_vertices) {
57+
return ceil(double(num_vertices) * (num_vertices - 1) / 2);
58+
}
59+
60+
/**
61+
* This function computes the number of samples a Sketch should support in order to solve
62+
* connected components. Optionally, can increase or decrease the number of samples by a
63+
* multiplicative factor.
64+
* @param num_vertices Number of graph vertices
65+
* @param f Multiplicative sample factor
66+
* @return The number of samples
67+
*/
68+
static size_t calc_cc_samples(node_id_t num_vertices, double f) {
69+
return std::max(size_t(18), (size_t) ceil(f * log2(num_vertices) / num_samples_div));
70+
}
71+
72+
/**
73+
* Construct a sketch object
74+
* @param vector_len Length of the vector we are sketching
75+
* @param seed Random seed of the sketch
76+
* @param num_samples [Optional] Number of samples this sketch supports (default = 1)
77+
* @param cols_per_sample [Optional] Number of sketch columns for each sample (default = 1)
78+
*/
79+
DenseSketch(vec_t vector_len, uint64_t seed, size_t num_samples = 1,
80+
size_t cols_per_sample = default_cols_per_sample);
81+
82+
/**
83+
* Construct a sketch from a serialized stream
84+
* @param vector_len Length of the vector we are sketching
85+
* @param seed Random seed of the sketch
86+
* @param binary_in Stream holding serialized sketch object
87+
* @param num_buckets Number of buckets in serialized sketch
88+
* @param num_samples [Optional] Number of samples this sketch supports (default = 1)
89+
* @param cols_per_sample [Optional] Number of sketch columns for each sample (default = 1)
90+
*/
91+
DenseSketch(vec_t vector_len, uint64_t seed, std::istream& binary_in, size_t num_buckets,
92+
size_t num_samples = 1, size_t cols_per_sample = default_cols_per_sample);
93+
94+
/**
95+
* Sketch copy constructor
96+
* @param s The sketch to copy.
97+
*/
98+
DenseSketch(const DenseSketch& s);
99+
100+
~DenseSketch();
101+
102+
/**
103+
* Update a sketch based on information about one of its indices.
104+
* @param update the point update.
105+
*/
106+
void update(const vec_t update);
107+
108+
/**
109+
* Function to sample from the sketch.
110+
* cols_per_sample determines the number of columns we allocate to this query
111+
* @return A pair with the result index and a code indicating the type of result.
112+
*/
113+
SketchSample sample();
114+
115+
/**
116+
* Function to sample from the appropriate columns to return 1 or more non-zero indices
117+
* @return A pair with the result indices and a code indicating the type of result.
118+
*/
119+
ExhaustiveSketchSample exhaustive_sample();
120+
121+
std::mutex mutex; // lock the sketch for applying updates in multithreaded processing
122+
123+
/**
124+
* In-place merge function.
125+
* @param other Sketch to merge into caller
126+
*/
127+
void merge(const DenseSketch &other);
128+
129+
/**
130+
* In-place range merge function. Updates the caller Sketch.
131+
* The range merge only merges some of the Sketches
132+
* This function should only be used if you know what you're doing
133+
* @param other Sketch to merge into caller
134+
* @param start_sample Index of first sample to merge
135+
* @param n_samples Number of samples to merge
136+
*/
137+
void range_merge(const DenseSketch &other, size_t start_sample, size_t n_samples);
138+
139+
/**
140+
* Perform an in-place merge function without another Sketch and instead
141+
* use a raw bucket memory.
142+
* We also allow for only a portion of the buckets to be merge at once
143+
* @param raw_bucket Raw bucket data to merge into this sketch
144+
* @param n_raw_buckets Size of raw_buckets in number of Bucket data-structures
145+
*/
146+
void merge_raw_bucket_buffer(const Bucket *raw_buckets, size_t n_raw_buckets);
147+
148+
/**
149+
* Zero out all the buckets of a sketch.
150+
*/
151+
void zero_contents();
152+
153+
friend bool operator==(const DenseSketch& sketch1, const DenseSketch& sketch2);
154+
friend std::ostream& operator<<(std::ostream& os, const DenseSketch& sketch);
155+
156+
/**
157+
* Serialize the sketch to a binary output stream.
158+
* @param binary_out the stream to write to.
159+
*/
160+
void serialize(std::ostream& binary_out) const;
161+
162+
inline void reset_sample_state() {
163+
sample_idx = 0;
164+
}
165+
166+
// return the size of the sketching datastructure in bytes (just the buckets, not the metadata)
167+
inline size_t bucket_array_bytes() const {
168+
return num_buckets * sizeof(Bucket);
169+
}
170+
171+
// return the size of a sketch given vector size n and number of samples s
172+
static size_t estimate_bytes(size_t n, size_t s) {
173+
return (1 + calc_bkt_per_col(n) * s * default_cols_per_sample) * sizeof(Bucket);
174+
}
175+
176+
inline const Bucket* get_readonly_bucket_ptr() const { return (const Bucket*) buckets; }
177+
inline uint64_t get_seed() const { return seed; }
178+
inline size_t column_seed(size_t column_idx) const { return seed + column_idx * 5; }
179+
inline size_t checksum_seed() const { return seed; }
180+
inline size_t get_columns() const { return num_columns; }
181+
inline size_t get_buckets() const { return num_buckets; }
182+
inline size_t get_num_samples() const { return num_samples; }
183+
184+
static size_t calc_bkt_per_col(size_t n) { return ceil(log2(n)) + 1; }
185+
186+
static constexpr size_t default_cols_per_sample = 1;
187+
static constexpr double num_samples_div = 1 - log2(2 - 0.8);
188+
};

0 commit comments

Comments
 (0)