Skip to content

Commit c3eb70f

Browse files
committed
update
1 parent aae5f18 commit c3eb70f

File tree

5 files changed

+67
-114
lines changed

5 files changed

+67
-114
lines changed

crates/luars/src/gc.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,30 @@ impl GC {
147147
}
148148

149149
/// Register a new object for GC tracking
150+
/// OPTIMIZED: Use lightweight tracking without HashMap insert
150151
#[inline]
151-
pub fn register_object(&mut self, obj_id: u32, obj_type: GcObjectType) -> usize {
152+
pub fn register_object(&mut self, _obj_id: u32, obj_type: GcObjectType) -> usize {
153+
let gc_id = self.next_gc_id;
154+
self.next_gc_id += 1;
155+
156+
// Defer HashMap insert - only insert when we actually need to track
157+
// This avoids expensive HashMap operations on hot path
158+
// Objects will be discovered during GC marking phase from roots
159+
self.allocations_since_minor_gc += 1;
160+
161+
let size = match obj_type {
162+
GcObjectType::String => 64,
163+
GcObjectType::Table => 256,
164+
GcObjectType::Function => 128,
165+
};
166+
self.record_allocation(size);
167+
168+
gc_id
169+
}
170+
171+
/// Register object with full tracking (for when we need to track specific objects)
172+
#[inline]
173+
pub fn register_object_tracked(&mut self, obj_id: u32, obj_type: GcObjectType) -> usize {
152174
let gc_id = self.next_gc_id;
153175
self.next_gc_id += 1;
154176

crates/luars/src/lua_vm/dispatcher/table_instructions.rs

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,39 @@ pub fn exec_newtable(vm: &mut LuaVM, instr: u32) -> LuaResult<()> {
1616
let base_ptr = frame.base_ptr;
1717

1818
// NEWTABLE is always followed by an EXTRAARG instruction in Lua 5.4
19-
// PC has already been incremented to point to EXTRAARG
20-
let pc = frame.pc;
21-
frame.pc += 1; // Skip the EXTRAARG instruction
22-
23-
// OPTIMIZATION: Inline EXTRAARG reading to avoid function pointer overhead
24-
let extra_arg = unsafe {
25-
let func_ptr = frame.get_function_ptr().unwrap_unchecked();
26-
let func = &*func_ptr;
27-
let func_ref = func.borrow();
28-
let chunk = &func_ref.chunk;
29-
30-
if pc < chunk.code.len() {
31-
Instruction::get_ax(chunk.code[pc])
32-
} else {
33-
0
19+
// Get EXTRAARG and skip it
20+
let extra_arg = if b == 0 {
21+
let pc = frame.pc;
22+
frame.pc += 1; // Skip the EXTRAARG instruction
23+
24+
// Read EXTRAARG from bytecode
25+
unsafe {
26+
let func_ptr = frame.get_function_ptr().unwrap_unchecked();
27+
let func = &*func_ptr;
28+
let func_ref = func.borrow();
29+
let chunk = &func_ref.chunk;
30+
31+
if pc < chunk.code.len() {
32+
Instruction::get_ax(chunk.code[pc])
33+
} else {
34+
0
35+
}
3436
}
37+
} else {
38+
// No EXTRAARG needed, skip it anyway
39+
frame.pc += 1;
40+
0
3541
};
3642

37-
// Calculate array size hint and hash size hint
43+
// Calculate array size hint (fast path: b > 0 means small table)
3844
let array_size = if b > 0 {
3945
(b - 1) as usize
4046
} else {
4147
extra_arg as usize
4248
};
43-
let hash_size = c as usize;
4449

4550
// Create new table with size hints
46-
let table = vm.create_table(array_size, hash_size);
51+
let table = vm.create_table(array_size, c as usize);
4752
vm.register_stack[base_ptr + a] = table;
4853

4954
// GC checkpoint: table now safely stored in register

crates/luars/src/lua_vm/dispatcher/upvalue_instructions.rs

Lines changed: 16 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -319,92 +319,32 @@ pub fn exec_setlist(vm: &mut LuaVM, instr: u32) -> LuaResult<()> {
319319
let a = Instruction::get_a(instr) as usize;
320320
let b = Instruction::get_b(instr) as usize;
321321
let c = Instruction::get_c(instr) as usize;
322-
let _k = Instruction::get_k(instr);
323322

324323
let frame = vm.current_frame();
325324
let base_ptr = frame.base_ptr;
326-
327325
let table = vm.register_stack[base_ptr + a];
328326

329-
// Calculate starting index
330-
let start_idx = c * 50 + 1; // LFIELDS_PER_FLUSH = 50 in Lua
331-
332-
let count = if b == 0 {
333-
// Use all values to top of stack
334-
frame.top - a - 1
335-
} else {
336-
b
337-
};
338-
339-
// OPTIMIZATION: SETLIST is typically used for table initialization
340-
// Batch all operations in a single borrow_mut() to minimize RefCell overhead
341-
// This is safe because:
342-
// 1. SETLIST is only emitted by compiler for table constructors
343-
// 2. Newly created tables don't have metatables yet
344-
// 3. Even if metatable exists, Lua doesn't call __newindex for array part
345-
346-
// Fast path: Use cached pointer (avoids ObjectPool lookup)
347-
if let Some(lua_table) = table.as_lua_table() {
348-
// ULTRA-OPTIMIZED: Sequential insertion from start_idx
349-
let mut table_mut = lua_table.borrow_mut();
350-
351-
// Reserve capacity upfront to avoid reallocations
352-
let expected_end_idx = (start_idx - 1 + count) as usize;
353-
let current_len = table_mut.array.len();
354-
if table_mut.array.capacity() < expected_end_idx && expected_end_idx > current_len {
355-
table_mut.array.reserve(expected_end_idx - current_len);
327+
let start_idx = c * 50; // 0-based for array indexing
328+
let count = if b == 0 { frame.top - a - 1 } else { b };
329+
330+
// Fast path: direct array manipulation
331+
if let Some(ptr) = table.as_table_ptr() {
332+
let lua_table = unsafe { &*ptr };
333+
let mut t = lua_table.borrow_mut();
334+
335+
// Reserve space
336+
let needed = start_idx + count;
337+
if t.array.len() < needed {
338+
t.array.resize(needed, crate::LuaValue::nil());
356339
}
357-
358-
// Batch process all values with proper index handling
340+
341+
// Copy all values
359342
for i in 0..count {
360-
let key_int = (start_idx + i) as i64;
361-
let value = vm.register_stack[base_ptr + a + i + 1];
362-
363-
// Use optimized set_int which correctly handles nils and gaps
364-
let idx = (key_int - 1) as usize;
365-
366-
if value.is_nil() {
367-
// Nil values: only store if filling a gap in existing array
368-
if idx < table_mut.array.len() {
369-
table_mut.array[idx] = crate::LuaValue::nil();
370-
}
371-
// Don't extend array for trailing nils
372-
} else {
373-
// Non-nil value: ensure array is large enough
374-
if idx < table_mut.array.len() {
375-
table_mut.array[idx] = value;
376-
} else if idx == table_mut.array.len() {
377-
// Fast append
378-
table_mut.array.push(value);
379-
} else {
380-
// Gap: fill with nils then append value
381-
while table_mut.array.len() < idx {
382-
table_mut.array.push(crate::LuaValue::nil());
383-
}
384-
table_mut.array.push(value);
385-
}
386-
}
387-
}
388-
drop(table_mut); // Release borrow early
389-
390-
// Write barrier for GC (outside borrow scope)
391-
// OPTIMIZATION: Only check for GC-relevant values
392-
if let Some(table_id) = table.as_table_id() {
393-
vm.gc
394-
.barrier_forward(crate::gc::GcObjectType::Table, table_id.0);
395-
396-
// Optimized barrier: skip primitives
397-
for i in 0..count {
398-
let value = vm.register_stack[base_ptr + a + i + 1];
399-
if !value.is_nil() && !value.is_number() && !value.is_boolean() {
400-
vm.gc.barrier_back(&value);
401-
}
402-
}
343+
t.array[start_idx + i] = vm.register_stack[base_ptr + a + 1 + i];
403344
}
404345
} else {
405-
// Slow path: fallback to table_set_with_meta
406346
for i in 0..count {
407-
let key = LuaValue::integer((start_idx + i) as i64);
347+
let key = LuaValue::integer((start_idx + i + 1) as i64);
408348
let value = vm.register_stack[base_ptr + a + i + 1];
409349
vm.table_set_with_meta(table, key, value)?;
410350
}

crates/luars/src/lua_vm/mod.rs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,30 +1219,15 @@ impl LuaVM {
12191219
/// Create a new table in object pool
12201220
#[inline]
12211221
pub fn create_table(&mut self, array_size: usize, hash_size: usize) -> LuaValue {
1222-
let id = self.object_pool.create_table(array_size, hash_size);
1222+
let (id, ptr) = self.object_pool.create_table(array_size, hash_size);
12231223

1224-
// Estimate memory cost: base overhead + array + hash part
1225-
// Base: ~64 bytes for LuaTable struct + Rc overhead
1226-
// Array: array_size * 16 bytes per LuaValue
1227-
// Hash: hash_size * 32 bytes per Node (key+value pair)
1228-
let estimated_bytes = 64 + (array_size * 16) + (hash_size * 32);
1229-
self.gc.record_allocation(estimated_bytes);
1230-
1231-
// Register with GC for manual collection
1224+
// Register with GC - record_allocation is called inside register_object
12321225
self.gc
12331226
.register_object(id.0, crate::gc::GcObjectType::Table);
12341227

12351228
// GC check MUST NOT happen here - object not yet protected!
12361229
// Caller must call check_gc() AFTER storing value in register
12371230

1238-
// Get pointer from object pool for direct access
1239-
// OPTIMIZATION: Use unwrap_unchecked in release mode since we just created the table
1240-
let ptr = unsafe {
1241-
self.object_pool
1242-
.get_table(id)
1243-
.map(|t| Rc::as_ptr(t) as *const std::cell::RefCell<LuaTable>)
1244-
.unwrap_unchecked()
1245-
};
12461231
LuaValue::table_id_ptr(id, ptr)
12471232
}
12481233

crates/luars/src/object_pool.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,10 +250,11 @@ impl ObjectPool {
250250

251251
/// Create a new table
252252
#[inline]
253-
pub fn create_table(&mut self, array_size: usize, hash_size: usize) -> TableId {
253+
pub fn create_table(&mut self, array_size: usize, hash_size: usize) -> (TableId, *const RefCell<LuaTable>) {
254254
let table = Rc::new(RefCell::new(LuaTable::new(array_size, hash_size)));
255+
let ptr = Rc::as_ptr(&table);
255256
let slot_id = self.tables.insert(table);
256-
TableId(slot_id)
257+
(TableId(slot_id), ptr)
257258
}
258259

259260
/// Get table by ID

0 commit comments

Comments
 (0)