Skip to content

Commit c5c9a38

Browse files
stepanchegfacebook-github-bot
authored andcommitted
Reimplement SmallMap (again)
Summary: Change `VecMap` (which is linear part of `SmallMap`) from ``` Vec<(Hash, K, V)> ``` to ``` Vec2<(K, V), Hash> ``` The idea is this: hash is 32 bit. But because of padding, it occupies 64 bits. So by changing layout from ``` H, K, V, H, K, V, .... ``` to ``` K, V, K, V, ..., H, H, ... ``` we save 4 bytes per entry. At cost of slower operations with `SmallMap`. However `SmallMap` is meant to be small, not fast, so this is probably acceptable. Basically we trade 2% memory for 0.5% CPU. Also, additional savings of memory are possible: because hashes are stored in contiguous memory now, we can use SIMD to scan them. So we can increase the amount of entries with scan linearly before allocating the index without hurting much performance (currently we create the index at 12 entries). Also, we can now implement storing `RawTable` next to `Vec2` data, to reduce the size of empty `SmallMap` from 4 words to 3. Reviewed By: ndmitchell Differential Revision: D40557486 fbshipit-source-id: 44ef3dd8de0b72171fa084a417dbf7376c793bae
1 parent 531cc29 commit c5c9a38

File tree

2 files changed

+431
-73
lines changed

2 files changed

+431
-73
lines changed

starlark_map/src/vec2/iter.rs

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,77 +15,124 @@
1515
* limitations under the License.
1616
*/
1717

18+
use std::marker::PhantomData;
19+
use std::ptr;
20+
use std::ptr::NonNull;
1821
use std::slice;
1922

2023
use gazebo::prelude::*;
2124

25+
use crate::vec2::Vec2;
26+
2227
#[derive(Clone_)]
2328
pub(crate) struct Iter<'a, K, V> {
2429
pub(crate) keys: slice::Iter<'a, K>,
25-
pub(crate) values: slice::Iter<'a, V>,
30+
pub(crate) values: *const V,
31+
pub(crate) _marker: PhantomData<slice::Iter<'a, V>>,
2632
}
2733

34+
unsafe impl<K: Sync, V: Sync> Sync for Iter<'_, K, V> {}
35+
unsafe impl<K: Sync, V: Sync> Send for Iter<'_, K, V> {}
36+
2837
impl<'a, K, V> Iterator for Iter<'a, K, V> {
2938
type Item = (&'a K, &'a V);
3039

40+
#[inline]
3141
fn next(&mut self) -> Option<Self::Item> {
32-
match (self.keys.next(), self.values.next()) {
33-
(Some(k), Some(v)) => Some((k, v)),
34-
_ => None,
35-
}
42+
let key = self.keys.next()?;
43+
let value = unsafe { &*self.values };
44+
self.values = unsafe { self.values.add(1) };
45+
Some((key, value))
3646
}
3747

48+
#[inline]
3849
fn size_hint(&self) -> (usize, Option<usize>) {
3950
self.keys.size_hint()
4051
}
4152
}
4253

4354
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
55+
#[inline]
4456
fn len(&self) -> usize {
4557
self.keys.len()
4658
}
4759
}
4860

4961
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
62+
#[inline]
5063
fn next_back(&mut self) -> Option<Self::Item> {
51-
match (self.keys.next_back(), self.values.next_back()) {
52-
(Some(k), Some(v)) => Some((k, v)),
53-
_ => None,
54-
}
64+
let key = self.keys.next_back()?;
65+
let value = unsafe { &*self.values.add(self.keys.len()) };
66+
Some((key, value))
5567
}
5668
}
5769

5870
pub(crate) struct IntoIter<K, V> {
59-
pub(crate) keys: std::vec::IntoIter<K>,
60-
pub(crate) values: std::vec::IntoIter<V>,
71+
// TODO: make NonNull
72+
pub(crate) keys_begin: *mut K,
73+
pub(crate) values_begin: *mut V,
74+
pub(crate) values_end: *mut V,
75+
pub(crate) values_ptr: NonNull<V>,
76+
pub(crate) cap: usize,
6177
}
6278

79+
unsafe impl<K: Send, V: Send> Send for IntoIter<K, V> {}
80+
unsafe impl<K: Sync, V: Sync> Sync for IntoIter<K, V> {}
81+
6382
impl<K, V> Iterator for IntoIter<K, V> {
6483
type Item = (K, V);
6584

85+
#[inline]
6686
fn next(&mut self) -> Option<Self::Item> {
67-
match (self.keys.next(), self.values.next()) {
68-
(Some(k), Some(v)) => Some((k, v)),
69-
_ => None,
87+
if self.values_begin == self.values_end {
88+
None
89+
} else {
90+
unsafe {
91+
let k = ptr::read(self.keys_begin);
92+
let v = ptr::read(self.values_begin);
93+
self.keys_begin = self.keys_begin.add(1);
94+
self.values_begin = self.values_begin.add(1);
95+
Some((k, v))
96+
}
7097
}
7198
}
7299

100+
#[inline]
73101
fn size_hint(&self) -> (usize, Option<usize>) {
74-
self.keys.size_hint()
102+
let rem = self.len();
103+
(rem, Some(rem))
104+
}
105+
}
106+
107+
impl<K, V> Drop for IntoIter<K, V> {
108+
fn drop(&mut self) {
109+
unsafe {
110+
let rem = self.len();
111+
ptr::drop_in_place(slice::from_raw_parts_mut(self.keys_begin, rem));
112+
ptr::drop_in_place(slice::from_raw_parts_mut(self.values_begin, rem));
113+
Vec2::<K, V>::dealloc_impl(self.values_ptr, self.cap);
114+
}
75115
}
76116
}
77117

78118
impl<K, V> ExactSizeIterator for IntoIter<K, V> {
119+
#[inline]
79120
fn len(&self) -> usize {
80-
self.keys.len()
121+
unsafe { self.values_end.offset_from(self.values_begin) as usize }
81122
}
82123
}
83124

84125
impl<K, V> DoubleEndedIterator for IntoIter<K, V> {
85126
fn next_back(&mut self) -> Option<Self::Item> {
86-
match (self.keys.next_back(), self.values.next_back()) {
87-
(Some(k), Some(v)) => Some((k, v)),
88-
_ => None,
127+
if self.values_begin == self.values_end {
128+
None
129+
} else {
130+
unsafe {
131+
self.values_end = self.values_end.sub(1);
132+
let k = ptr::read(self.keys_begin.add(self.len()));
133+
let v = ptr::read(self.values_end);
134+
Some((k, v))
135+
}
89136
}
90137
}
91138
}

0 commit comments

Comments
 (0)