Skip to content

Commit 04c6e14

Browse files
committed
optimize code
1 parent ee804b4 commit 04c6e14

File tree

2 files changed

+56
-38
lines changed

2 files changed

+56
-38
lines changed

crates/luars/src/lua_value/lua_table.rs

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::LuaValue;
55
use crate::LuaVM;
66

77
/// Hash node - mimics Lua 5.4's Node structure
8-
/// Contains key+value pair and next index for collision chaining
8+
/// Contains key+value pair
99
#[derive(Clone, Copy)]
1010
struct Node {
1111
key: LuaValue,
@@ -75,29 +75,26 @@ impl LuaTable {
7575
}
7676
}
7777

78-
/// Hash function for LuaValue - matches Lua's hashing strategy
79-
#[inline]
78+
/// Hash function for LuaValue - optimized for speed
79+
#[inline(always)]
8080
fn hash_key(key: &LuaValue, size: usize) -> usize {
8181
if size == 0 {
8282
return 0;
8383
}
8484

85-
// Use Lua's approach: extract a numeric hash from the value
86-
let hash = if let Some(i) = key.as_integer() {
87-
i as u64
88-
} else if let Some(f) = key.as_float() {
89-
f.to_bits()
90-
} else {
91-
// For other types, use the primary value as hash
92-
key.primary
93-
};
85+
// Use XOR of both words for good distribution
86+
// This is faster than type-checking and works for all value types
87+
let raw = key.primary ^ key.secondary;
88+
89+
// Simple but effective mixing (from FxHash)
90+
let hash = raw.wrapping_mul(0x517cc1b727220a95);
9491

95-
// Simple modulo - Lua uses power-of-2 sizes for fast masking
92+
// Fast modulo using bitmask (size is power of 2)
9693
(hash as usize) & (size - 1)
9794
}
9895

9996
/// Find a node with the given key, returns Some(index) if found
100-
#[inline]
97+
#[inline(always)]
10198
fn find_node(&self, key: &LuaValue) -> Option<usize> {
10299
let size = self.nodes.len();
103100
if size == 0 {
@@ -107,23 +104,30 @@ impl LuaTable {
107104
let mut idx = Self::hash_key(key, size);
108105
let start_idx = idx;
109106

107+
// Fast path: check first slot (no collision case - most common)
108+
let node = unsafe { self.nodes.get_unchecked(idx) };
109+
if node.is_empty() {
110+
return None;
111+
}
112+
if node.key == *key {
113+
return Some(idx);
114+
}
115+
116+
// Collision path: linear probe
110117
loop {
111-
let node = &self.nodes[idx];
118+
idx = (idx + 1) & (size - 1);
119+
120+
if idx == start_idx {
121+
return None;
122+
}
123+
124+
let node = unsafe { self.nodes.get_unchecked(idx) };
112125
if node.is_empty() {
113126
return None;
114127
}
115-
116128
if node.key == *key {
117129
return Some(idx);
118130
}
119-
120-
// Linear probing
121-
idx = (idx + 1) & (size - 1);
122-
123-
// Avoid infinite loop
124-
if idx == start_idx {
125-
return None;
126-
}
127131
}
128132
}
129133

@@ -150,25 +154,28 @@ impl LuaTable {
150154
}
151155

152156
/// Simple insert using linear probing (no complex chaining)
157+
#[inline]
153158
fn insert_node_simple(&mut self, key: LuaValue, value: LuaValue) {
154159
let size = self.nodes.len();
155160
debug_assert!(size > 0, "insert_node_simple called with empty nodes");
156161

157162
let mut idx = Self::hash_key(&key, size);
163+
164+
// Fast path: first slot is empty (common case)
165+
let node = unsafe { self.nodes.get_unchecked(idx) };
166+
if node.is_empty() {
167+
self.nodes[idx] = Node { key, value };
168+
self.hash_size += 1;
169+
return;
170+
}
171+
if node.key == key {
172+
self.nodes[idx].value = value;
173+
return;
174+
}
175+
176+
// Collision path
158177
let start_idx = idx;
159-
160178
loop {
161-
if self.nodes[idx].is_empty() {
162-
// New insertion
163-
self.nodes[idx] = Node { key, value };
164-
self.hash_size += 1;
165-
return;
166-
} else if self.nodes[idx].key == key {
167-
// Update existing
168-
self.nodes[idx].value = value;
169-
return;
170-
}
171-
172179
idx = (idx + 1) & (size - 1);
173180

174181
if idx == start_idx {
@@ -178,6 +185,17 @@ impl LuaTable {
178185
size, self.hash_size, key
179186
);
180187
}
188+
189+
let node = unsafe { self.nodes.get_unchecked(idx) };
190+
if node.is_empty() {
191+
self.nodes[idx] = Node { key, value };
192+
self.hash_size += 1;
193+
return;
194+
}
195+
if node.key == key {
196+
self.nodes[idx].value = value;
197+
return;
198+
}
181199
}
182200
}
183201

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ pub fn exec_getfield(vm: &mut LuaVM, instr: u32) -> LuaResult<()> {
262262
let lua_table = unsafe { &*ptr };
263263
let borrowed = lua_table.borrow();
264264

265-
// Try direct access
266-
if let Some(val) = borrowed.raw_get(&key_value) {
265+
// Use optimized hash-only lookup (GETFIELD always uses string keys, never integers)
266+
if let Some(val) = borrowed.get_from_hash(&key_value) {
267267
if !val.is_nil() {
268268
vm.register_stack[base_ptr + a] = val;
269269
return Ok(());

0 commit comments

Comments
 (0)