mirror of
				https://github.com/bspeice/speice.io
				synced 2025-11-03 18:10:32 -05:00 
			
		
		
		
	More sketching
This commit is contained in:
		@ -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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -169,3 +165,28 @@ will ever be dispatched. A couple of places to look at for confirming this behav
 | 
				
			|||||||
[`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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -446,3 +448,27 @@ pub fn do_call() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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?
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user