1个不稳定版本

0.1.0 2022年2月16日

#9 in #sharded


用于 shardize

MIT/Apache

10KB
160

shardize

实验,看看我们是否可以用宏创建现有容器的分片形式。

如果它可行,这将允许我们在多线程场景中减少竞争,通过确保锁(或对无锁资源的竞争)只应用于单个分片而不是整个容器。

性能

在一个非常简单的测试中,我们比较了以下两个结构

let m1 = Mutex::new(HashMap::<usize, String>::new());

let m2 = ShardedMutexHashmap::<Mutex<HashMap<usize, String>>, 10>::new(&my_shard_key);

m2 是一个具有 getset 方法与 m1 相同的容器,但其底层实现使用了10个不同的 HashMap,每个都在自己的 Mutex 中。

当我们启动10个线程并读写1000个值到 m1m2 中时,我们可以看到 m2 的性能要好得多

$ cargo bench
...
test read_from_mutex_hashmap         ... bench:   1,032,460 ns/iter (+/- 835,085)
test read_from_sharded_mutex_hashmap ... bench:     302,203 ns/iter (+/- 22,340)
test write_to_mutex_hashmap          ... bench:   1,221,228 ns/iter (+/- 1,394,711)
test write_to_sharded_mutex_hashmap  ... bench:     378,115 ns/iter (+/- 21,017)

如何使用

为了能够像上面那样使用 ShardedMutexHashmap,我们需要提供以下代码

#[shardize(ShardedMutexHashmap)]
trait MyTrait: Default {
    fn get(&self, key: usize) -> String;
    fn set(&self, key: usize, value: String);
}

impl MyTrait for Mutex<HashMap<usize, String>> {
    fn get(&self, key: usize) -> String {
        self.lock().unwrap().get(&key).unwrap().clone()
    }

    fn set(&self, key: usize, value: String) {
        self.lock().unwrap().insert(key, value);
    }
}

fn my_shard_key(key: usize) -> usize {
    key
}

ShardedMutexHashmap 是由 shardize 宏基于 MyTraitMyTrait 的实现生成的。生成的代码为每个分片创建底层容器(这里是一个 Mutex<HashMap<...>>)的副本,并通过调用 my_shard_keygetset 传入的键来查找合适的分片,从中存储/检索值。

许可

版权所有2022 John Bell 和 Andy Balaam,根据您的选择,受Apache许可证版本2.0或MIT许可证

行为准则

请注意,该项目附带一份贡献者行为准则。参与此项目即表示您同意遵守其条款。

Contributor Covenant

依赖项

~1.5MB
~35K SLoC