You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+19-19
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@
5
5
6
6
An efficient dynamic capacity vector with pinned element guarantees.
7
7
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.
9
9
10
10
## Growth and Capacity Decisions
11
11
@@ -89,7 +89,7 @@ let std_vec: Vec<_> = vec.into();
89
89
assert_eq!(&std_vec, &[0, 1, 2, 3]);
90
90
```
91
91
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.
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.
201
201
202
202
### Benchmark: Growth
203
203
204
204
*You may see the benchmark at [benches/grow.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/grow.rs).*
205
205
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>`.
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**.
211
211
212
212
*`Recursive` strategy is omitted here since it behaves exactly as the `Doubling` strategy in the growth scenario.*
213
213
214
214
### Benchmark: Random Access
215
215
216
216
*You may see the benchmark at [benches/random_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/random_access.rs).*
217
217
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.
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.
223
223
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.
225
225
226
226
### Benchmark: Serial Access
227
227
228
228
*You may see the benchmark at [benches/serial_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/serial_access.rs).*
229
229
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.
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.
235
235
236
236
### Benchmark: Append
237
237
238
238
*You may see the benchmark at [benches/serial_access.rs](https://github.com/orxfun/orx-split-vec/blob/main/benches/append.rs).*
239
239
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.
241
241
242
242
***Vec**, `SplitVec<_, Doubling>` and `SplitVec<_, Linear>` perform memory copies in order to keep their internal structure which allows for efficient random access.
243
243
*`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