-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprofiling.go
More file actions
153 lines (136 loc) · 5.1 KB
/
Copy pathprofiling.go
File metadata and controls
153 lines (136 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main
import (
"fmt"
"runtime"
"time"
chess "github.com/TheUtkarsh8939/bitboardChess"
)
// Sink variables make benchmark results observable so the compiler cannot optimize calls away.
var (
benchIntSink int
benchUint64Sink uint64
benchBoolSink bool
benchPosSink *chess.Position
benchErrSink error
)
// cpuPercent converts CPU-time delta over wall-time delta into a 0..100 percentage.
func cpuPercent(cpuDeltaSeconds float64, elapsed time.Duration) float64 {
elapsedSeconds := elapsed.Seconds()
if elapsedSeconds <= 0 {
return 0
}
procs := float64(runtime.GOMAXPROCS(0))
if procs <= 0 {
procs = 1
}
percent := (cpuDeltaSeconds / elapsedSeconds) * 100.0 / procs
if percent < 0 {
return 0
}
if percent > 100 {
return 100
}
return percent
}
// runBenchmark executes fn exactly calls times and prints elapsed and CPU usage.
func runBenchmark(functionName string, calls int, fn func(iter int)) {
startCPU := readCPUSeconds()
startTime := time.Now()
for i := 0; i < calls; i++ {
fn(i)
}
elapsedTime := time.Since(startTime)
endCPU := readCPUSeconds()
usedCPUPercent := cpuPercent(endCPU-startCPU, elapsedTime)
// Runtime CPU metrics can be noisy on very short runs; take a longer sample for CPU%% only.
if elapsedTime < 300*time.Millisecond {
sampleCalls := calls
sampleStartCPU := readCPUSeconds()
sampleStart := time.Now()
sampleElapsed := time.Duration(0)
for sampleElapsed < time.Second {
for i := 0; i < sampleCalls; i++ {
fn(i)
}
sampleElapsed = time.Since(sampleStart)
if sampleElapsed < time.Second {
sampleCalls *= 2
}
}
sampleEndCPU := readCPUSeconds()
usedCPUPercent = cpuPercent(sampleEndCPU-sampleStartCPU, sampleElapsed)
}
// timeInNanoseconds := elapsedTime.Nanoseconds()
fmt.Printf("%s took %vns and used %.2f percent of cpu\n\n", functionName, int(elapsedTime.Nanoseconds())/calls, usedCPUPercent)
}
// Benchmark profiles hot functions using benchmarkCalls iterations each.
func Benchmark() {
fmt.Println("Starting profiling benchmarks...")
fmt.Printf("Calls per function: %d\n\n", benchmarkCalls)
fen, _ := chess.FEN("r1bqkbnr/pppp1ppp/2n5/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 3")
game := chess.NewGame(fen)
position := game.Position()
pst := initPST()
rootHash := fastPosHash(position)
moves := position.ValidMoves()
if len(moves) == 0 {
fmt.Println("No legal moves in benchmark position; skipping profiling.")
return
}
move := &moves[0]
child := position.Update(move)
bb, _ := position.MarshalBinary()
wbb := bb[5]
bitboards, _ := position.MarshalBinary()
wbb, bbb := bitboards[5], bitboards[11]
runBenchmark("OpenFilesEval", benchmarkCalls, func(_ int) {
passedPawnPotentialWeight := 20 //Ranges from 0-20 depending on material traded
endgamePawnsWhite, endgamePawnsBlack := (wbb>>32)<<32, (bbb<<32)>>32 // Only consider pawns on the opponent's half of the board for passed-pawn potential, as central pawns are more likely to become passed and create threats.This also reduces noise during opening from pawns that are still far from promotion and unlikely to become passed for many moves.
benchIntSink += (PassedPawnPotentialScore(endgamePawnsWhite, bbb, chess.White) - PassedPawnPotentialScore(endgamePawnsBlack, wbb, chess.Black)) * passedPawnPotentialWeight
benchIntSink += kingNearOpenFiles(wbb, bitboards[0]) * 5
benchIntSink -= kingNearOpenFiles(bbb, bitboards[6]) * 5
benchIntSink += rookOnOpenFiles(wbb|bbb, bitboards[2]) * 3
benchIntSink -= rookOnOpenFiles(wbb|bbb, bitboards[8]) * 3
})
runBenchmark("MarshalBinary", benchmarkCalls, func(_ int) {
bb, _ = position.MarshalBinary()
})
runBenchmark("PawnStructure", benchmarkCalls, func(_ int) {
benchIntSink += pawnStructure(wbb)
})
runBenchmark("ValidMoves", benchmarkCalls, func(_ int) {
benchIntSink += len(position.ValidMoves())
})
runBenchmark("Update", benchmarkCalls, func(_ int) {
benchPosSink = position.Update(move)
})
runBenchmark("FastEvaluatePos", benchmarkCalls, func(_ int) {
benchIntSink += EvaluatePos(position, &pst)
})
runBenchmark("EvaluateMove", benchmarkCalls, func(_ int) {
benchIntSink += EvaluateMove(move, position, 6)
})
runBenchmark("fastPosHash", benchmarkCalls, func(_ int) {
benchUint64Sink ^= fastPosHash(position)
})
runBenchmark("fastChildHash", benchmarkCalls, func(_ int) {
benchUint64Sink ^= fastChildHash(position, child, move, rootHash)
})
runBenchmark("fastNullHash", benchmarkCalls, func(_ int) {
benchUint64Sink ^= fastNullHash(position, child, rootHash)
})
runBenchmark("ttLookup", benchmarkCalls, func(_ int) {
score, alpha, beta, ok := ttLookup(rootHash, 4, minScore, maxScore)
benchIntSink += score + alpha + beta
benchBoolSink = benchBoolSink != ok
})
runBenchmark("ttStore", benchmarkCalls, func(iter int) {
key := rootHash + uint64(iter&7)
ttStore(key, iter&1023, 4, ttBoundExact)
benchIntSink += transpositionTable[key&ttMask].score
})
runBenchmark("quiescence_search_depth4", benchmarkCalls, func(_ int) {
benchIntSink += quiescence_search(position, minScore, maxScore, true, 4, &pst, 0, nil)
})
fmt.Println("Profiling benchmarks complete.")
}