Skip to main content

zk_nalloc/
polynomial.rs

1//! Polynomial Arena for nalloc.
2//!
3//! The `PolynomialArena` is optimized for FFT/NTT operations:
4//!
5//! - **64-byte alignment**: Ensures data fits cache lines for SIMD operations.
6//! - **4KB page alignment**: Optionally available for huge vector allocations.
7//! - **Massive capacity**: Pre-reserved for 1GB+ polynomial vectors.
8
9use crate::bump::BumpAlloc;
10use crate::config::{CACHE_LINE_ALIGN, PAGE_ALIGN};
11use std::sync::Arc;
12
13/// Specialized handle for Polynomial and FFT data.
14///
15/// Optimized for cache-line alignment and massive vectors.
16pub struct PolynomialArena {
17    inner: Arc<BumpAlloc>,
18}
19
20impl PolynomialArena {
21    /// Create a new `PolynomialArena` wrapping a `BumpAlloc`.
22    #[inline]
23    pub fn new(inner: Arc<BumpAlloc>) -> Self {
24        Self { inner }
25    }
26
27    /// Allocate polynomial data with 64-byte alignment for optimal FFT/NTT performance.
28    ///
29    /// This alignment is critical for SIMD-accelerated operations:
30    /// - **AVX-512**: Requires 64-byte alignment
31    /// - **AVX/AVX2**: Benefits from 32-byte alignment (64 is a superset)
32    /// - **Cache efficiency**: Modern cache lines are 64 bytes
33    #[inline]
34    pub fn alloc_fft_friendly(&self, size: usize) -> *mut u8 {
35        debug_assert!(size > 0);
36        self.inner.alloc(size, CACHE_LINE_ALIGN)
37    }
38
39    /// Allocate huge vectors with page alignment (4096 bytes).
40    ///
41    /// Use this for vectors exceeding a few megabytes. Benefits:
42    /// - **TLB efficiency**: Reduces translation lookaside buffer misses
43    /// - **Huge pages**: May enable transparent huge page usage on Linux
44    /// - **DMA compatibility**: Required for some hardware accelerators
45    #[inline]
46    pub fn alloc_huge(&self, size: usize) -> *mut u8 {
47        debug_assert!(size > 0);
48        self.inner.alloc(size, PAGE_ALIGN)
49    }
50
51    /// Allocate with custom alignment.
52    ///
53    /// Use this when you have specific alignment requirements.
54    /// Alignment must be a power of two.
55    #[inline]
56    pub fn alloc(&self, size: usize, align: usize) -> *mut u8 {
57        debug_assert!(size > 0);
58        debug_assert!(align > 0);
59        debug_assert!(align.is_power_of_two());
60        self.inner.alloc(size, align)
61    }
62
63    /// Allocate a typed slice of elements with appropriate alignment.
64    ///
65    /// This is a convenience method for allocating arrays of field elements
66    /// or other ZK primitive types.
67    ///
68    /// # Safety
69    /// The returned pointer must be properly aligned for type T.
70    /// The caller is responsible for initializing the memory.
71    #[inline]
72    pub unsafe fn alloc_slice<T>(&self, count: usize) -> *mut T {
73        debug_assert!(count > 0);
74        let size = count * std::mem::size_of::<T>();
75        let align = std::mem::align_of::<T>().max(CACHE_LINE_ALIGN);
76        self.inner.alloc(size, align) as *mut T
77    }
78
79    /// Reset the polynomial arena.
80    ///
81    /// # Safety
82    /// All previously allocated polynomial memory becomes invalid.
83    #[inline]
84    pub unsafe fn reset(&self) {
85        self.inner.reset();
86    }
87
88    /// Get the remaining capacity in bytes.
89    #[inline]
90    pub fn remaining(&self) -> usize {
91        self.inner.remaining()
92    }
93
94    /// Get the number of bytes currently allocated.
95    #[inline]
96    pub fn used(&self) -> usize {
97        self.inner.used()
98    }
99
100    /// Get the total capacity in bytes.
101    #[inline]
102    pub fn capacity(&self) -> usize {
103        self.inner.capacity()
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::arena::ArenaManager;
111
112    #[test]
113    fn test_fft_alignment() {
114        let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
115        let poly = PolynomialArena::new(manager.polynomial());
116
117        for _ in 0..100 {
118            let ptr = poly.alloc_fft_friendly(1024);
119            assert!(!ptr.is_null());
120            assert_eq!(
121                (ptr as usize) % CACHE_LINE_ALIGN,
122                0,
123                "FFT allocation not 64-byte aligned"
124            );
125        }
126    }
127
128    #[test]
129    fn test_huge_alignment() {
130        let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
131        let poly = PolynomialArena::new(manager.polynomial());
132
133        for _ in 0..10 {
134            let ptr = poly.alloc_huge(64 * 1024);
135            assert!(!ptr.is_null());
136            assert_eq!(
137                (ptr as usize) % PAGE_ALIGN,
138                0,
139                "Huge allocation not page-aligned"
140            );
141        }
142    }
143
144    #[test]
145    fn test_typed_slice_allocation() {
146        let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
147        let poly = PolynomialArena::new(manager.polynomial());
148
149        // Allocate u64 field elements
150        let ptr: *mut u64 = unsafe { poly.alloc_slice(1024) };
151        assert!(!ptr.is_null());
152        assert_eq!(
153            (ptr as usize) % std::mem::align_of::<u64>(),
154            0,
155            "Slice not aligned for u64"
156        );
157
158        // Write and read
159        unsafe {
160            for i in 0..1024 {
161                *ptr.add(i) = i as u64;
162            }
163            for i in 0..1024 {
164                assert_eq!(*ptr.add(i), i as u64);
165            }
166        }
167    }
168
169    #[test]
170    fn test_custom_alignment() {
171        let manager = ArenaManager::with_sizes(1024 * 1024, 2 * 1024 * 1024, 1024 * 1024).unwrap();
172        let poly = PolynomialArena::new(manager.polynomial());
173
174        // Test various power-of-two alignments
175        for align_pow in 0..12 {
176            let align = 1usize << align_pow;
177            let ptr = poly.alloc(64, align);
178            assert!(!ptr.is_null());
179            assert_eq!(
180                (ptr as usize) % align,
181                0,
182                "Custom alignment {} failed",
183                align
184            );
185        }
186    }
187}