Skip to content

Commit 8101f55

Browse files
committed
Add metrics + update tracing
1 parent 8cdc76e commit 8101f55

File tree

6 files changed

+109
-25
lines changed

6 files changed

+109
-25
lines changed

Sources/_StringProcessing/Engine/Instruction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct Instruction: RawRepresentable, Hashable {
2222
}
2323

2424
extension Instruction {
25-
enum OpCode: UInt64 {
25+
enum OpCode: UInt64, CaseIterable {
2626
case invalid = 0
2727

2828
// MARK: - General Purpose
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
extension Processor {
2+
struct ProcessorMetrics {
3+
var instructionCounts: [Int] = .init(repeating: 0, count: Instruction.OpCode.allCases.count)
4+
var caseInsensitiveInstrs: Bool = false
5+
}
6+
7+
func printMetrics() {
8+
// print("Total cycle count: \(cycleCount)")
9+
// print("Instructions:")
10+
let sorted = metrics.instructionCounts.enumerated()
11+
.filter({$0.1 != 0})
12+
.sorted(by: { (a,b) in a.1 > b.1 })
13+
for (opcode, count) in sorted {
14+
print("\(Instruction.OpCode.init(rawValue: UInt64(opcode))!),\(count)")
15+
}
16+
}
17+
18+
mutating func measureMetrics() {
19+
if shouldMeasureMetrics {
20+
let (opcode, _) = fetch().destructure
21+
metrics.instructionCounts[Int(opcode.rawValue)] += 1
22+
}
23+
}
24+
}

Sources/_StringProcessing/Engine/Processor.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ struct Processor {
8989
// MARK: Metrics, debugging, etc.
9090
var cycleCount = 0
9191
var isTracingEnabled: Bool
92+
let shouldMeasureMetrics: Bool = true
93+
var metrics: ProcessorMetrics = ProcessorMetrics()
9294
}
9395

9496
extension Processor {
@@ -397,6 +399,7 @@ extension Processor {
397399
defer {
398400
cycleCount += 1
399401
trace()
402+
measureMetrics()
400403
_checkInvariants()
401404
}
402405
let (opcode, payload) = fetch().destructure

Sources/_StringProcessing/Engine/Tracing.swift

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,81 @@ extension Processor: TracedProcessor {
2929

3030
extension Instruction: CustomStringConvertible {
3131
var description: String {
32-
// TODO: opcode specific rendering
33-
"\(opcode) \(payload)"
34-
}
35-
}
36-
37-
extension Instruction.Payload: CustomStringConvertible {
38-
var description: String {
39-
// var result = ""
40-
// if hasCondition {
41-
// result += "\(condition) "
42-
// }
43-
// if hasPayload {
44-
// let payload: TypedInt<_Boo> = payload()
45-
// result += payload.description
46-
// }
47-
// return result
48-
49-
// TODO: Without bit packing our representation, what
50-
// should we do? I'd say a payload cannot be printed
51-
// in isolation of the instruction...
52-
return "\(rawValue)"
32+
switch opcode {
33+
case .advance:
34+
return "\(opcode) \(payload.distance)"
35+
case .assertBy:
36+
return "\(opcode) \(payload.assertion)"
37+
case .backreference:
38+
return "\(opcode) \(payload.capture.rawValue)"
39+
case .beginCapture:
40+
return "\(opcode) \(payload.capture.rawValue)"
41+
case .branch:
42+
return "\(opcode) \(payload.addr)"
43+
case .captureValue:
44+
let (val, cap) = payload.pairedValueCapture
45+
return "\(opcode) vals[\(val)] -> captures[\(cap)]"
46+
case .condBranchSamePosition:
47+
let (addr, pos) = payload.pairedAddrPos
48+
return "\(opcode) \(addr) pos[\(pos)]"
49+
case .condBranchZeroElseDecrement:
50+
let (addr, int) = payload.pairedAddrInt
51+
return "\(opcode) \(addr) int[\(int)]"
52+
case .consumeBy:
53+
return "\(opcode) consumer[\(payload.consumer)]"
54+
case .endCapture:
55+
return "\(opcode) \(payload.capture.rawValue)"
56+
case .match:
57+
let (isCaseInsensitive, reg) = payload.elementPayload
58+
if isCaseInsensitive {
59+
return "matchCaseInsensitive char[\(reg)]"
60+
} else {
61+
return "match char[\(reg)]"
62+
}
63+
case .matchBitset:
64+
let (isScalar, reg) = payload.bitsetPayload
65+
if isScalar {
66+
return "matchBitsetScalar bitset[\(reg)]"
67+
} else {
68+
return "matchBitset bitset[\(reg)]"
69+
}
70+
case .matchBuiltin:
71+
let payload = payload.characterClassPayload
72+
return "matchBuiltin \(payload.cc) (\(payload.isInverted))"
73+
case .matchBy:
74+
let (matcherReg, valReg) = payload.pairedMatcherValue
75+
return "\(opcode) match[\(matcherReg)] -> val[\(valReg)]"
76+
case .matchScalar:
77+
let (scalar, caseInsensitive, boundaryCheck) = payload.scalarPayload
78+
if caseInsensitive {
79+
return "matchScalarCaseInsensitive \(scalar) boundaryCheck: \(boundaryCheck)"
80+
} else {
81+
return "matchScalar \(scalar) boundaryCheck: \(boundaryCheck)"
82+
}
83+
case .moveCurrentPosition:
84+
let reg = payload.position
85+
return "\(opcode) -> pos[\(reg)]"
86+
case .moveImmediate:
87+
let (imm, reg) = payload.pairedImmediateInt
88+
return "\(opcode) \(imm) -> int[\(reg)]"
89+
case .quantify:
90+
let payload = payload.quantify
91+
return "\(opcode) \(payload.type) \(payload.minTrips) \(payload.extraTrips?.description ?? "unbounded" )"
92+
case .save:
93+
let resumeAddr = payload.addr
94+
return "\(opcode) \(resumeAddr)"
95+
case .saveAddress:
96+
let resumeAddr = payload.addr
97+
return "\(opcode) \(resumeAddr)"
98+
case .splitSaving:
99+
let (nextPC, resumeAddr) = payload.pairedAddrAddr
100+
return "\(opcode) saving: \(resumeAddr) jumpingTo: \(nextPC)"
101+
case .transformCapture:
102+
let (cap, trans) = payload.pairedCaptureTransform
103+
return "\(opcode) trans[\(trans)](\(cap))"
104+
default:
105+
return "\(opcode)"
106+
}
53107
}
54108
}
55109

@@ -62,7 +116,9 @@ extension Processor.SavePoint {
62116
if rangeIsEmpty {
63117
posStr = "<none>"
64118
} else {
65-
posStr = "\(rangeStart!...rangeEnd!)"
119+
let startStr = "\(input.distance(from: input.startIndex, to: rangeStart!))"
120+
let endStr = "\(input.distance(from: input.startIndex, to: rangeEnd!))"
121+
posStr = "\(startStr)...\(endStr)"
66122
}
67123
}
68124
return """

Sources/_StringProcessing/Executor.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ struct Executor {
3030
input: input,
3131
subjectBounds: subjectBounds,
3232
searchBounds: searchBounds)
33-
33+
defer { if cpu.shouldMeasureMetrics { cpu.printMetrics() } }
3434
var low = searchBounds.lowerBound
3535
let high = searchBounds.upperBound
3636
while true {
@@ -57,6 +57,7 @@ struct Executor {
5757
) throws -> Regex<Output>.Match? {
5858
var cpu = engine.makeProcessor(
5959
input: input, bounds: subjectBounds, matchMode: mode)
60+
defer { if cpu.shouldMeasureMetrics { cpu.printMetrics() } }
6061
return try _match(input, from: subjectBounds.lowerBound, using: &cpu)
6162
}
6263

Sources/_StringProcessing/Utility/Traced.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ extension TracedProcessor {
144144
result += formatCallStack()
145145
result += formatSavePoints()
146146
result += formatRegisters()
147-
result += formatInput()
147+
// result += formatInput()
148148
result += "\n"
149149
result += formatInstructionWindow()
150150
return result

0 commit comments

Comments
 (0)