@@ -64,12 +64,13 @@ use zerogc::{GcSafe, Trace, GcVisitor};
6464use zerogc_context:: utils:: { ThreadId , MemorySize } ;
6565
6666use crate :: alloc:: { SmallArenaList , SmallArena } ;
67- use crate :: layout:: { StaticGcType , GcType , SimpleVecRepr , DynamicObj , StaticVecType , SimpleMarkData , SimpleMarkDataSnapshot , GcHeader , BigGcObject , HeaderLayout , GcArrayHeader , GcVecHeader } ;
67+ use crate :: layout:: { StaticGcType , GcType , SimpleVecRepr , DynamicObj , StaticVecType , SimpleMarkData , SimpleMarkDataSnapshot , GcHeader , BigGcObject , HeaderLayout , GcArrayHeader , GcVecHeader , GcTypeLayout } ;
6868
6969use zerogc_context:: collector:: { RawSimpleAlloc , RawCollectorImpl } ;
7070use zerogc_context:: handle:: { GcHandleList , RawHandleImpl } ;
7171use zerogc_context:: { CollectionManager as AbstractCollectionManager , RawContext as AbstractRawContext , CollectorContext } ;
7272use zerogc:: vec:: { GcRawVec } ;
73+ use std:: cell:: Cell ;
7374
7475#[ cfg( feature = "small-object-arenas" ) ]
7576mod alloc;
@@ -116,6 +117,9 @@ impl Default for GcConfig {
116117 }
117118}
118119
120+ /// The alignment of the singleton empty vector
121+ const EMPTY_VEC_ALIGNMENT : usize = std:: mem:: align_of :: < usize > ( ) ;
122+
119123#[ cfg( feature = "sync" ) ]
120124type RawContext < C > = zerogc_context:: state:: sync:: RawContext < C > ;
121125#[ cfg( feature = "sync" ) ]
@@ -166,20 +170,40 @@ unsafe impl RawSimpleAlloc for RawSimpleCollector {
166170 ( context. collector ( ) . id ( ) , ptr. cast ( ) )
167171 }
168172
173+ #[ inline]
174+ fn alloc_vec < ' gc , T > ( context : & ' gc CollectorContext < Self > ) -> GcVec < ' gc , T > where T : GcSafe + ' gc {
175+ if std:: mem:: align_of :: < T > ( ) > EMPTY_VEC_ALIGNMENT {
176+ // We have to do an actual allocation because we want higher alignment :(
177+ return Self :: alloc_vec_with_capacity ( context, 0 )
178+ }
179+ let header = context. collector ( ) . heap . empty_vec ( ) ;
180+ // NOTE: Assuming header is already initialized
181+ unsafe {
182+ debug_assert_eq ! ( ( * header) . len. get( ) , 0 ) ;
183+ debug_assert_eq ! ( ( * header) . capacity, 0 ) ;
184+ }
185+ let id = context. collector ( ) . id ( ) ;
186+ let ptr = unsafe { NonNull :: new_unchecked ( ( header as * mut u8 )
187+ . add ( GcVecHeader :: LAYOUT . value_offset ( EMPTY_VEC_ALIGNMENT ) ) ) } ;
188+ GcVec {
189+ raw : unsafe { GcRawVec :: from_repr ( Gc :: from_raw ( id, ptr. cast ( ) ) ) } ,
190+ context
191+ }
192+ }
193+
169194 fn alloc_vec_with_capacity < ' gc , T > ( context : & ' gc CollectorContext < Self > , capacity : usize ) -> GcVec < ' gc , T > where T : GcSafe + ' gc {
170- let ptr = if capacity == 0 {
171- NonNull :: dangling ( )
172- } else {
173- let ( header, value_ptr) = context. collector ( ) . heap . allocator . alloc_layout (
174- GcVecHeader :: LAYOUT ,
175- SimpleVecRepr :: < T > :: layout ( capacity) ,
176- <T as StaticVecType >:: STATIC_VEC_TYPE
177- ) ;
178- unsafe {
179- ( * header) . capacity = capacity;
180- ( * header) . len . set ( 0 ) ;
181- NonNull :: new_unchecked ( value_ptr as * mut SimpleVecRepr < T > )
182- }
195+ if capacity == 0 && std:: mem:: align_of :: < T > ( ) <= EMPTY_VEC_ALIGNMENT {
196+ return Self :: alloc_vec ( context)
197+ }
198+ let ( header, value_ptr) = context. collector ( ) . heap . allocator . alloc_layout (
199+ GcVecHeader :: LAYOUT ,
200+ SimpleVecRepr :: < T > :: layout ( capacity) ,
201+ <T as StaticVecType >:: STATIC_VEC_TYPE
202+ ) ;
203+ let ptr = unsafe {
204+ ( * header) . capacity = capacity;
205+ ( * header) . len . set ( 0 ) ;
206+ NonNull :: new_unchecked ( value_ptr as * mut SimpleVecRepr < T > )
183207 } ;
184208 let id = context. collector ( ) . id ( ) ;
185209 GcVec {
@@ -241,7 +265,8 @@ unsafe impl DynTrace for GcHandleListWrapper {
241265struct GcHeap {
242266 config : Arc < GcConfig > ,
243267 threshold : AtomicUsize ,
244- allocator : SimpleAlloc
268+ allocator : SimpleAlloc ,
269+ cached_empty_vec : Cell < Option < * mut GcVecHeader > >
245270}
246271impl GcHeap {
247272 fn new ( config : Arc < GcConfig > ) -> GcHeap {
@@ -252,7 +277,43 @@ impl GcHeap {
252277 config. initial_threshold
253278 } ) ,
254279 allocator : SimpleAlloc :: new ( ) ,
255- config
280+ config, cached_empty_vec : Cell :: new ( None )
281+ }
282+ }
283+ #[ inline]
284+ pub fn empty_vec ( & self ) -> * mut GcVecHeader {
285+ match self . cached_empty_vec . get ( ) {
286+ Some ( cached) => cached,
287+ None => {
288+ let res = self . create_empty_vec ( ) ;
289+ self . cached_empty_vec . set ( Some ( self . create_empty_vec ( ) ) ) ;
290+ res
291+ }
292+ }
293+ }
294+ #[ cold]
295+ fn create_empty_vec < ' gc > ( & self ) -> * mut GcVecHeader {
296+ const DUMMY_LAYOUT : Layout = unsafe { Layout :: from_size_align_unchecked (
297+ 0 , EMPTY_VEC_ALIGNMENT
298+ ) } ;
299+ const DUMMY_TYPE : GcType = GcType {
300+ layout : GcTypeLayout :: Vec {
301+ element_layout : DUMMY_LAYOUT ,
302+ } ,
303+ value_offset_from_common_header : GcVecHeader :: LAYOUT
304+ . value_offset_from_common_header ( EMPTY_VEC_ALIGNMENT ) ,
305+ drop_func : None ,
306+ trace_func : None
307+ } ;
308+ let ( header, _) = self . allocator . alloc_layout (
309+ GcVecHeader :: LAYOUT ,
310+ DUMMY_LAYOUT ,
311+ & DUMMY_TYPE
312+ ) ;
313+ unsafe {
314+ ( * header) . capacity = 0 ;
315+ ( * header) . len . set ( 0 ) ;
316+ header
256317 }
257318 }
258319 #[ inline]
0 commit comments