Performance

Here is a snapshot example of using BenchmarkTools on PokerHandEvaluator.jl's base evaluation method evaluate5:

Introspection

using InteractiveUtils, PlayingCards, PokerHandEvaluator
@code_typed PokerHandEvaluator.evaluate5((A♡, A♣, A♠, 3♡, 2♢))
CodeInfo(
1 ───        nothing::Nothing
│     %2   = Base.getfield(t, 2, false)::PlayingCards.Card
│     %3   = Base.getfield(%2, :value)::UInt8
│     %4   = Base.and_int(0x30, %3)::UInt8
│     %5   = Base.lshr_int(%4, 0x0000000000000004)::UInt8
│     %6   = Base.shl_int(%4, 0xfffffffffffffffc)::UInt8
│     %7   = Base.ifelse(true, %5, %6)::UInt8
│     %8   = Base.slt_int(0, 0)::Bool
│     %9   = Base.bitcast(UInt64, 0)::UInt64
│     %10  = Core.zext_int(Core.UInt64, %7)::UInt64
│     %11  = Base.ule_int(%9, %10)::Bool
│     %12  = Base.or_int(%8, %11)::Bool
└────        goto #3 if not %12
2 ─── %14  = Base.sle_int(0, 3)::Bool
│     %15  = Base.bitcast(UInt64, 3)::UInt64
│     %16  = Core.zext_int(Core.UInt64, %7)::UInt64
│     %17  = Base.ule_int(%16, %15)::Bool
│     %18  = Base.and_int(%14, %17)::Bool
└────        goto #4
3 ───        nothing::Nothing
4 ┄── %21  = φ (#2 => %18, #3 => false)::Bool
└────        goto #6 if not %21
5 ─── %23  = PlayingCards.Suit::Type{PlayingCards.Suit}
│     %24  = %new(%23, %7)::PlayingCards.Suit
└────        goto #7
6 ─── %26  = Base.string("invalid suit number: ", %7)::Any
│     %27  = PlayingCards.ArgumentError(%26)::Any
│            PlayingCards.throw(%27)::Union{}
└────        unreachable
7 ───        goto #8
8 ─── %31  = Base.getfield(t, 1, false)::PlayingCards.Card
│     %32  = Base.getfield(%31, :value)::UInt8
│     %33  = Base.and_int(0x30, %32)::UInt8
│     %34  = Base.lshr_int(%33, 0x0000000000000004)::UInt8
│     %35  = Base.shl_int(%33, 0xfffffffffffffffc)::UInt8
│     %36  = Base.ifelse(true, %34, %35)::UInt8
│     %37  = Base.slt_int(0, 0)::Bool
│     %38  = Base.bitcast(UInt64, 0)::UInt64
│     %39  = Core.zext_int(Core.UInt64, %36)::UInt64
│     %40  = Base.ule_int(%38, %39)::Bool
│     %41  = Base.or_int(%37, %40)::Bool
└────        goto #10 if not %41
9 ─── %43  = Base.sle_int(0, 3)::Bool
│     %44  = Base.bitcast(UInt64, 3)::UInt64
│     %45  = Core.zext_int(Core.UInt64, %36)::UInt64
│     %46  = Base.ule_int(%45, %44)::Bool
│     %47  = Base.and_int(%43, %46)::Bool
└────        goto #11
10 ──        nothing::Nothing
11 ┄─ %50  = φ (#9 => %47, #10 => false)::Bool
└────        goto #13 if not %50
12 ── %52  = PlayingCards.Suit::Type{PlayingCards.Suit}
│     %53  = %new(%52, %36)::PlayingCards.Suit
└────        goto #14
13 ── %55  = Base.string("invalid suit number: ", %36)::Any
│     %56  = PlayingCards.ArgumentError(%55)::Any
│            PlayingCards.throw(%56)::Union{}
└────        unreachable
14 ──        goto #15
15 ── %60  = (%53 === %24)::Bool
└────        goto #44 if not %60
16 ── %62  = Base.getfield(t, 3, false)::PlayingCards.Card
│     %63  = Base.getfield(%62, :value)::UInt8
│     %64  = Base.and_int(0x30, %63)::UInt8
│     %65  = Base.lshr_int(%64, 0x0000000000000004)::UInt8
│     %66  = Base.shl_int(%64, 0xfffffffffffffffc)::UInt8
│     %67  = Base.ifelse(true, %65, %66)::UInt8
│     %68  = Base.slt_int(0, 0)::Bool
│     %69  = Base.bitcast(UInt64, 0)::UInt64
│     %70  = Core.zext_int(Core.UInt64, %67)::UInt64
│     %71  = Base.ule_int(%69, %70)::Bool
│     %72  = Base.or_int(%68, %71)::Bool
└────        goto #18 if not %72
17 ── %74  = Base.sle_int(0, 3)::Bool
│     %75  = Base.bitcast(UInt64, 3)::UInt64
│     %76  = Core.zext_int(Core.UInt64, %67)::UInt64
│     %77  = Base.ule_int(%76, %75)::Bool
│     %78  = Base.and_int(%74, %77)::Bool
└────        goto #19
18 ──        nothing::Nothing
19 ┄─ %81  = φ (#17 => %78, #18 => false)::Bool
└────        goto #21 if not %81
20 ── %83  = PlayingCards.Suit::Type{PlayingCards.Suit}
│     %84  = %new(%83, %67)::PlayingCards.Suit
└────        goto #22
21 ── %86  = Base.string("invalid suit number: ", %67)::Any
│     %87  = PlayingCards.ArgumentError(%86)::Any
│            PlayingCards.throw(%87)::Union{}
└────        unreachable
22 ──        goto #23
23 ── %91  = (%24 === %84)::Bool
└────        goto #42 if not %91
24 ── %93  = Base.getfield(t, 4, false)::PlayingCards.Card
│     %94  = Base.getfield(%93, :value)::UInt8
│     %95  = Base.and_int(0x30, %94)::UInt8
│     %96  = Base.lshr_int(%95, 0x0000000000000004)::UInt8
│     %97  = Base.shl_int(%95, 0xfffffffffffffffc)::UInt8
│     %98  = Base.ifelse(true, %96, %97)::UInt8
│     %99  = Base.slt_int(0, 0)::Bool
│     %100 = Base.bitcast(UInt64, 0)::UInt64
│     %101 = Core.zext_int(Core.UInt64, %98)::UInt64
│     %102 = Base.ule_int(%100, %101)::Bool
│     %103 = Base.or_int(%99, %102)::Bool
└────        goto #26 if not %103
25 ── %105 = Base.sle_int(0, 3)::Bool
│     %106 = Base.bitcast(UInt64, 3)::UInt64
│     %107 = Core.zext_int(Core.UInt64, %98)::UInt64
│     %108 = Base.ule_int(%107, %106)::Bool
│     %109 = Base.and_int(%105, %108)::Bool
└────        goto #27
26 ──        nothing::Nothing
27 ┄─ %112 = φ (#25 => %109, #26 => false)::Bool
└────        goto #29 if not %112
28 ── %114 = PlayingCards.Suit::Type{PlayingCards.Suit}
│     %115 = %new(%114, %98)::PlayingCards.Suit
└────        goto #30
29 ── %117 = Base.string("invalid suit number: ", %98)::Any
│     %118 = PlayingCards.ArgumentError(%117)::Any
│            PlayingCards.throw(%118)::Union{}
└────        unreachable
30 ──        goto #31
31 ── %122 = (%84 === %115)::Bool
└────        goto #40 if not %122
32 ── %124 = Base.getfield(t, 5, false)::PlayingCards.Card
│     %125 = Base.getfield(%124, :value)::UInt8
│     %126 = Base.and_int(0x30, %125)::UInt8
│     %127 = Base.lshr_int(%126, 0x0000000000000004)::UInt8
│     %128 = Base.shl_int(%126, 0xfffffffffffffffc)::UInt8
│     %129 = Base.ifelse(true, %127, %128)::UInt8
│     %130 = Base.slt_int(0, 0)::Bool
│     %131 = Base.bitcast(UInt64, 0)::UInt64
│     %132 = Core.zext_int(Core.UInt64, %129)::UInt64
│     %133 = Base.ule_int(%131, %132)::Bool
│     %134 = Base.or_int(%130, %133)::Bool
└────        goto #34 if not %134
33 ── %136 = Base.sle_int(0, 3)::Bool
│     %137 = Base.bitcast(UInt64, 3)::UInt64
│     %138 = Core.zext_int(Core.UInt64, %129)::UInt64
│     %139 = Base.ule_int(%138, %137)::Bool
│     %140 = Base.and_int(%136, %139)::Bool
└────        goto #35
34 ──        nothing::Nothing
35 ┄─ %143 = φ (#33 => %140, #34 => false)::Bool
└────        goto #37 if not %143
36 ── %145 = PlayingCards.Suit::Type{PlayingCards.Suit}
│     %146 = %new(%145, %129)::PlayingCards.Suit
└────        goto #38
37 ── %148 = Base.string("invalid suit number: ", %129)::Any
│     %149 = PlayingCards.ArgumentError(%148)::Any
│            PlayingCards.throw(%149)::Union{}
└────        unreachable
38 ──        goto #39
39 ── %153 = (%115 === %146)::Bool
└────        goto #41
40 ──        nothing::Nothing
41 ┄─ %156 = φ (#39 => %153, #40 => false)::Bool
└────        goto #43
42 ──        nothing::Nothing
43 ┄─ %159 = φ (#41 => %156, #42 => false)::Bool
└────        goto #45
44 ──        nothing::Nothing
45 ┄─ %162 = φ (#43 => %159, #44 => false)::Bool
└────        goto #100 if not %162
46 ── %164 = Core.getfield(t, 1)::PlayingCards.Card
│     %165 = Core.getfield(t, 2)::PlayingCards.Card
│     %166 = Core.getfield(t, 3)::PlayingCards.Card
│     %167 = Core.getfield(t, 4)::PlayingCards.Card
│     %168 = Core.getfield(t, 5)::PlayingCards.Card
│     %169 = Base.getfield(%164, :value)::UInt8
│     %170 = Base.and_int(%169, 0x0f)::UInt8
│     %171 = Core.lshr_int(%170, 7)::UInt8
│     %172 = Core.eq_int(%171, 0x01)::Bool
└────        goto #48 if not %172
47 ──        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %170::UInt8)::Union{}
└────        unreachable
48 ──        goto #49
49 ── %177 = Core.bitcast(Core.Int8, %170)::Int8
└────        goto #50
50 ──        goto #51
51 ──        goto #52
52 ── %181 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %182 = Core.sext_int(Core.Int64, %177)::Int64
│     %183 = Base.getfield(%181, %182, true)::Int64
└────        goto #53
53 ──        goto #54
54 ── %186 = Base.getfield(%165, :value)::UInt8
│     %187 = Base.and_int(%186, 0x0f)::UInt8
│     %188 = Core.lshr_int(%187, 7)::UInt8
│     %189 = Core.eq_int(%188, 0x01)::Bool
└────        goto #56 if not %189
55 ──        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %187::UInt8)::Union{}
└────        unreachable
56 ──        goto #57
57 ── %194 = Core.bitcast(Core.Int8, %187)::Int8
└────        goto #58
58 ──        goto #59
59 ──        goto #60
60 ── %198 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %199 = Core.sext_int(Core.Int64, %194)::Int64
│     %200 = Base.getfield(%198, %199, true)::Int64
└────        goto #61
61 ── %202 = Base.mul_int(%183, %200)::Int64
└────        goto #62
62 ── %204 = Base.getfield(%166, :value)::UInt8
│     %205 = Base.and_int(%204, 0x0f)::UInt8
│     %206 = Core.lshr_int(%205, 7)::UInt8
│     %207 = Core.eq_int(%206, 0x01)::Bool
└────        goto #64 if not %207
63 ──        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %205::UInt8)::Union{}
└────        unreachable
64 ──        goto #65
65 ── %212 = Core.bitcast(Core.Int8, %205)::Int8
└────        goto #66
66 ──        goto #67
67 ──        goto #68
68 ── %216 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %217 = Core.sext_int(Core.Int64, %212)::Int64
│     %218 = Base.getfield(%216, %217, true)::Int64
└────        goto #69
69 ── %220 = Base.mul_int(%202, %218)::Int64
└────        goto #70
70 ── %222 = Base.getfield(%167, :value)::UInt8
│     %223 = Base.and_int(%222, 0x0f)::UInt8
│     %224 = Core.lshr_int(%223, 7)::UInt8
│     %225 = Core.eq_int(%224, 0x01)::Bool
└────        goto #72 if not %225
71 ──        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %223::UInt8)::Union{}
└────        unreachable
72 ──        goto #73
73 ── %230 = Core.bitcast(Core.Int8, %223)::Int8
└────        goto #74
74 ──        goto #75
75 ──        goto #76
76 ── %234 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %235 = Core.sext_int(Core.Int64, %230)::Int64
│     %236 = Base.getfield(%234, %235, true)::Int64
└────        goto #77
77 ── %238 = Base.mul_int(%220, %236)::Int64
└────        goto #78
78 ── %240 = Base.getfield(%168, :value)::UInt8
│     %241 = Base.and_int(%240, 0x0f)::UInt8
│     %242 = Core.lshr_int(%241, 7)::UInt8
│     %243 = Core.eq_int(%242, 0x01)::Bool
└────        goto #80 if not %243
79 ──        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %241::UInt8)::Union{}
└────        unreachable
80 ──        goto #81
81 ── %248 = Core.bitcast(Core.Int8, %241)::Int8
└────        goto #82
82 ──        goto #83
83 ──        goto #84
84 ── %252 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %253 = Core.sext_int(Core.Int64, %248)::Int64
│     %254 = Base.getfield(%252, %253, true)::Int64
└────        goto #85
85 ── %256 = Base.mul_int(%238, %254)::Int64
└────        goto #86
86 ──        goto #87
87 ──        goto #88
88 ──        goto #89
89 ──        goto #90
90 ──        goto #91
91 ──        goto #92
92 ──        goto #93
93 ──        goto #94
94 ──        goto #95
95 ──        goto #96
96 ── %268 = PokerHandEvaluator.hash_table_suited::Core.Const(Dict(134589 => 1449, 145299 => 1121, 2055781 => 642, 78474 => 686, 211497 => 1446, 71630 => 1432, 933317 => 1254, 135546 => 655, 3159985 => 827, 1829465 => 842…))
│     %269 = invoke Base.ht_keyindex(%268::Dict{Int64, Int64}, %256::Int64)::Int64
│     %270 = Base.slt_int(%269, 0)::Bool
└────        goto #98 if not %270
97 ── %272 = Base.KeyError(%256)::Any
│            Base.throw(%272)::Union{}
└────        unreachable
98 ── %275 = Base.getfield(%268, :vals)::Vector{Int64}
│     %276 = Base.arrayref(false, %275, %269)::Int64
└────        goto #99
99 ──        return %276
100 ─ %279 = Core.getfield(t, 1)::PlayingCards.Card
│     %280 = Core.getfield(t, 2)::PlayingCards.Card
│     %281 = Core.getfield(t, 3)::PlayingCards.Card
│     %282 = Core.getfield(t, 4)::PlayingCards.Card
│     %283 = Core.getfield(t, 5)::PlayingCards.Card
│     %284 = Base.getfield(%279, :value)::UInt8
│     %285 = Base.and_int(%284, 0x0f)::UInt8
│     %286 = Core.lshr_int(%285, 7)::UInt8
│     %287 = Core.eq_int(%286, 0x01)::Bool
└────        goto #102 if not %287
101 ─        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %285::UInt8)::Union{}
└────        unreachable
102 ─        goto #103
103 ─ %292 = Core.bitcast(Core.Int8, %285)::Int8
└────        goto #104
104 ─        goto #105
105 ─        goto #106
106 ─ %296 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %297 = Core.sext_int(Core.Int64, %292)::Int64
│     %298 = Base.getfield(%296, %297, true)::Int64
└────        goto #107
107 ─        goto #108
108 ─ %301 = Base.getfield(%280, :value)::UInt8
│     %302 = Base.and_int(%301, 0x0f)::UInt8
│     %303 = Core.lshr_int(%302, 7)::UInt8
│     %304 = Core.eq_int(%303, 0x01)::Bool
└────        goto #110 if not %304
109 ─        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %302::UInt8)::Union{}
└────        unreachable
110 ─        goto #111
111 ─ %309 = Core.bitcast(Core.Int8, %302)::Int8
└────        goto #112
112 ─        goto #113
113 ─        goto #114
114 ─ %313 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %314 = Core.sext_int(Core.Int64, %309)::Int64
│     %315 = Base.getfield(%313, %314, true)::Int64
└────        goto #115
115 ─ %317 = Base.mul_int(%298, %315)::Int64
└────        goto #116
116 ─ %319 = Base.getfield(%281, :value)::UInt8
│     %320 = Base.and_int(%319, 0x0f)::UInt8
│     %321 = Core.lshr_int(%320, 7)::UInt8
│     %322 = Core.eq_int(%321, 0x01)::Bool
└────        goto #118 if not %322
117 ─        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %320::UInt8)::Union{}
└────        unreachable
118 ─        goto #119
119 ─ %327 = Core.bitcast(Core.Int8, %320)::Int8
└────        goto #120
120 ─        goto #121
121 ─        goto #122
122 ─ %331 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %332 = Core.sext_int(Core.Int64, %327)::Int64
│     %333 = Base.getfield(%331, %332, true)::Int64
└────        goto #123
123 ─ %335 = Base.mul_int(%317, %333)::Int64
└────        goto #124
124 ─ %337 = Base.getfield(%282, :value)::UInt8
│     %338 = Base.and_int(%337, 0x0f)::UInt8
│     %339 = Core.lshr_int(%338, 7)::UInt8
│     %340 = Core.eq_int(%339, 0x01)::Bool
└────        goto #126 if not %340
125 ─        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %338::UInt8)::Union{}
└────        unreachable
126 ─        goto #127
127 ─ %345 = Core.bitcast(Core.Int8, %338)::Int8
└────        goto #128
128 ─        goto #129
129 ─        goto #130
130 ─ %349 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %350 = Core.sext_int(Core.Int64, %345)::Int64
│     %351 = Base.getfield(%349, %350, true)::Int64
└────        goto #131
131 ─ %353 = Base.mul_int(%335, %351)::Int64
└────        goto #132
132 ─ %355 = Base.getfield(%283, :value)::UInt8
│     %356 = Base.and_int(%355, 0x0f)::UInt8
│     %357 = Core.lshr_int(%356, 7)::UInt8
│     %358 = Core.eq_int(%357, 0x01)::Bool
└────        goto #134 if not %358
133 ─        invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int8::Type{Int8}, %356::UInt8)::Union{}
└────        unreachable
134 ─        goto #135
135 ─ %363 = Core.bitcast(Core.Int8, %356)::Int8
└────        goto #136
136 ─        goto #137
137 ─        goto #138
138 ─ %367 = PokerHandEvaluator.HandCombinations.primes::NTuple{13, Int64}
│     %368 = Core.sext_int(Core.Int64, %363)::Int64
│     %369 = Base.getfield(%367, %368, true)::Int64
└────        goto #139
139 ─ %371 = Base.mul_int(%353, %369)::Int64
└────        goto #140
140 ─        goto #141
141 ─        goto #142
142 ─        goto #143
143 ─        goto #144
144 ─        goto #145
145 ─        goto #146
146 ─        goto #147
147 ─        goto #148
148 ─        goto #149
149 ─        goto #150
150 ─ %383 = PokerHandEvaluator.hash_table_offsuit::Core.Const(Dict(134589 => 7312, 2055781 => 6505, 78474 => 6549, 9035849 => 4306, 836349 => 4325, 71630 => 7295, 397010 => 3680, 417074 => 1804, 70805 => 3125, 5142179 => 3465…))
│     %384 = invoke Base.ht_keyindex(%383::Dict{Int64, Int64}, %371::Int64)::Int64
│     %385 = Base.slt_int(%384, 0)::Bool
└────        goto #152 if not %385
151 ─ %387 = Base.KeyError(%371)::Any
│            Base.throw(%387)::Union{}
└────        unreachable
152 ─ %390 = Base.getfield(%383, :vals)::Vector{Int64}
│     %391 = Base.arrayref(false, %390, %384)::Int64
└────        goto #153
153 ─        return %391
) => Int64

