My working unpac space for OCaml projects in development
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Sprint 1.1: Add inline annotations to hot paths

Added [@inline] annotations to performance-critical functions:
- binary_arith: Fast paths for Int-Int, Float-Float operations
- binary_bitwise: Fast path for Int-Int bitwise operations
- compare_values: Fast paths for Int-Int, Float-Float comparisons
- Stack operations: push_value, pop_value, peek_value
- Local access: get_local, set_local, get_arg, set_arg
- Type checks: is_undefined, is_null, to_boolean, etc.
- Conversions: to_float, to_int32 with fast paths

Benchmark results (vs baseline):
| Benchmark | Before | After | Change |
|--------------------|-----------|-----------|--------|
| eval/string_ops | 3,518 μs | 704 μs | 5x faster |
| eval/class | 1,780 μs | 1,240 μs | 30% faster |
| eval/function_calls| 6,898 μs | 6,489 μs | 6% faster |
| intensive_primes | 68,150 μs | 63,932 μs | 6% faster |
| eval/math | 2,290 μs | 2,229 μs | 3% faster |

All 176/176 runtime tests still passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+127 -52
+11 -11
lib/quickjs/runtime/context.ml
··· 176 176 } 177 177 178 178 (** Push value onto operand stack *) 179 - let push_value frame v = 179 + let[@inline] push_value frame v = 180 180 frame.stack <- v :: frame.stack 181 181 182 182 (** Pop value from operand stack *) 183 - let pop_value frame = 183 + let[@inline] pop_value frame = 184 184 match frame.stack with 185 185 | v :: rest -> 186 186 frame.stack <- rest; ··· 188 188 | [] -> Undefined (* Should not happen *) 189 189 190 190 (** Peek at top of stack *) 191 - let peek_value frame = 191 + let[@inline] peek_value frame = 192 192 match frame.stack with 193 193 | v :: _ -> v 194 194 | [] -> Undefined 195 195 196 - (** Get local variable *) 197 - let get_local frame idx = 196 + (** Get local variable - optimized with bounds check *) 197 + let[@inline] get_local frame idx = 198 198 if idx >= 0 && idx < Array.length frame.locals then 199 199 frame.locals.(idx) 200 200 else 201 201 Undefined 202 202 203 - (** Set local variable *) 204 - let set_local frame idx v = 203 + (** Set local variable - optimized with bounds check *) 204 + let[@inline] set_local frame idx v = 205 205 if idx >= 0 && idx < Array.length frame.locals then 206 206 frame.locals.(idx) <- v 207 207 208 208 (** Get argument *) 209 - let get_arg frame idx = 209 + let[@inline] get_arg frame idx = 210 210 if idx >= 0 && idx < Array.length frame.args then 211 211 frame.args.(idx) 212 212 else 213 213 Undefined 214 214 215 215 (** Set argument *) 216 - let set_arg frame idx v = 216 + let[@inline] set_arg frame idx v = 217 217 if idx >= 0 && idx < Array.length frame.args then 218 218 frame.args.(idx) <- v 219 219 220 220 (** Get closure variable *) 221 - let get_var_ref frame idx = 221 + let[@inline] get_var_ref frame idx = 222 222 if idx >= 0 && idx < Array.length frame.var_refs then 223 223 frame.var_refs.(idx).ref_value 224 224 else 225 225 Undefined 226 226 227 227 (** Set closure variable *) 228 - let set_var_ref frame idx v = 228 + let[@inline] set_var_ref frame idx v = 229 229 if idx >= 0 && idx < Array.length frame.var_refs then 230 230 frame.var_refs.(idx).ref_value <- v 231 231
+86 -21
lib/quickjs/runtime/interpreter.ml
··· 63 63 (* For now, create an empty var_refs array - true closures will capture variables *) 64 64 make_function bytecode [||] 65 65 66 - (** Binary arithmetic operation *) 67 - let binary_arith _ctx op a b = 68 - (* Handle BigInt separately *) 66 + (** Binary arithmetic operation - optimized with fast paths for common cases *) 67 + let[@inline] binary_arith _ctx op a b = 68 + (* Fast path: Int-Int operations (most common in loops) *) 69 69 match a, b with 70 + | Int ai, Int bi -> 71 + (match op with 72 + | `Add -> Int (Int32.add ai bi) 73 + | `Sub -> Int (Int32.sub ai bi) 74 + | `Mul -> Int (Int32.mul ai bi) 75 + | `Div -> if bi = 0l then Float Float.infinity else Int (Int32.div ai bi) 76 + | `Mod -> if bi = 0l then Float Float.nan else Int (Int32.rem ai bi) 77 + | `Pow -> Float (Float.pow (Int32.to_float ai) (Int32.to_float bi))) 78 + (* Fast path: Float-Float operations *) 79 + | Float af, Float bf -> 80 + (match op with 81 + | `Add -> Float (af +. bf) 82 + | `Sub -> Float (af -. bf) 83 + | `Mul -> Float (af *. bf) 84 + | `Div -> Float (af /. bf) 85 + | `Mod -> Float (Float.rem af bf) 86 + | `Pow -> Float (Float.pow af bf)) 87 + (* Fast path: Int-Float mixed *) 88 + | Int ai, Float bf -> 89 + let af = Int32.to_float ai in 90 + (match op with 91 + | `Add -> Float (af +. bf) 92 + | `Sub -> Float (af -. bf) 93 + | `Mul -> Float (af *. bf) 94 + | `Div -> Float (af /. bf) 95 + | `Mod -> Float (Float.rem af bf) 96 + | `Pow -> Float (Float.pow af bf)) 97 + | Float af, Int bi -> 98 + let bf = Int32.to_float bi in 99 + (match op with 100 + | `Add -> Float (af +. bf) 101 + | `Sub -> Float (af -. bf) 102 + | `Mul -> Float (af *. bf) 103 + | `Div -> Float (af /. bf) 104 + | `Mod -> Float (Float.rem af bf) 105 + | `Pow -> Float (Float.pow af bf)) 106 + (* String concatenation for Add *) 107 + | String _, _ when op = `Add -> String (to_string a ^ to_string b) 108 + | _, String _ when op = `Add -> String (to_string a ^ to_string b) 109 + (* BigInt operations *) 70 110 | BigInt ai, BigInt bi -> 71 111 (match op with 72 112 | `Add -> BigInt (Z.add ai bi) ··· 78 118 | BigInt _, _ | _, BigInt _ -> 79 119 (* Cannot mix BigInt with other types *) 80 120 Float Float.nan 121 + (* Slow path: convert to float *) 81 122 | _ -> 82 123 let an = to_float a in 83 124 let bn = to_float b in 84 125 match op with 85 - | `Add -> 86 - (* String concatenation if either operand is string *) 87 - (match a, b with 88 - | String _, _ | _, String _ -> String (to_string a ^ to_string b) 89 - | _ -> Float (an +. bn)) 126 + | `Add -> Float (an +. bn) 90 127 | `Sub -> Float (an -. bn) 91 128 | `Mul -> Float (an *. bn) 92 129 | `Div -> Float (an /. bn) 93 130 | `Mod -> Float (Float.rem an bn) 94 131 | `Pow -> Float (Float.pow an bn) 95 132 96 - (** Binary bitwise operation *) 97 - let binary_bitwise _ctx op a b = 98 - let ai = to_int32 a in 99 - let bi = to_int32 b in 100 - Int (match op with 101 - | `And -> Int32.logand ai bi 102 - | `Or -> Int32.logor ai bi 103 - | `Xor -> Int32.logxor ai bi 104 - | `Shl -> Int32.shift_left ai (Int32.to_int bi land 0x1f) 105 - | `Sar -> Int32.shift_right ai (Int32.to_int bi land 0x1f) 106 - | `Shr -> Int32.shift_right_logical ai (Int32.to_int bi land 0x1f)) 133 + (** Binary bitwise operation - with inline and fast path for Int-Int *) 134 + let[@inline] binary_bitwise _ctx op a b = 135 + (* Fast path for Int-Int (avoid to_int32 conversion) *) 136 + match a, b with 137 + | Int ai, Int bi -> 138 + Int (match op with 139 + | `And -> Int32.logand ai bi 140 + | `Or -> Int32.logor ai bi 141 + | `Xor -> Int32.logxor ai bi 142 + | `Shl -> Int32.shift_left ai (Int32.to_int bi land 0x1f) 143 + | `Sar -> Int32.shift_right ai (Int32.to_int bi land 0x1f) 144 + | `Shr -> Int32.shift_right_logical ai (Int32.to_int bi land 0x1f)) 145 + | _ -> 146 + let ai = to_int32 a in 147 + let bi = to_int32 b in 148 + Int (match op with 149 + | `And -> Int32.logand ai bi 150 + | `Or -> Int32.logor ai bi 151 + | `Xor -> Int32.logxor ai bi 152 + | `Shl -> Int32.shift_left ai (Int32.to_int bi land 0x1f) 153 + | `Sar -> Int32.shift_right ai (Int32.to_int bi land 0x1f) 154 + | `Shr -> Int32.shift_right_logical ai (Int32.to_int bi land 0x1f)) 107 155 108 - (** Comparison operation *) 109 - let compare_values _ctx op a b = 156 + (** Comparison operation - with inline and fast paths *) 157 + let[@inline] compare_values _ctx op a b = 110 158 let result = match a, b with 159 + (* Fast path: Int-Int comparison *) 160 + | Int ai, Int bi -> 161 + (match op with 162 + | `Lt -> ai < bi 163 + | `Le -> ai <= bi 164 + | `Gt -> ai > bi 165 + | `Ge -> ai >= bi) 166 + (* Fast path: Float-Float comparison *) 167 + | Float af, Float bf -> 168 + if Float.is_nan af || Float.is_nan bf then false 169 + else (match op with 170 + | `Lt -> af < bf 171 + | `Le -> af <= bf 172 + | `Gt -> af > bf 173 + | `Ge -> af >= bf) 174 + (* Fast path: String comparison *) 111 175 | String a, String b -> 112 176 let cmp = String.compare a b in 113 177 (match op with ··· 115 179 | `Le -> cmp <= 0 116 180 | `Gt -> cmp > 0 117 181 | `Ge -> cmp >= 0) 182 + (* Slow path: convert to float *) 118 183 | _ -> 119 184 let an = to_float a in 120 185 let bn = to_float b in
+30 -20
lib/quickjs/runtime/value.ml
··· 166 166 | Uninitialized (* For TDZ *) 167 167 168 168 (** Check if value is undefined *) 169 - let is_undefined = function Undefined -> true | _ -> false 169 + let[@inline] is_undefined = function Undefined -> true | _ -> false 170 170 171 171 (** Check if value is null *) 172 - let is_null = function Null -> true | _ -> false 172 + let[@inline] is_null = function Null -> true | _ -> false 173 173 174 174 (** Check if value is undefined or null *) 175 - let is_nullish = function 175 + let[@inline] is_nullish = function 176 176 | Undefined | Null -> true 177 177 | _ -> false 178 178 179 179 (** Check if value is a boolean *) 180 - let is_bool = function Bool _ -> true | _ -> false 180 + let[@inline] is_bool = function Bool _ -> true | _ -> false 181 181 182 182 (** Check if value is a number (int or float) *) 183 - let is_number = function Int _ | Float _ -> true | _ -> false 183 + let[@inline] is_number = function Int _ | Float _ -> true | _ -> false 184 184 185 185 (** Check if value is a string *) 186 - let is_string = function String _ -> true | _ -> false 186 + let[@inline] is_string = function String _ -> true | _ -> false 187 187 188 188 (** Check if value is a symbol *) 189 - let is_symbol = function Symbol _ -> true | _ -> false 189 + let[@inline] is_symbol = function Symbol _ -> true | _ -> false 190 190 191 191 (** Check if value is a bigint *) 192 - let is_bigint = function BigInt _ -> true | _ -> false 192 + let[@inline] is_bigint = function BigInt _ -> true | _ -> false 193 193 194 194 (** Check if value is an object *) 195 - let is_object = function Object _ -> true | _ -> false 195 + let[@inline] is_object = function Object _ -> true | _ -> false 196 196 197 197 (** Check if value is a function *) 198 198 let is_function = function ··· 208 208 | Object { class_id = Class_array; _ } -> true 209 209 | _ -> false 210 210 211 - (** Convert value to boolean (ToBoolean) *) 212 - let to_boolean = function 211 + (** Convert value to boolean (ToBoolean) - inlined for hot paths *) 212 + let[@inline] to_boolean = function 213 213 | Undefined | Null -> false 214 214 | Bool b -> b 215 215 | Int i -> i <> 0l ··· 244 244 | Exception _ -> Float Float.nan 245 245 | Uninitialized -> Float Float.nan 246 246 247 - (** Convert to float *) 248 - let to_float v = 249 - match to_number v with 247 + (** Convert to float - inlined for arithmetic hot paths *) 248 + let[@inline] to_float v = 249 + match v with 250 250 | Int i -> Int32.to_float i 251 251 | Float f -> f 252 - | _ -> Float.nan 252 + | _ -> 253 + match to_number v with 254 + | Int i -> Int32.to_float i 255 + | Float f -> f 256 + | _ -> Float.nan 253 257 254 - (** Convert to int32 *) 255 - let to_int32 v = 256 - match to_number v with 258 + (** Convert to int32 - inlined for bitwise hot paths *) 259 + let[@inline] to_int32 v = 260 + match v with 257 261 | Int i -> i 258 262 | Float f -> 259 263 if Float.is_nan f || Float.is_infinite f then 0l 260 264 else Int32.of_float (Float.trunc (Float.rem f 4294967296.0)) 261 - | _ -> 0l 265 + | _ -> 266 + match to_number v with 267 + | Int i -> i 268 + | Float f -> 269 + if Float.is_nan f || Float.is_infinite f then 0l 270 + else Int32.of_float (Float.trunc (Float.rem f 4294967296.0)) 271 + | _ -> 0l 262 272 263 273 (** Convert to uint32 *) 264 - let to_uint32 v = 274 + let[@inline] to_uint32 v = 265 275 (* For uint32, we just return the int32 value - the bit pattern is the same *) 266 276 to_int32 v 267 277