Rendezvous Hashing是一种解决与一致散列相同问题的算法:
...允许客户端实现分布式协议的算法 在哪个站点(或代理)放置一个给定的对象。
Rendezvous哈希具有以下属性。
低开销:使用的哈希函数效率很高,因此客户端的开销非常低。
负载均衡:由于哈希函数是随机化的,因此n个站点中的每一个都同样可能接收到对象O.负载均匀 跨站点。
高命中率:由于所有客户同意将对象O放入同一站点SO,因此每次获取或将O放入SO中都会产生 命中率方面的最大效用。对象O将永远是 除非被SO的某些替代算法驱逐,否则会被发现。
- 醇>
最小中断:当站点发生故障时,只需要重新映射映射到该站点的对象。中断是最小的 水平,如证明 [1]
优秀。现在,我已经实现了一个修改后的版本,它将密钥集映射到文件箱before,这看起来非常有效:
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <assert.h>
#include <math.h>
//#include <blake2.h>
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define NODES 20
#define KEYS 16777216
#define CSKIP 1048576
__uint128_t hashes[NODES] = {0};
int count[NODES] = {0};
static inline int rendezvous (__uint128_t *pool, uint64_t id) {
register int x, bin = 0;
register __uint128_t max = 0, cache;
for (x = 0; x < NODES; x++) {
// blake2 ((void *) &cache, &pool[x], &id, sizeof (cache), sizeof (pool[x]), sizeof (id));
cache = pool[x] * ~id;
max = MAX (max, cache);
bin = max == cache ? x : bin;
}
return bin;
}
int main (int argc, char **argv) {
assert (fread (hashes, sizeof (hashes[0]), NODES, stdin) == NODES);
register int x, bin;
for (x = 0; x < KEYS; x++) {
bin = rendezvous (hashes, x);
count[bin]++;
if (!(x % CSKIP)) {fprintf (stdout, "%i\n", x);}
}
fputs ("\n\n\n\n\n\n\n\n", stdout);
float avg = (float) KEYS / (float) NODES;
float mad = 0;
for (x = 0; x < NODES; x++) {
fprintf (stdout, "Bin %i\t: %i\n", x, count[x]);
mad += abs ((float) count[x] - avg);
}
fputs ("\n\n\n\n\n\n\n\n", stdout);
fprintf (stdout, "avg: keys/bin\t= %f\n", avg);
mad /= (float) NODES;
fprintf (stdout, "mad: keys/bin\t= %f\n", mad);
float moa = mad / avg;
moa *= 100;
fprintf (stdout, "mad / avg\t= %05.2f %%\n", moa);
return 0;
}
使用gcc sbins.c -lm
进行编译并使用cat /dev/urandom | ./a.out
运行时,它似乎分布得相当不错:
1048576
2097152
3145728
4194304
5242880
6291456
7340032
8388608
9437184
10485760
11534336
12582912
13631488
14680064
15728640
Bin 0 : 838966
Bin 1 : 838596
Bin 2 : 839535
Bin 3 : 838835
Bin 4 : 839220
Bin 5 : 838759
Bin 6 : 838907
Bin 7 : 838903
Bin 8 : 838302
Bin 9 : 838726
Bin 10 : 838522
Bin 11 : 838034
Bin 12 : 839020
Bin 13 : 839334
Bin 14 : 838898
Bin 15 : 838905
Bin 16 : 838984
Bin 17 : 838753
Bin 18 : 838956
Bin 19 : 839061
avg: keys/bin = 838860.812500
mad: keys/bin = 235.500000
mad / avg = 00.03 %
到目前为止,这么好。不久之后(现在是AKA),我实际上需要编写使用它的代码。我知道我需要复制品,所以我想我会简单地存储,排序和返回列表,而不是总是选择最高值。我最终创建了一组类似的例程,基于我上面写的两个文件来完成这个任务:
#pragma once
// adapted from
// http://git.io/pgkqvw
// (haneefmubarak/experiments/rendezvous)
//=== Includes
#include <stdint.h>
#include <stdlib.h>
//=== Structures
typedef struct {
__uint128_t val;
int bin;
} rdv;
//=== Special
#define SORT_NAME rendezvous
#define SORT_TYPE rdv
#define SORT_CMP(x, y) ((((rdv)x).val) - (((rdv)y).val))
#include "./deps/sort/sort.h"
//=== Functions
static inline int *rendezvous (__uint128_t *pool, uint64_t id, int nodes) {
register int x;
// store temporary results
rdv *cache = malloc (nodes * sizeof (rdv));
if (!cache)
return NULL;
int *bin = malloc (nodes * sizeof (int));
if (!bin) {
free (cache);
return NULL;
}
// calculate for each server
for (x = 0; x < nodes; x++) {
cache[x].val = pool[x] * ~id;
cache[x].bin = x;
}
// sort the results
rendezvous_tim_sort (cache, nodes);
// extract the results
for (x = 0; x < nodes; x++) {
bin[x] = cache[x].bin;
}
// cleanup
free (cache);
return bin;
}
和测试程序:
#include <stdio.h>
#include <assert.h>
#include "../rendezvous.h"
#define NODES 20
#define REPFAC 4
#define KEYS 16777216
#define CSKIP 1048576
__uint128_t hashes[NODES] = {0};
int count[REPFAC][NODES] = {{0}};
int main (int argc, char **argv) {
assert (fread (hashes, sizeof (hashes[0]), NODES, stdin) == NODES);
register uint64_t x, y;
register int *bin;
for (x = 0; x < KEYS; x++) {
bin = rendezvous (hashes, x, NODES);
for (y = 0; y < REPFAC; y++)
count[y][bin[y]] += 1;
free (bin);
if (!(x % CSKIP)) {fprintf (stdout, "%llu\n", x);}
}
for (x = 0; x < REPFAC; x++) {
fputs ("\n\n\n\n\n\n\n\n", stdout);
fprintf (stdout, "RepSet %llu\n", x);
for (y = 0; y < NODES; y++)
fprintf (stdout, "Bin %llu\t: %i\n", y, count[x][y]);
}
return 0;
}
但是,使用gcc rendezvous.c
编译此文件并使用cat /dev/urandom | ./a.out
运行时,代码似乎在EPICLY失败:
0
1048576
2097152
3145728
4194304
5242880
6291456
7340032
8388608
9437184
10485760
11534336
12582912
13631488
14680064
15728640
RepSet 0
Bin 0 : 0
Bin 1 : 16777216
Bin 2 : 0
Bin 3 : 0
Bin 4 : 0
Bin 5 : 0
Bin 6 : 0
Bin 7 : 0
Bin 8 : 0
Bin 9 : 0
Bin 10 : 0
Bin 11 : 0
Bin 12 : 0
Bin 13 : 0
Bin 14 : 0
Bin 15 : 0
Bin 16 : 0
Bin 17 : 0
Bin 18 : 0
Bin 19 : 0
RepSet 1
Bin 0 : 0
Bin 1 : 0
Bin 2 : 16777216
Bin 3 : 0
Bin 4 : 0
Bin 5 : 0
Bin 6 : 0
Bin 7 : 0
Bin 8 : 0
Bin 9 : 0
Bin 10 : 0
Bin 11 : 0
Bin 12 : 0
Bin 13 : 0
Bin 14 : 0
Bin 15 : 0
Bin 16 : 0
Bin 17 : 0
Bin 18 : 0
Bin 19 : 0
RepSet 2
Bin 0 : 0
Bin 1 : 0
Bin 2 : 0
Bin 3 : 16777216
Bin 4 : 0
Bin 5 : 0
Bin 6 : 0
Bin 7 : 0
Bin 8 : 0
Bin 9 : 0
Bin 10 : 0
Bin 11 : 0
Bin 12 : 0
Bin 13 : 0
Bin 14 : 0
Bin 15 : 0
Bin 16 : 0
Bin 17 : 0
Bin 18 : 0
Bin 19 : 0
RepSet 3
Bin 0 : 0
Bin 1 : 0
Bin 2 : 0
Bin 3 : 0
Bin 4 : 16777216
Bin 5 : 0
Bin 6 : 0
Bin 7 : 0
Bin 8 : 0
Bin 9 : 0
Bin 10 : 0
Bin 11 : 0
Bin 12 : 0
Bin 13 : 0
Bin 14 : 0
Bin 15 : 0
Bin 16 : 0
Bin 17 : 0
Bin 18 : 0
Bin 19 : 0
我试图弄清楚问题可能是什么原因。我甚至认为我可能有一个优先问题或count[y][bin[y]]++
的东西,所以我改变了,但无济于事。我认为这个错误发生在rendezvous.h
,但此时我已经大部分时间都没有想法,而且还有一个线索。
理想情况下,第二个程序的输出应该均匀分布,类似于第一个程序。我无法找到单个垃圾箱中唯一被填充的地方。
那么我怎样才能让程序正常运行?任何帮助表示赞赏。
答案 0 :(得分:1)
重新连接比较器算法。请记住,您使用的是无符号值。因此,这是一个比较器:
#define SORT_CMP(x, y) ((((rdv)x).val) - (((rdv)y).val))
如果x.val
小于y.val
,将下溢。反过来,这使得任何人都假定标准的“负,零,正”结果来指示“更少,更平等,更大”的结果将会失败。它们都将变得“更大”(假设下溢返回一个正数;陷阱条件不能承受;技术上是它的UB),或者至多是“等于”,如果它们......好......等于。 / p>
我修改了你的代码以使用qsort()
和简单的比较器:
int cmp_rdv(const void *arg1, const void* arg2)
{
const rdv* lhs = arg1;
const rdv* rhs = arg2;
return (lhs->val < rhs->val) ? -1 : (rhs->val < lhs->val);
}
然后按原样调用它,抛出旧算法:
// calculate for each server
for (x = 0; x < nodes; x++) {
cache[x].val = pool[x] * ~id;
cache[x].bin = x;
}
// sort the results
qsort(cache, nodes, sizeof(*cache), cmp_rdv);
// extract the results
for (x = 0; x < nodes; x++) {
bin[x] = cache[x].bin;
}
结果如下,可能是你正在寻找的东西(我清理了墙 - 换行图并为我自己的理智添加了一些表格)。
0
1048576
2097152
3145728
4194304
5242880
6291456
7340032
8388608
9437184
10485760
11534336
12582912
13631488
14680064
15728640
RepSet 0
Bin 0 : 838214
Bin 1 : 838719
Bin 2 : 838792
Bin 3 : 839251
Bin 4 : 838699
Bin 5 : 838436
Bin 6 : 838827
Bin 7 : 839038
Bin 8 : 839062
Bin 9 : 838787
Bin 10 : 839000
Bin 11 : 838683
Bin 12 : 839024
Bin 13 : 838922
Bin 14 : 838847
Bin 15 : 839312
Bin 16 : 838980
Bin 17 : 838607
Bin 18 : 839191
Bin 19 : 838825
RepSet 1
Bin 0 : 840405
Bin 1 : 838603
Bin 2 : 838612
Bin 3 : 837891
Bin 4 : 839700
Bin 5 : 839559
Bin 6 : 838918
Bin 7 : 838834
Bin 8 : 839128
Bin 9 : 838711
Bin 10 : 838930
Bin 11 : 839856
Bin 12 : 838654
Bin 13 : 837914
Bin 14 : 838665
Bin 15 : 837965
Bin 16 : 838278
Bin 17 : 839167
Bin 18 : 837817
Bin 19 : 839609
RepSet 2
Bin 0 : 838054
Bin 1 : 838727
Bin 2 : 838852
Bin 3 : 839306
Bin 4 : 837806
Bin 5 : 838103
Bin 6 : 838929
Bin 7 : 837990
Bin 8 : 837851
Bin 9 : 838877
Bin 10 : 838228
Bin 11 : 837360
Bin 12 : 839282
Bin 13 : 840473
Bin 14 : 839353
Bin 15 : 840022
Bin 16 : 840401
Bin 17 : 838826
Bin 18 : 840139
Bin 19 : 838637
RepSet 3
Bin 0 : 838545
Bin 1 : 840246
Bin 2 : 839210
Bin 3 : 839441
Bin 4 : 838555
Bin 5 : 838835
Bin 6 : 838539
Bin 7 : 839562
Bin 8 : 838275
Bin 9 : 839164
Bin 10 : 839194
Bin 11 : 840005
Bin 12 : 838463
Bin 13 : 837821
Bin 14 : 839498
Bin 15 : 838393
Bin 16 : 837376
Bin 17 : 838632
Bin 18 : 839393
Bin 19 : 838069
我确实没有下载你提到的排序源只是因为我很懒(为什么说谎?)。但是我确实检查了当你不提供默认值时的默认值,它是:
#define SORT_CMP(x, y) ((x) < (y) ? -1 : ((x) == (y) ? 0 : 1))
确认我怀疑他们需要我们都习惯提供的标准小于零/零/大于零的三重奏。你可能需要你的算法匹配这种格式,它会给你你想要的东西。
祝你好运。