1个不稳定版本
0.1.0 | 2022年2月16日 |
---|
#9 in #sharded
用于 shardize
10KB
160 行
shardize
实验,看看我们是否可以用宏创建现有容器的分片形式。
如果它可行,这将允许我们在多线程场景中减少竞争,通过确保锁(或对无锁资源的竞争)只应用于单个分片而不是整个容器。
性能
在一个非常简单的测试中,我们比较了以下两个结构
let m1 = Mutex::new(HashMap::<usize, String>::new());
和
let m2 = ShardedMutexHashmap::<Mutex<HashMap<usize, String>>, 10>::new(&my_shard_key);
m2
是一个具有 get
和 set
方法与 m1
相同的容器,但其底层实现使用了10个不同的 HashMap
,每个都在自己的 Mutex
中。
当我们启动10个线程并读写1000个值到 m1
和 m2
中时,我们可以看到 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
宏基于 MyTrait
和 MyTrait
的实现生成的。生成的代码为每个分片创建底层容器(这里是一个 Mutex<HashMap<...>>
)的副本,并通过调用 my_shard_key
在 get
或 set
传入的键来查找合适的分片,从中存储/检索值。
许可
版权所有2022 John Bell 和 Andy Balaam,根据您的选择,受Apache许可证版本2.0或MIT许可证。
行为准则
请注意,该项目附带一份贡献者行为准则。参与此项目即表示您同意遵守其条款。
依赖项
~1.5MB
~35K SLoC