#container #sharded #lock-free #sharding

shardize

生成代码以创建现有容器的分片形式的 proc 宏

1 个不稳定版本

0.1.0 2022年2月16日

#736 in 并发

MIT/Apache

18KB
175

shardize

关于是否可以使用宏来创建现有容器的分片形式的实验。

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

性能

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

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

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

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

当我们启动 10 个线程并向 m1m2 中写入 1000 个值时,我们可以看到 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<...>>)的一个副本,并从适当的分片中存储/检索值,它通过在 getset 中传入的键上调用 my_shard_key 来找到它。

许可证

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

行为准则

请注意,本项目遵循 贡献者行为准则。通过参与本项目,您同意遵守其条款。

Contributor Covenant

依赖关系

~1.5MB
~35K SLoC