mirror of
https://github.com/bspeice/speice.io
synced 2024-12-22 16:48:10 -05:00
More sketching
This commit is contained in:
parent
f7a5fea93d
commit
f32b107d73
@ -72,11 +72,7 @@ we'll follow this guide:
|
|||||||
- Smart pointers hold their contents in the heap
|
- Smart pointers hold their contents in the heap
|
||||||
- Collections are smart pointers for many objects at a time, and reallocate
|
- Collections are smart pointers for many objects at a time, and reallocate
|
||||||
when they need to grow
|
when they need to grow
|
||||||
- Boxed closures (FnBox, others?) are heap allocated
|
- `lazy_static!` and `thread_local!` force heap allocation
|
||||||
- "Move" semantics don't trigger new allocation; just a change of ownership,
|
|
||||||
so are incredibly fast
|
|
||||||
- In examples, is address of data before and after the same?
|
|
||||||
- Can `Copy` trigger allocation?
|
|
||||||
- Stack-based alternatives to standard library types should be preferred (spin, parking_lot)
|
- Stack-based alternatives to standard library types should be preferred (spin, parking_lot)
|
||||||
|
|
||||||
# Smart pointers
|
# Smart pointers
|
||||||
@ -96,8 +92,8 @@ crate should look mostly familiar:
|
|||||||
- [`Arc`](https://doc.rust-lang.org/alloc/sync/struct.Arc.html)
|
- [`Arc`](https://doc.rust-lang.org/alloc/sync/struct.Arc.html)
|
||||||
- [`Cow`](https://doc.rust-lang.org/alloc/borrow/enum.Cow.html)
|
- [`Cow`](https://doc.rust-lang.org/alloc/borrow/enum.Cow.html)
|
||||||
|
|
||||||
The [standard library](https://doc.rust-lang.org/std/) also defines some smart pointers,
|
The [standard library](https://doc.rust-lang.org/std/) also defines some smart pointers
|
||||||
though more than can be covered in this article. Some examples:
|
to manage heap objects, though more than can be covered here. Some examples:
|
||||||
- [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html)
|
- [`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html)
|
||||||
- [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)
|
- [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)
|
||||||
|
|
||||||
@ -168,4 +164,29 @@ But because the vector has no elements it is managing, no calls to the allocator
|
|||||||
will ever be dispatched. A couple of places to look at for confirming this behavior:
|
will ever be dispatched. A couple of places to look at for confirming this behavior:
|
||||||
[`Vec::new()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new),
|
[`Vec::new()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new),
|
||||||
[`HashMap::new()`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.new),
|
[`HashMap::new()`](https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.new),
|
||||||
and [`String::new()`](https://doc.rust-lang.org/std/string/struct.String.html#method.new).
|
and [`String::new()`](https://doc.rust-lang.org/std/string/struct.String.html#method.new).
|
||||||
|
|
||||||
|
# **thread_local!** and **lazy_static!**
|
||||||
|
|
||||||
|
# Heap Alternatives
|
||||||
|
|
||||||
|
While it is a bit strange for us to talk of the stack after spending so much time with the heap,
|
||||||
|
it's worth pointing out that some heap-allocated objects in Rust have stack-based counterparts
|
||||||
|
provided by other crates. There are a number of cases where this may be helpful, so it's useful
|
||||||
|
to know that alternatives exist if you need them.
|
||||||
|
|
||||||
|
When it comes to some of the standard library smart pointers
|
||||||
|
([`RwLock`](https://doc.rust-lang.org/std/sync/struct.RwLock.html) and
|
||||||
|
[`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)), stack-based alternatives
|
||||||
|
are provided in crates like [spin](https://crates.io/crates/spin) and
|
||||||
|
[parking_lot](https://crates.io/crates/parking_lot). You can check out
|
||||||
|
[`lock_api::RwLock`](https://docs.rs/lock_api/0.1.5/lock_api/struct.RwLock.html),
|
||||||
|
[`lock_api::Mutex`](https://docs.rs/lock_api/0.1.5/lock_api/struct.Mutex.html), and
|
||||||
|
[`spin::Once`](https://mvdnes.github.io/rust-docs/spin-rs/spin/struct.Once.html)
|
||||||
|
if you're in need of synchronization primitives.
|
||||||
|
|
||||||
|
[thread_id](https://crates.io/crates/thread-id)
|
||||||
|
may still be necessary if you're implementing an allocator (*cough cough* the author *cough cough*)
|
||||||
|
because [`thread::current().id()`](https://doc.rust-lang.org/std/thread/struct.ThreadId.html)
|
||||||
|
[uses a `thread_local!` structure](https://doc.rust-lang.org/stable/src/std/sys_common/thread_info.rs.html#22-40)
|
||||||
|
that needs heap allocation.
|
||||||
|
@ -34,6 +34,44 @@ There will, however, be an opera of optimization.
|
|||||||
|
|
||||||
# The Case of the Disappearing Box
|
# The Case of the Disappearing Box
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Currently doesn't work, not sure why.
|
||||||
|
use std::alloc::{GlobalAlloc, Layout, System};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
fn allocate_box() {
|
||||||
|
let x = Box::new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
// Turn on panicking if we allocate on the heap
|
||||||
|
DO_PANIC.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
allocate_box();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static A: PanicAllocator = PanicAllocator;
|
||||||
|
static DO_PANIC: AtomicBool = AtomicBool::new(false);
|
||||||
|
struct PanicAllocator;
|
||||||
|
|
||||||
|
unsafe impl GlobalAlloc for PanicAllocator {
|
||||||
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
|
if DO_PANIC.load(Ordering::SeqCst) {
|
||||||
|
panic!("Unexpected allocation.");
|
||||||
|
}
|
||||||
|
System.alloc(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||||
|
if DO_PANIC.load(Ordering::SeqCst) {
|
||||||
|
panic!("Unexpected deallocation.");
|
||||||
|
}
|
||||||
|
System.dealloc(ptr, layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Vectors of Usual Size
|
# Vectors of Usual Size
|
||||||
|
|
||||||
# Dr. Array or: How I Learned to Love the Optimizer
|
# Dr. Array or: How I Learned to Love the Optimizer
|
||||||
|
@ -98,6 +98,8 @@ With all that in mind, let's talk about situations in which we're guaranteed to
|
|||||||
- [Arrays](https://doc.rust-lang.org/std/primitive.array.html) are always stack-allocated.
|
- [Arrays](https://doc.rust-lang.org/std/primitive.array.html) are always stack-allocated.
|
||||||
- Closures capture their arguments on the stack
|
- Closures capture their arguments on the stack
|
||||||
- Generics will use stack allocation, even with dynamic dispatch.
|
- Generics will use stack allocation, even with dynamic dispatch.
|
||||||
|
- [`Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html) types are guaranteed to be
|
||||||
|
stack-allocated, and copying them will be done in stack memory.
|
||||||
|
|
||||||
# Structs
|
# Structs
|
||||||
|
|
||||||
@ -445,4 +447,28 @@ pub fn do_call() {
|
|||||||
-- [Compiler Explorer](https://godbolt.org/z/u_yguS)
|
-- [Compiler Explorer](https://godbolt.org/z/u_yguS)
|
||||||
|
|
||||||
It's hard to imagine practical situations where dynamic dispatch would be
|
It's hard to imagine practical situations where dynamic dispatch would be
|
||||||
used for objects that aren't heap allocated, but it technically can be done.
|
used for objects that aren't heap allocated, but it technically can be done.
|
||||||
|
|
||||||
|
# Copy types
|
||||||
|
|
||||||
|
Understanding move semantics and copy semantics in Rust is hard. The Rust docs
|
||||||
|
[go into detail](https://doc.rust-lang.org/stable/core/marker/trait.Copy.html)
|
||||||
|
far better than can be addressed here, so I'll leave them to do the job.
|
||||||
|
Their guideline is reasonable though:
|
||||||
|
[if your type can implemement `Copy`, it should](https://doc.rust-lang.org/stable/core/marker/trait.Copy.html#when-should-my-type-be-copy).
|
||||||
|
While there are potential speed tradeoffs to benchmark when discussing `Copy`
|
||||||
|
(move semantics for stack objects vs. copying stack pointers vs. copying stack `struct`s),
|
||||||
|
*it's impossible for `Copy` to introduce a heap allocation*.
|
||||||
|
|
||||||
|
But why is this the case? Fundamentally, it's because the language controls
|
||||||
|
what `Copy` means -
|
||||||
|
["the behavior of `Copy` is not overloadable"](https://doc.rust-lang.org/std/marker/trait.Copy.html#whats-the-difference-between-copy-and-clone)
|
||||||
|
because it's a marker trait. From there we'll note that a type
|
||||||
|
[can implement `Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html#when-can-my-type-be-copy)
|
||||||
|
if (and only if) its components implement `Copy`, and that
|
||||||
|
[no heap-allocated types implement `Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html#implementors).
|
||||||
|
Thus, assignments involving heap types are always move semantics, and new heap
|
||||||
|
allocations won't occur without explicit calls to
|
||||||
|
[`clone()`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone).
|
||||||
|
|
||||||
|
TODO: Some examples. Maybe just need to show compiler errors?
|
||||||
|
Loading…
Reference in New Issue
Block a user