Skip to content

Commit cc0a191

Browse files
authored
Merge pull request #66 from orxfun/minor-documentation-fixes
minor documentation fixes
2 parents 36e80a0 + 8067ffb commit cc0a191

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

README.md

+19-19
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
An efficient dynamic capacity vector with pinned element guarantees.
77

8-
A **SplitVec** implements [`PinnedVec`](https://crates.io/crates/orx-split-vec); you may read the detailed information about [pinned element guarantees](https://docs.rs/orx-pinned-vec/latest/orx_pinned_vec/#pinned-elements-guarantees) and why they are useful in the [motivation-and-examples](https://docs.rs/orx-pinned-vec/latest/orx_pinned_vec/#motivation--examples) section. In brief, a pinned vector does not allow implicit changes in memory locations of its elements; such as moving the entire vector to another memory location due to additional capacity requirement.
8+
A **SplitVec** implements [`PinnedVec`](https://crates.io/crates/orx-pinned-vec); you may read the detailed information about [pinned element guarantees](https://docs.rs/orx-pinned-vec/latest/orx_pinned_vec/#pinned-elements-guarantees) and why they are useful in the [motivation-and-examples](https://docs.rs/orx-pinned-vec/latest/orx_pinned_vec/#motivation--examples) section. In brief, a pinned vector does not allow implicit changes in memory locations of its elements; such as moving the entire vector to another memory location due to additional capacity requirement.
99

1010
## Growth and Capacity Decisions
1111

@@ -89,7 +89,7 @@ let std_vec: Vec<_> = vec.into();
8989
assert_eq!(&std_vec, &[0, 1, 2, 3]);
9090
```
9191

92-
Naturally, it has certain specific differences and operations. For instance, we cannot have `as_slice` method for the split vector since it is not a single big chunk of memory. Instead, we have the `slices` and `try_get_slice` methods.
92+
Naturally, it has certain specific differences and operations. For instance, we cannot have `as_slice` method for the split vector since it is not a single big chunk of memory. Instead, we have the `slices`, `slices_mut` and `try_get_slice` methods.
9393

9494
```rust
9595
use orx_split_vec::*;
@@ -140,7 +140,12 @@ assert_eq!(slice, SplitVecSlice::Fragmented(0, 1));
140140
let slice = vec.try_get_slice(3..7);
141141
assert_eq!(slice, SplitVecSlice::OutOfBounds);
142142

143-
// or the slice can be obtained as a vector of slices
143+
// instead of a single slice; we can get an iterator of slices
144+
let slices = vec.slices(..);
145+
assert_eq!(2, slices.len());
146+
assert_eq!(slices[0], &[0, 1, 2, 3]);
147+
assert_eq!(slices[1], &[4]);
148+
144149
let slices = vec.slices(0..3);
145150
assert_eq!(1, slices.len());
146151
assert_eq!(slices[0], &[0, 1, 2]);
@@ -149,11 +154,6 @@ let slices = vec.slices(3..5);
149154
assert_eq!(2, slices.len());
150155
assert_eq!(slices[0], &[3]);
151156
assert_eq!(slices[1], &[4]);
152-
153-
let slices = vec.slices(0..vec.len());
154-
assert_eq!(2, slices.len());
155-
assert_eq!(slices[0], &[0, 1, 2, 3]);
156-
assert_eq!(slices[1], &[4]);
157157
```
158158

159159
Finally, its main difference and objective is to provide pinned element guarantees as demonstrated in the example below.
@@ -187,8 +187,8 @@ assert_eq!(3, vec.fragments().len());
187187
assert_eq!(addr42, &vec[0] as *const usize);
188188

189189
// we can safely dereference it and read the correct value
190-
// dereferencing is still unsafe for SplitVec,
191-
// however, it allows for safe api's for wrapper types such as
190+
// of course, dereferencing is still through the unsafe api,
191+
// however, the guarantee allows for safe api's for wrapper types such as
192192
// ConcurrentVec, ImpVec, SelfRefCol
193193
assert_eq!(unsafe { *addr42 }, 42);
194194
```
@@ -197,47 +197,47 @@ assert_eq!(unsafe { *addr42 }, 42);
197197

198198
## Benchmarks
199199

200-
Recall that the motivation of using a split vector is to provide pinned element guarantees. However, it is also important to keep the performance within an acceptable range compared to the standard vector. Growth strategies implemented in this crate seem to achieve this goal.
200+
Recall that the motivation of using a split vector is to provide pinned element guarantees. However, it is also important to keep the performance within an acceptable range compared to the standard vector. Growth strategies implemented in this crate achieve this goal.
201201

202202
### Benchmark: Growth
203203

204204
*You may see the benchmark at [benches/grow.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/grow.rs).*
205205

206-
The benchmark compares the build up time of vectors by pushing elements one by one. The baseline is the vector created by `std::vec::Vec::with_capacity` which has the perfect information on the number of elements to be pushed. Compared variants are vectors created with no prior knowledge about capacity: `Vec::new`, `SplitVec<_, Linear>` and `SplitVec<_, Doubling>`.
206+
The benchmark compares the build up time of vectors by pushing elements one by one. The baseline is the standard vector created by **Vec::with_capacity** which has the perfect information on the number of elements to be pushed. Compared variants are vectors created with no prior knowledge about capacity: **Vec::new**, `SplitVec<_, Linear>` and `SplitVec<_, Doubling>`.
207207

208208
<img src="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_grow.PNG" alt="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_grow.PNG" />
209209

210-
The baseline **Vec::with_capacity** performs between 1.5 and 2.0 times faster than **Vec::new** which has no capacity information and requires copies while growing. **SplitVec** growth is copy-free. Overall, its growth performance is much closer to standard vector with perfect capacity information than **Vec::new**.
210+
The baseline **Vec::with_capacity** performs between 1.5 and 2.0 times faster than **Vec::new**. **SplitVec** variants also do not use prior knowledge about the number of elements to be pushed; however, it has the advantage of copy-free growth. Overall, its growth performance is much closer to standard vector with perfect capacity information than that of the **Vec::new**.
211211

212212
*`Recursive` strategy is omitted here since it behaves exactly as the `Doubling` strategy in the growth scenario.*
213213

214214
### Benchmark: Random Access
215215

216216
*You may see the benchmark at [benches/random_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/random_access.rs).*
217217

218-
In this benchmark, we access vector elements by indices in a random order. The baseline is again the standard vector which is compared to `Linear` and `Doubling` growth strategies that allow for constant time random access. `Recursive` strategy is also included in the benchmark; however, recall that it does not provide constant time random access.
218+
In this benchmark, we access vector elements by indices in a random order. The baseline standard vector is compared to `Linear` and `Doubling` growth strategies that allow for constant time random access. `Recursive` strategy without constant time random access is also included in the experimentation.
219219

220220
<img src="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_random_access.PNG" alt="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_random_access.PNG" />
221221

222-
We can see that `Linear` is slower than `Doubling`. Random access performance of `Doubling` is at most 40% slower than tht of the standard vector, and the difference diminishes as the element size or number of elements gets larger.
222+
We can see that `Linear` is slower than `Doubling`. Random access performance of `Doubling` is at most 40% slower than that of the standard vector, and the difference diminishes as the element size or number of elements gets larger.
223223

224-
`Recursive`, on the other hand, is between 5 and 7 times slower than the slower access for small elements and around 1.5 times slower for large structs.
224+
`Recursive`, on the other hand, is between 5 and 7 times slower for small elements and around 1.5 times slower for larger structs.
225225

226226
### Benchmark: Serial Access
227227

228228
*You may see the benchmark at [benches/serial_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/serial_access.rs).*
229229

230-
Here, we benchmark the case where we access each element of the vector in order starting from the first element to the last. For completeness, baseline **Vec** is compared with `Doubling`, `Linear` and `Recursive` growth strategies; however, `SplitVec` actually uses the same iterator to allow for the serial access for any growth strategy. The difference, if any, stems from the sizes of fragments and their impact on cache locality.
230+
Here, we benchmark the case where we access each element of the vector in order starting from the first element to the last. Baseline **Vec** is compared with `Doubling`, `Linear` and `Recursive` growth strategies; however, `SplitVec` actually uses the same iterator to allow for the serial access for any growth strategy. The difference, if any, stems from the sizes of fragments and their impact on cache locality.
231231

232232
<img src="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_serial_access.PNG" alt="https://raw.githubusercontent.com/orxfun/orx-split-vec/main/docs/img/bench_serial_access.PNG" />
233233

234-
Furthermore, we observe that split vector performance is almost identical to that of the standard vector. Although there are minor deviations, we do not observe any significant difference among tested growth strategies.
234+
We observe that split vector performance is almost identical to that of the standard vector. Although there are minor deviations, we do not observe any significant difference among tested growth strategies.
235235

236236
### Benchmark: Append
237237

238238
*You may see the benchmark at [benches/serial_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/append.rs).*
239239

240-
Appending vectors to vectors might be a critical operation in certain cases. One example is the recursive data structures such as trees or linked lists. We might append a tree to another tree to get a new merged tree. This operation could be handled by copying data to keep a certain required structure or by simply accepting the incoming chunk (no-ops).
240+
Appending vector to another vector is a critical operation for certain use cases. One example is recursive data structures such as trees or linked lists. Consider appending a tree to the leaf of another tree to get a new merged tree. This operation could be handled by copying data around to maintain a certain structure or by simply accepting the incoming chunk in constant time.
241241

242242
* **Vec**, `SplitVec<_, Doubling>` and `SplitVec<_, Linear>` perform memory copies in order to keep their internal structure which allows for efficient random access.
243243
* `SplitVec<_, Recursive>`, on the other hand, utilizes its fragmented structure and accepts the incoming chunk as it is. Hence, appending another vector to it is simply no-ops. This does not degrade serial access performance. However, it leads to slower random access as we observe in the previous benchmark.

0 commit comments

Comments
 (0)