Skip to main content

ort_openrouter_cli/common/
alloc.rs

1//! ort: Open Router CLI
2//! https://github.com/grahamking/ort
3//!
4//! MIT License
5//! Copyright (c) 2025 Graham King
6
7#![allow(static_mut_refs)]
8
9#[cfg(not(feature = "arena-alloc"))]
10use crate::libc;
11use core::alloc::Layout;
12#[cfg(not(feature = "arena-alloc"))]
13use core::ffi::c_void;
14
15#[cfg(feature = "panic-on-realloc")]
16static mut IS_FIRST_REALLOC: bool = true;
17
18#[cfg(feature = "arena-alloc")]
19use core::sync::atomic::{AtomicUsize, Ordering};
20
21#[cfg(feature = "print-allocations")]
22use crate::common::utils::to_ascii;
23
24/// All allocated memory locations will have this alignment, max_align_t
25#[cfg(feature = "arena-alloc")]
26const ALIGN: usize = 16;
27
28// How much memory to allocate total. Don't exceed this!
29#[cfg(feature = "arena-alloc")]
30const MEM_SIZE: usize = 2 * 1024 * 1024;
31
32#[cfg(feature = "arena-alloc")]
33#[repr(align(16))]
34struct Heap(pub [u8; MEM_SIZE]);
35
36#[cfg(feature = "arena-alloc")]
37static mut HEAP: Heap = Heap([0u8; MEM_SIZE]);
38#[cfg(feature = "arena-alloc")]
39static mut OFFSET: AtomicUsize = AtomicUsize::new(0);
40
41pub struct LibcAlloc;
42
43// In case you were wondering, yes all three methods get used. Rust does
44// a bnuch of alloc_zeroed and realloc.
45//
46// Build with feature "print-allocations" to see memory being allocated:
47// cargo build --features="print-allocations"
48// cargo build --release --features="print-allocations" -Zbuild-std="core,alloc"
49//
50// There's a Python script at the end of this file to summarize the output.
51//
52// Normal / prompt usage seems to peak under 64 Kib of active memory.
53//
54// `ort list` peaks around 180 Kib because it has one large (~128 KiB)
55// allocation which is a string holding the names of all models, so we can sort them.
56unsafe impl core::alloc::GlobalAlloc for LibcAlloc {
57    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
58        #[cfg(feature = "print-allocations")]
59        {
60            let mut buf = [0u8; 16];
61            buf[0] = b'+';
62            let len = to_ascii(layout.size(), &mut buf[1..]);
63            unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
64
65            // Also print alignment
66            //let mut buf = [0u8; 16];
67            //buf[0] = b' ';
68            //let len = to_ascii(layout.align(), &mut buf[1..]);
69            //unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
70        }
71
72        #[cfg(not(feature = "arena-alloc"))]
73        unsafe {
74            libc::malloc(layout.size().max(layout.align())) as *mut u8
75        }
76
77        #[cfg(feature = "arena-alloc")]
78        {
79            let next_offset = layout.size().next_multiple_of(ALIGN);
80            unsafe {
81                HEAP.0
82                    .as_mut_ptr()
83                    .add(OFFSET.fetch_add(next_offset, Ordering::Relaxed))
84            }
85        }
86    }
87
88    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
89        #[cfg(feature = "print-allocations")]
90        {
91            let mut buf = [0u8; 16];
92            buf[0] = b'+';
93            let len = to_ascii(layout.size(), &mut buf[1..]);
94            unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
95
96            // Also print alignment
97            //let mut buf = [0u8; 16];
98            //buf[0] = b' ';
99            //let len = to_ascii(layout.align(), &mut buf[1..]);
100            //unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
101        }
102
103        #[cfg(not(feature = "arena-alloc"))]
104        unsafe {
105            libc::calloc(1, layout.size().max(layout.align())) as *mut u8
106        }
107
108        #[cfg(feature = "arena-alloc")]
109        {
110            let next_offset = layout.size().next_multiple_of(ALIGN);
111            unsafe {
112                HEAP.0
113                    .as_mut_ptr()
114                    .add(OFFSET.fetch_add(next_offset, Ordering::Relaxed))
115            }
116        }
117    }
118
119    #[allow(unused_variables)]
120    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
121        #[cfg(feature = "print-allocations")]
122        {
123            let mut buf = [0u8; 16];
124            buf[0] = b'-';
125            let len = to_ascii(layout.size(), &mut buf[1..]);
126            unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
127        }
128
129        #[cfg(not(feature = "arena-alloc"))]
130        unsafe {
131            libc::free(ptr as *mut c_void)
132        }
133    }
134
135    #[cfg(not(feature = "arena-alloc"))]
136    #[allow(unused_variables)]
137    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
138        #[cfg(feature = "print-allocations")]
139        {
140            let mut buf = [0u8; 16];
141            buf[0] = b'\\';
142            let len = to_ascii(layout.size(), &mut buf[1..]);
143            unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
144
145            buf[0] = b'/';
146            let len = to_ascii(new_size, &mut buf[1..]);
147            unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
148
149            // Also print alignment
150            //let mut buf = [0u8; 16];
151            //buf[0] = b' ';
152            //let len = to_ascii(layout.align(), &mut buf[1..]);
153            //unsafe { crate::libc::write(2, buf.as_ptr().cast(), len) };
154        }
155
156        #[cfg(feature = "panic-on-realloc")]
157        unsafe {
158            // The panic machinery uses realloc, so we must immediately disable this or we would
159            // panic during the panic, and get no useful stack trace.
160            if IS_FIRST_REALLOC {
161                IS_FIRST_REALLOC = false;
162                panic!("realloc {} -> {}", layout.size(), new_size);
163            }
164        }
165
166        unsafe { libc::realloc(ptr as *mut c_void, new_size) as *mut u8 }
167    }
168}
169
170/*
171"""Print running totals from allocs.txt and report the maximum cumulative value."""
172
173from pathlib import Path
174
175
176def main() -> None:
177    path = Path("allocs.txt")
178    if not path.is_file():
179        raise SystemExit("allocs.txt not found in the current directory")
180
181    total = 0
182    max_total = None  # Highest cumulative total
183    max_plus = None   # Largest individual + value
184    max_minus = None  # Largest (most negative) individual - value
185
186    with path.open() as fh:
187        for raw_line in fh:
188            line = raw_line.strip()
189            if not line:
190                continue  # Skip blank lines silently
191
192            try:
193                delta = int(line)
194            except ValueError as exc:
195                raise SystemExit(f"Invalid line in allocs.txt: {line!r}") from exc
196            # realloc indicators
197            line = line.replace('/', '+').replace('\\', '-')
198
199            if delta > 0:
200                max_plus = delta if max_plus is None else max(max_plus, delta)
201            elif delta < 0:
202                max_minus = delta if max_minus is None else min(max_minus, delta)
203
204            total += delta
205            max_total = total if max_total is None else max(max_total, total)
206            print(f"{line} {total}")
207
208    print()  # Blank line before the summary, matching the example
209    print(f"Max: {max_total if max_total is not None else 0}")
210    print(f"Largest +: {max_plus if max_plus is not None else 0}")
211    print(f"Largest -: {max_minus if max_minus is not None else 0}")
212
213
214if __name__ == "__main__":
215    main()
216*/