소스 정보
- 저장소
- tomevault-io/skills-registry
- 최근 소스 활동
- 2026년 7월 3일 19:45
- 감지된 SKILL.md 언어
- 영어
- 스타
- 0
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill rust-memory-optimization명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | rust-memory-optimization |
| description | | Use when this capability is needed. |
Advanced strategies for reducing allocations, shrinking layout sizes, and boosting performance.
For collections that are usually small, avoid standard vectors to bypass heap allocations.
SmallVec: Stack-allocated up to a threshold, overflows to the heap.ArrayVec: Fixed-capacity array wrapper; never allocates on the heap.ThinVec: Only takes a single pointer size when empty, reducing size in enum layouts.use smallvec::{smallvec, SmallVec};
use arrayvec::ArrayVec;
// SmallVec: Allocates 4 items on the stack. Goes to heap only if count > 4.
let mut local_tags: SmallVec<[String; 4]> = smallvec![];
local_tags.push("Rust".to_string());
// ArrayVec: Absolute limit of 8 items on the stack. No heap overflow.
let mut coordinates: ArrayVec<f64, 8> = ArrayVec::new();
coordinates.push(42.0);
Standard String consumes 24 bytes and allocates heap space immediately. Use CompactString for inline small strings (under 24 bytes) without allocating.
use compact_str::CompactString;
// No heap allocation for strings <= 24 characters
let username = CompactString::new("user_123");
If a collection's size is determined once at initialization and never changes, convert it from Vec<T> to Box<[T]> to save 8 bytes of stack capacity space.
let items_vec: Vec<i32> = vec![1, 2, 3];
// Save capacity field (shrinks stack representation from 24 to 16 bytes)
let items_slice: Box<[i32]> = items_vec.into_boxed_slice();
Instead of cloning or allocating strings, hold references to existing memory using slices or crates like bytes::Bytes.
use bytes::Bytes;
pub struct Message {
// Zero-copy reference-counted byte buffer slice
pub payload: Bytes,
}
// Can be sliced and shared among threads without cloning underlying memory
let original = Bytes::from(vec![0; 1024]);
let slice = original.slice(0..100);
For batch allocation where structures share a lifecycle (e.g. AST parsing), use arena allocators (bumpalo) to allocate items sequentially in a continuous region of memory and free them all at once.
use bumpalo::Bump;
let bump = Bump::new();
// Allocates quickly on the bump arena
let string_ref = bump.alloc_str("dynamic string");
let int_ref = bump.alloc(42);
// Entire arena is cleared at once on drop
Reuse collection allocations in performance-critical loops with .clear() instead of re-instantiating.
// Bad: Allocates a new vector every iteration
for item in feed {
let mut batch = Vec::new();
batch.push(item);
}
// Good: Reuses allocation buffer
let mut batch = Vec::with_capacity(100);
for item in feed {
batch.clear();
batch.push(item);
}
Use std::mem::size_of to understand data layout.
println!("User size: {}", std::mem::size_of::<User>());
Reorder fields to reduce padding only when the type is numerous or hot.
// Often smaller: group large-alignment fields first
struct Record {
id: u64,
flags: u32,
kind: u8,
}
fn label<'a>(name: &'a str, fallback: &'a str) -> std::borrow::Cow<'a, str> {
if name.is_empty() { fallback.into() } else { name.into() }
}
Avoid format! in tight loops. Reuse buffers with clear().
Return slices into the input when the input outlives parsed data.
pub struct Header<'a> {
pub name: &'a str,
pub value: &'a str,
}
Do not force zero-copy when it creates unmanageable lifetimes for little gain.
When enums contain Vec variants, ThinVec reduces the enum size by storing only a pointer when empty:
use thin_vec::ThinVec;
// Regular Vec in an enum makes the enum 24+ bytes even when empty
enum Node {
Leaf(i64),
Children(Vec<Node>), // 24 bytes for Vec
}
// ThinVec only stores 8 bytes (a pointer) when empty
enum NodeOptimized {
Leaf(i64),
Children(ThinVec<Node>), // 8 bytes for ThinVec
}
use std::borrow::Cow;
pub struct Error {
message: Cow<'static, str>,
}
impl Error {
// No allocation for static messages
pub fn new(msg: &'static str) -> Self {
Self { message: Cow::Borrowed(msg) }
}
// Allocates only when dynamic
pub fn with_detail(msg: String) -> Self {
Self { message: Cow::Owned(msg) }
}
}
use std::rc::Rc;
use std::sync::Arc;
// Single-threaded shared ownership — cheaper than Arc
let shared = Rc::new(large_data);
// Multi-threaded shared ownership — required across threads
let shared = Arc::new(large_data);
Prefer Rc when data never crosses thread boundaries. Arc adds atomic reference counting overhead.
use hashbrown::HashMap;
// hashbrown's HashMap uses less memory than std's and is faster
let mut map: HashMap<String, i32> = HashMap::with_capacity(128);
// Reserve capacity upfront to avoid rehashing
map.reserve(expected_count);
// Consider FxHashMap for non-adversarial keys
use rustc_hash::FxHashMap;
let mut fast_map: FxHashMap<String, i32> = FxHashMap::default();
| Type | Stack Size | Heap Alloc | Use When |
|---|---|---|---|
String | 24 bytes | Yes | Owns mutable text |
&str | 16 bytes | No | Borrowed text, lifetime bound |
CompactString | 24 bytes | No (≤24 chars) | Short strings that own data |
Cow<str> | 32 bytes | Conditionally | Mostly static, rarely dynamic |
Box<str> | 16 bytes | Yes | Owned fixed string |
Vec<T> | 24 bytes | Yes | Growable collection |
SmallVec<[T; N]> | ~24+ bytes | Only if >N | Usually small, occasionally large |
Box<[T]> | 16 bytes | Yes | Fixed-size after construction |
ThinVec<T> | 8 bytes | Only if non-empty | Empty or small in enums |
use std::alloc::{GlobalAlloc, System, Layout};
struct TrackingAllocator;
unsafe impl GlobalAlloc for TrackingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}
// For detailed profiling, use dhat, heaptrack, or valgrind
Use dhat crate for heap profiling in tests:
#[cfg(test)]
mod tests {
#[test]
fn memory_test() {
let _profiler = dhat::Profiler::new_heap();
// code under test
// dhat prints allocation stats on drop
}
}
Vec<T>: general purpose growable collection.SmallVec<[T; N]>: usually-small collection with occasional heap spill.ArrayVec<T, N>: hard maximum size, no heap allocation.Box<[T]>: fixed-size heap slice after construction.Bytes: shared immutable byte buffers.Bump: many same-lifetime allocations.Cow<'a, T>: borrow unless mutation/ownership is required.// Bad: cloning a large Vec just to avoid lifetime complexity
let data = original.clone(); // expensive and unnecessary
// Good: use Arc or Cow for shared ownership
let data: Arc<[u8]> = original.into();
// Bad: allocating inside a hot loop
for item in items {
let s = format!("processing {}", item); // allocates every iteration
}
// Good: reuse a buffer
let mut buf = String::with_capacity(128);
for item in items {
buf.clear();
write!(buf, "processing {}", item).unwrap();
}
Vec with SmallVec without measuring.Arc as a default clone workaround.Box<dyn Trait> when generics would eliminate the allocation entirely.When reviewing memory optimization, ask what was measured, which allocation is removed, what lifetime assumptions changed, and whether the new type increases stack size or API complexity.
Cow before cloning strings or buffers.dhat or heaptrack to find actual allocation hotspots.hashbrown or rustc-hash for HashMap-heavy code.Source: adxptived/Rust-Skills — distributed by TomeVault.