mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-16 20:51:04 +00:00
Minor changes in performance
This commit is contained in:
@@ -51,17 +51,8 @@ compile time — no table is built during a normal library build:
|
||||
odin run <arch>/tablegen # ENCODING_TABLE -> generated Odin + <arch>/tables.odin
|
||||
odin run <arch>/tablegen/generated # -> <arch>/tables/<arch>.*.bin
|
||||
```
|
||||
|
||||
Regenerate after editing `ENCODING_TABLE`. See `docs/table_migration.md`.
|
||||
|
||||
## Performance (x86)
|
||||
|
||||
With `-o:speed -microarch:native -no-bounds-check`:
|
||||
- Encoder: ~17 M instructions/sec (~56 MB/s)
|
||||
- Decoder: ~16 M instructions/sec (~54 MB/s)
|
||||
|
||||
Measured on AMD Ryzen 3950X.
|
||||
|
||||
## Usage
|
||||
|
||||
```odin
|
||||
|
||||
@@ -269,7 +269,7 @@ decode_opcode :: proc(state: ^Decoder_State) -> (entry: ^Decode_Entry, vex_entry
|
||||
}
|
||||
|
||||
// Handle VEX/EVEX encoded instructions
|
||||
if state.vex_type != .NONE {
|
||||
if state.vex_type != nil {
|
||||
return decode_opcode_vex(state)
|
||||
}
|
||||
|
||||
@@ -575,8 +575,7 @@ decode_operands :: proc(state: ^Decoder_State, entry: ^Decode_Entry) -> (inst: I
|
||||
|
||||
// Check if we need ModR/M
|
||||
needs_modrm := false
|
||||
for _, i in entry.enc {
|
||||
enc := entry.enc[i]
|
||||
for enc in entry.enc {
|
||||
if enc == .MR || enc == .REG || enc == .VVVV {
|
||||
needs_modrm = true
|
||||
break
|
||||
@@ -617,25 +616,20 @@ decode_operands :: proc(state: ^Decoder_State, entry: ^Decode_Entry) -> (inst: I
|
||||
}
|
||||
|
||||
// Decode each operand
|
||||
for _, i in entry.ops {
|
||||
op_type := entry.ops[i]
|
||||
op_enc := entry.enc[i]
|
||||
|
||||
for op_type, i in entry.ops {
|
||||
if op_type == .NONE {
|
||||
break
|
||||
}
|
||||
op_enc := entry.enc[i]
|
||||
|
||||
// i386: default_64 entries have R64/RM64 operand types but
|
||||
// really mean R32/RM32 in 32-bit mode (same encoded bytes).
|
||||
effective := mode_rewrite_op_type(op_type, state.mode, entry.flags.default_64)
|
||||
inst.ops[i], err = decode_single_operand(state, effective, op_enc, modrm_info, sib_info, has_sib)
|
||||
if err != nil {
|
||||
return {}, err
|
||||
}
|
||||
inst.ops[i] = decode_single_operand(state, effective, op_enc, modrm_info, sib_info, has_sib) or_return
|
||||
inst.operand_count += 1
|
||||
}
|
||||
|
||||
return inst, .NONE
|
||||
return
|
||||
}
|
||||
|
||||
decode_operands_vex :: proc(state: ^Decoder_State, entry: ^VEX_Decode_Entry) -> (inst: Instruction, err: Error_Code) {
|
||||
@@ -664,30 +658,25 @@ decode_operands_vex :: proc(state: ^Decoder_State, entry: ^VEX_Decode_Entry) ->
|
||||
}
|
||||
|
||||
// Decode each operand
|
||||
for _, i in entry.ops {
|
||||
op_type := entry.ops[i]
|
||||
op_enc := entry.enc[i]
|
||||
|
||||
for op_type, i in entry.ops {
|
||||
if op_type == .NONE {
|
||||
break
|
||||
}
|
||||
op_enc := entry.enc[i]
|
||||
|
||||
inst.ops[i], err = decode_single_operand_vex(state, op_type, op_enc, modrm_info, sib_info, has_sib)
|
||||
if err != nil {
|
||||
return {}, err
|
||||
}
|
||||
inst.ops[i] = decode_single_operand_vex(state, op_type, op_enc, modrm_info, sib_info, has_sib) or_return
|
||||
inst.operand_count += 1
|
||||
}
|
||||
|
||||
return inst, .NONE
|
||||
return
|
||||
}
|
||||
|
||||
decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_enc: Operand_Encoding,
|
||||
modrm_info: ModRM_Info, sib_info: SIB_Info, has_sib: bool) -> (op: Operand, err: Error_Code) {
|
||||
modrm_info: ModRM_Info, sib_info: SIB_Info, has_sib: bool) -> (op: Operand, err: Error_Code) {
|
||||
|
||||
switch op_enc {
|
||||
case .NONE:
|
||||
return {}, .NONE
|
||||
return
|
||||
|
||||
case .REG:
|
||||
// Register encoded in ModR/M.reg
|
||||
@@ -696,7 +685,8 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
register_number += 8
|
||||
}
|
||||
reg := decode_register(register_number, op_type, state.rex)
|
||||
return op_reg(reg), .NONE
|
||||
op = op_reg(reg)
|
||||
return
|
||||
|
||||
case .MR:
|
||||
// Register or memory in ModR/M.rm
|
||||
@@ -707,7 +697,8 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
register_number += 8
|
||||
}
|
||||
reg := decode_register(register_number, op_type, state.rex)
|
||||
return op_reg(reg), .NONE
|
||||
op = op_reg(reg)
|
||||
return
|
||||
} else {
|
||||
// Memory
|
||||
return decode_memory_operand(state, modrm_info, sib_info, has_sib, op_type)
|
||||
@@ -716,43 +707,44 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
case .IB:
|
||||
// 8-bit immediate or rel8
|
||||
if state.position >= len(state.data) {
|
||||
return {}, .BUFFER_TOO_SHORT
|
||||
err = .BUFFER_TOO_SHORT
|
||||
return
|
||||
}
|
||||
immediate_value := i64(i8(state.data[state.position]))
|
||||
state.position += 1
|
||||
if op_type == .REL8 {
|
||||
return Operand{kind = .RELATIVE, relative = immediate_value, size = 1}, .NONE
|
||||
}
|
||||
return Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 1}, .NONE
|
||||
op = Operand{kind = (op_type == .REL8 ? .RELATIVE : .IMMEDIATE), relative = immediate_value, size = 1}
|
||||
return
|
||||
|
||||
case .IW:
|
||||
// 16-bit immediate
|
||||
if state.position + 2 > len(state.data) {
|
||||
return {}, .BUFFER_TOO_SHORT
|
||||
err = .BUFFER_TOO_SHORT
|
||||
return
|
||||
}
|
||||
immediate_value := i64(i16(u16(state.data[state.position]) | u16(state.data[state.position+1]) << 8))
|
||||
state.position += 2
|
||||
return Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 2}, .NONE
|
||||
op = Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 2}
|
||||
return
|
||||
|
||||
case .ID:
|
||||
// 32-bit immediate or rel32
|
||||
if state.position + 4 > len(state.data) {
|
||||
return {}, .BUFFER_TOO_SHORT
|
||||
err = .BUFFER_TOO_SHORT
|
||||
return
|
||||
}
|
||||
immediate_value := i64(i32(u32(state.data[state.position]) |
|
||||
u32(state.data[state.position+1]) << 8 |
|
||||
u32(state.data[state.position+2]) << 16 |
|
||||
u32(state.data[state.position+3]) << 24))
|
||||
state.position += 4
|
||||
if op_type == .REL32 {
|
||||
return Operand{kind = .RELATIVE, relative = immediate_value, size = 4}, .NONE
|
||||
}
|
||||
return Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 4}, .NONE
|
||||
op = Operand{kind = (op_type == .REL32 ? .RELATIVE : .IMMEDIATE), relative = immediate_value, size = 4}
|
||||
return
|
||||
|
||||
case .IQ:
|
||||
// 64-bit immediate
|
||||
if state.position + 8 > len(state.data) {
|
||||
return {}, .BUFFER_TOO_SHORT
|
||||
err = .BUFFER_TOO_SHORT
|
||||
return
|
||||
}
|
||||
immediate_value := i64(u64(state.data[state.position]) |
|
||||
u64(state.data[state.position+1]) << 8 |
|
||||
@@ -763,7 +755,8 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
u64(state.data[state.position+6]) << 48 |
|
||||
u64(state.data[state.position+7]) << 56)
|
||||
state.position += 8
|
||||
return Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 8}, .NONE
|
||||
op = Operand{kind = .IMMEDIATE, immediate = immediate_value, size = 8}
|
||||
return
|
||||
|
||||
case .IMPL:
|
||||
// Implicit register - decode from operand type
|
||||
@@ -776,7 +769,8 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
register_number += 8
|
||||
}
|
||||
reg := decode_register(register_number, op_type, state.rex)
|
||||
return op_reg(reg), .NONE
|
||||
op = op_reg(reg)
|
||||
return
|
||||
|
||||
case .VVVV:
|
||||
// VEX.vvvv register
|
||||
@@ -785,25 +779,28 @@ decode_single_operand :: proc(state: ^Decoder_State, op_type: Operand_Type, op_e
|
||||
register_number += 16
|
||||
}
|
||||
reg := decode_register(register_number, op_type, state.rex)
|
||||
return op_reg(reg), .NONE
|
||||
op = op_reg(reg)
|
||||
return
|
||||
|
||||
case .IS4:
|
||||
// Immediate byte with register in high 4 bits
|
||||
if state.position >= len(state.data) {
|
||||
return {}, .BUFFER_TOO_SHORT
|
||||
err = .BUFFER_TOO_SHORT
|
||||
return
|
||||
}
|
||||
immediate_byte := state.data[state.position]
|
||||
state.position += 1
|
||||
register_number := (immediate_byte >> 4) & 0x0F
|
||||
reg := decode_register(register_number, op_type, state.rex)
|
||||
return op_reg(reg), .NONE
|
||||
op = op_reg(reg)
|
||||
return
|
||||
|
||||
case .AAA:
|
||||
// EVEX opmask - already decoded in state
|
||||
return {}, .NONE
|
||||
return
|
||||
}
|
||||
|
||||
return {}, .NONE
|
||||
return
|
||||
}
|
||||
|
||||
decode_single_operand_vex :: proc(state: ^Decoder_State, op_type: Operand_Type, op_enc: Operand_Encoding,
|
||||
@@ -940,7 +937,7 @@ decode_memory_operand :: proc(state: ^Decoder_State, modrm_info: ModRM_Info,
|
||||
// 8.8 Register Decoding Helpers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
decode_register :: proc(num: u8, op_type: Operand_Type, rex: u8) -> Register {
|
||||
decode_register :: #force_inline proc "contextless" (num: u8, op_type: Operand_Type, rex: u8) -> Register {
|
||||
#partial switch op_type {
|
||||
case .R64, .RM64:
|
||||
return gpr64_from_num(num)
|
||||
@@ -1032,7 +1029,7 @@ decode :: proc(
|
||||
}
|
||||
|
||||
data_length := len(data)
|
||||
pos: u32 = 0
|
||||
pos := 0
|
||||
has_errors := false
|
||||
|
||||
// Track branch targets for label inference (resolved in pass 2 by isa).
|
||||
@@ -1043,12 +1040,12 @@ decode :: proc(
|
||||
// PASS 1: Decode all instructions, collect branch targets
|
||||
// =========================================================================
|
||||
|
||||
for pos < u32(data_length) {
|
||||
for pos < data_length {
|
||||
inst: Instruction
|
||||
info: Instruction_Info
|
||||
|
||||
// Record offset
|
||||
info.offset = pos
|
||||
info.offset = u32(pos)
|
||||
|
||||
// Initialize decoder state
|
||||
state := Decoder_State{
|
||||
@@ -1080,7 +1077,7 @@ decode :: proc(
|
||||
is_dec := (b & 0x08) != 0
|
||||
reg: Register = state.prefix_66 ? gpr16_from_num(reg_num) : gpr32_from_num(reg_num)
|
||||
|
||||
inst.mnemonic = is_dec ? Mnemonic.DEC : Mnemonic.INC
|
||||
inst.mnemonic = is_dec ? .DEC : .INC
|
||||
inst.operand_count = 1
|
||||
inst.ops[0] = op_reg(reg)
|
||||
inst.length = u8(state.position)
|
||||
@@ -1095,7 +1092,7 @@ decode :: proc(
|
||||
|
||||
append(instructions, inst)
|
||||
append(inst_info, info)
|
||||
pos += u32(state.position)
|
||||
pos += state.position
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -1140,7 +1137,7 @@ decode :: proc(
|
||||
info.rep = inst.flags.rep
|
||||
info.segment = state.segment
|
||||
info.vex_type = state.vex_type
|
||||
if state.vex_type != .NONE && vex_entry != nil {
|
||||
if state.vex_type != nil && vex_entry != nil {
|
||||
// Use encoding requirements to distinguish LIG/WIG from L0/W0
|
||||
// If encoding says LIG, the actual L value doesn't matter for re-encoding
|
||||
// If encoding says L0/L1/L2, we should preserve the actual value
|
||||
@@ -1157,7 +1154,7 @@ decode :: proc(
|
||||
info.evex_b = state.evex_b
|
||||
info.evex_z = state.evex_z
|
||||
info.opmask = state.evex_aaa
|
||||
} else if state.vex_type != .NONE {
|
||||
} else if state.vex_type != nil {
|
||||
// Fallback when vex_entry is nil (shouldn't happen normally)
|
||||
info.vex_l = state.vex_l == 0 ? .L0 : (state.vex_l == 1 ? .L1 : .L2)
|
||||
info.vex_w = state.vex_w ? .W1 : .W0
|
||||
@@ -1167,7 +1164,7 @@ decode :: proc(
|
||||
}
|
||||
|
||||
// Check for relative operands and record pending branch targets
|
||||
inst_end := pos + u32(state.position)
|
||||
inst_end := pos + state.position
|
||||
for op_idx in 0..<inst.operand_count {
|
||||
op := &inst.ops[op_idx]
|
||||
if op.kind == .RELATIVE {
|
||||
@@ -1186,17 +1183,17 @@ decode :: proc(
|
||||
append(instructions, inst)
|
||||
append(inst_info, info)
|
||||
|
||||
pos += u32(state.position)
|
||||
pos += state.position
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// PASS 2: Infer labels from branch targets within the decoded region
|
||||
// =========================================================================
|
||||
|
||||
isa.infer_labels_from_branches(pending_branches[:], pos, label_defs, relocs)
|
||||
isa.infer_labels_from_branches(pending_branches[:], u32(pos), label_defs, relocs)
|
||||
|
||||
return Result{
|
||||
byte_count = pos,
|
||||
byte_count = u32(pos),
|
||||
success = !has_errors,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +705,7 @@ encode :: proc(
|
||||
// Check if instruction matches encoding (inlined for hot path).
|
||||
// `mode` lets default_64 entries match 32-bit operands in i386 and
|
||||
// filters out mode-restricted (mode_32_only) encodings when not in i386.
|
||||
encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction, enc: ^Encoding, mode: Mode) -> bool {
|
||||
encoding_matches_inline :: proc "contextless" (inst: ^Instruction, enc: ^Encoding, mode: Mode) -> bool {
|
||||
// Mode gate: skip i386-only encodings (short-form INC/DEC at 0x40-0x4F)
|
||||
// when not in Mode._32.
|
||||
if enc.flags.mode_32_only && mode != ._32 { return false }
|
||||
@@ -740,7 +740,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
|
||||
|
||||
if user_idx >= inst.operand_count - 1 { return false }
|
||||
effective_op_type := mode_rewrite_op_type(op_type, mode, enc.flags.default_64)
|
||||
if !operand_matches_inline(&inst.ops[user_idx], effective_op_type) { return false }
|
||||
operand_matches_inline(&inst.ops[user_idx], effective_op_type) or_return
|
||||
user_idx += 1
|
||||
}
|
||||
return user_idx == inst.operand_count - 1
|
||||
@@ -757,7 +757,7 @@ encoding_matches_inline :: #force_inline proc "contextless" (inst: ^Instruction,
|
||||
|
||||
if user_idx >= inst.operand_count { return false }
|
||||
effective_op_type := mode_rewrite_op_type(op_type, mode, enc.flags.default_64)
|
||||
if !operand_matches_inline(&inst.ops[user_idx], effective_op_type) { return false }
|
||||
operand_matches_inline(&inst.ops[user_idx], effective_op_type) or_return
|
||||
user_idx += 1
|
||||
}
|
||||
|
||||
@@ -861,16 +861,16 @@ imm_matches_inline :: #force_inline proc "contextless" (op: ^Operand, op_type: O
|
||||
#partial switch op_type {
|
||||
case .IMM8:
|
||||
// Full 8-bit range: signed [-128, 127] OR unsigned [0, 255]
|
||||
return op.immediate >= -128 && op.immediate <= 255
|
||||
return -128 <= op.immediate && op.immediate <= 255
|
||||
case .IMM8SX:
|
||||
// Sign-extended 8-bit: must be in signed 8-bit range
|
||||
return op.immediate >= -128 && op.immediate <= 127
|
||||
return -128 <= op.immediate && op.immediate <= 127
|
||||
case .IMM16:
|
||||
// Full 16-bit range: signed [-32768, 32767] OR unsigned [0, 65535]
|
||||
return op.immediate >= -32768 && op.immediate <= 65535
|
||||
return -32768 <= op.immediate && op.immediate <= 65535
|
||||
case .IMM32:
|
||||
// Full 32-bit range: signed [-2147483648, 2147483647] OR unsigned [0, 4294967295]
|
||||
return op.immediate >= -2147483648 && op.immediate <= 4294967295
|
||||
return -2147483648 <= op.immediate && op.immediate <= 4294967295
|
||||
case .IMM64:
|
||||
return true // Any i64 value fits
|
||||
}
|
||||
|
||||
@@ -324,13 +324,13 @@ reg_needs_evex :: #force_inline proc "contextless" (r: Register) -> bool {
|
||||
@(require_results)
|
||||
reg_is_gpr :: #force_inline proc "contextless" (r: Register) -> bool {
|
||||
c := reg_class(r)
|
||||
return c >= REG_GPR64 && c <= REG_GPR8H
|
||||
return REG_GPR64 <= c && c <= REG_GPR8H
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
reg_is_vector :: #force_inline proc "contextless" (r: Register) -> bool {
|
||||
c := reg_class(r)
|
||||
return c >= REG_XMM && c <= REG_ZMM
|
||||
return REG_XMM <= c && c <= REG_ZMM
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -340,7 +340,7 @@ reg_is_high_byte :: #force_inline proc "contextless" (r: Register) -> bool {
|
||||
|
||||
// Size in bits for register
|
||||
@(require_results)
|
||||
reg_size :: proc "contextless" (r: Register) -> u16 {
|
||||
reg_size :: #force_inline proc "contextless" (r: Register) -> u16 {
|
||||
switch reg_class(r) {
|
||||
case REG_GPR64: return 64
|
||||
case REG_GPR32: return 32
|
||||
@@ -382,19 +382,18 @@ gpr16_from_num :: #force_inline proc "contextless" (num: u8) -> Register {
|
||||
return num < 16 ? Register(REG_GPR16 | u16(num)) : NONE
|
||||
}
|
||||
|
||||
gpr8_from_num :: proc(num: u8, has_rex: bool) -> Register {
|
||||
@(require_results)
|
||||
gpr8_from_num :: #force_inline proc "contextless" (num: u8, has_rex: bool) -> Register {
|
||||
// Without REX prefix, nums 4-7 encode AH/CH/DH/BH (high byte legacy regs)
|
||||
// With REX prefix, nums 4-7 encode SPL/BPL/SIL/DIL (low byte regs)
|
||||
if has_rex {
|
||||
return num < 16 ? Register(REG_GPR8 | u16(num)) : NONE
|
||||
} else {
|
||||
if num < 4 {
|
||||
return Register(REG_GPR8 | u16(num)) // AL, CL, DL, BL
|
||||
} else if num < 8 {
|
||||
return Register(REG_GPR8H | u16(num)) // AH, CH, DH, BH (hw num 4-7)
|
||||
}
|
||||
return NONE
|
||||
} else if num < 4 {
|
||||
return Register(REG_GPR8 | u16(num)) // AL, CL, DL, BL
|
||||
} else if num < 8 {
|
||||
return Register(REG_GPR8H | u16(num)) // AH, CH, DH, BH (hw num 4-7)
|
||||
}
|
||||
return NONE
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -6,7 +6,6 @@ import x86 "../"
|
||||
import "../../isa"
|
||||
import "core:fmt"
|
||||
import "core:time"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
import "core:mem/virtual"
|
||||
import "core:math"
|
||||
@@ -3051,7 +3050,7 @@ run_benchmarks :: proc() {
|
||||
bench_insts := make([dynamic]x86.Instruction)
|
||||
defer delete(bench_insts)
|
||||
|
||||
for _ in 0..<1000 {
|
||||
for _ in 0..<100 {
|
||||
insts := []x86.Instruction{
|
||||
x86.inst_r(.PUSH, x86.RBP),
|
||||
x86.inst_r_r(.MOV, x86.RBP, x86.RSP),
|
||||
@@ -3071,25 +3070,34 @@ run_benchmarks :: proc() {
|
||||
append(&bench_insts, ..insts)
|
||||
}
|
||||
|
||||
code_buf: [16 * 1024]u8
|
||||
code_buf := make([]byte, 1<<16)
|
||||
defer delete(code_buf)
|
||||
|
||||
labels: [4]x86.Label_Definition
|
||||
|
||||
relocs: [dynamic]x86.Relocation; defer delete(relocs)
|
||||
errs: [dynamic]x86.Error; defer delete(relocs)
|
||||
|
||||
insts: [dynamic]x86.Instruction; defer delete(insts)
|
||||
info: [dynamic]x86.Instruction_Info; defer delete(info)
|
||||
lbls: [dynamic]x86.Label_Definition; defer delete(lbls)
|
||||
|
||||
// Encode
|
||||
enc_start := time.now()
|
||||
enc_bytes := 0
|
||||
for _ in 0..<ITERATIONS {
|
||||
relocs: [dynamic]x86.Relocation; defer delete(relocs)
|
||||
errs: [dynamic]x86.Error; defer delete(errs)
|
||||
clear(&relocs)
|
||||
clear(&errs)
|
||||
result := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
|
||||
enc_bytes += int(result.byte_count)
|
||||
}
|
||||
enc_dur := time.duration_microseconds(time.since(enc_start))
|
||||
enc_dur := time.duration_seconds(time.since(enc_start))
|
||||
|
||||
// Get encoded length for decode
|
||||
encoded_len: u32
|
||||
{
|
||||
relocs: [dynamic]x86.Relocation; defer delete(relocs)
|
||||
errs: [dynamic]x86.Error; defer delete(errs)
|
||||
clear(&relocs)
|
||||
clear(&errs)
|
||||
result := x86.encode(bench_insts[:], labels[:], code_buf[:], &relocs, &errs, true, 0)
|
||||
encoded_len = result.byte_count
|
||||
}
|
||||
@@ -3098,22 +3106,22 @@ run_benchmarks :: proc() {
|
||||
dec_start := time.now()
|
||||
dec_insts := 0
|
||||
for _ in 0..<ITERATIONS {
|
||||
insts: [dynamic]x86.Instruction; defer delete(insts)
|
||||
info: [dynamic]x86.Instruction_Info; defer delete(info)
|
||||
lbls: [dynamic]x86.Label_Definition; defer delete(lbls)
|
||||
errs: [dynamic]x86.Error; defer delete(errs)
|
||||
clear(&insts)
|
||||
clear(&info)
|
||||
clear(&lbls)
|
||||
clear(&errs)
|
||||
x86.decode(code_buf[:encoded_len], nil, &insts, &info, &lbls, &errs)
|
||||
dec_insts += len(insts)
|
||||
}
|
||||
dec_dur := time.duration_microseconds(time.since(dec_start))
|
||||
dec_dur := time.duration_seconds(time.since(dec_start))
|
||||
|
||||
enc_ips := f64(ITERATIONS * len(bench_insts)) / (enc_dur / 1_000_000)
|
||||
dec_ips := f64(dec_insts) / (dec_dur / 1_000_000)
|
||||
enc_mbps := f64(enc_bytes) / (enc_dur / 1_000_000) / 1_000_000
|
||||
dec_mbps := f64(ITERATIONS * int(encoded_len)) / (dec_dur / 1_000_000) / 1_000_000
|
||||
enc_ips := f64(ITERATIONS * len(bench_insts)) / enc_dur
|
||||
dec_ips := f64(dec_insts) / dec_dur
|
||||
enc_bps := u64(f64(enc_bytes) / enc_dur)
|
||||
dec_bps := u64(f64(ITERATIONS * int(encoded_len)) / dec_dur)
|
||||
|
||||
fmt.printf(" Encoder: %.1f M insts/sec (%.1f MB/s)\n", enc_ips / 1_000_000, enc_mbps)
|
||||
fmt.printf(" Decoder: %.1f M insts/sec (%.1f MB/s)\n", dec_ips / 1_000_000, dec_mbps)
|
||||
fmt.printf(" Encoder: %.1f M insts/sec (%.1M/s)\n", enc_ips / 1_000_000, enc_bps)
|
||||
fmt.printf(" Decoder: %.1f M insts/sec (%.1M/s)\n", dec_ips / 1_000_000, dec_bps)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user