Benchmark

There is a perf.jl file at the top level of the repo which estimates PokerHandEvaluator.jl's performance, here's how it can be run:

Note

This perf.jl file needs some additional packages (StatsBase.jl, BenchmarkTools.jl, and Combinatorics.jl) that are not shipped with PokerHandEvaluator.jl

using PokerHandEvaluator
@time include(joinpath(pkgdir(PokerHandEvaluator), "perf.jl"))
Δt_per_evaluate5 = 2.1683430295195003e-8
*******5-card hand evaluation benchmark*******
BenchmarkTools.Trial: 10000 samples with 178 evaluations.
 Range (min … max):  595.511 ns …   9.176 μs  ┊ GC (min … max): 0.00% … 92.81%
 Time  (median):     616.854 ns               ┊ GC (median):    0.00%
 Time  (mean ± σ):   648.499 ns ± 372.470 ns  ┊ GC (mean ± σ):  2.70% ±  4.39%

   ▄▆██▆▄▂▂▂▁    ▁                                 ▁         ▁▁ ▂
  █████████████████████▇▆▆▆▄▅▄▄▃▁▃▁▃▁▄▁▁▁▄▃▁▁▁▃▅▇▇▇███▆▅▆▆▆▇███ █
  596 ns        Histogram: log(frequency) by time        907 ns <

 Memory estimate: 608 bytes, allocs estimate: 8.
*******7-card hand evaluation benchmark*******
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
 Range (min … max):  1.130 μs … 183.621 μs  ┊ GC (min … max): 0.00% … 98.75%
 Time  (median):     1.440 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.478 μs ±   1.832 μs  ┊ GC (mean ± σ):  1.23% ±  0.99%

         ▆ ▇         ▅▁▁█ ▄         ▁
  ▁▁▂▂▆▅▆███▆▅▇▃▄▂▂▇▅██████▅▄▅▃▅▃▆▅▅█▅█▄▄▆▃▃▂▂▃▂▃▂▂▃▂▂▁▁▂▁▂▁▁ ▃
  1.13 μs         Histogram: frequency by time        1.96 μs <

 Memory estimate: 640 bytes, allocs estimate: 10.
  9.880839 seconds (59.99 M allocations: 2.974 GiB, 21.86% gc time)