Skip to content

Commit ba3e70e

Browse files
committed
Add performance measurement with instructions count
1 parent c808498 commit ba3e70e

7 files changed

+115
-35
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ Package.resolved
1818

1919
.DS_Store
2020
*.pyc
21+
22+
Tests/PerformanceTest/baselines.json

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ let package = Package(
283283

284284
.testTarget(
285285
name: "PerformanceTest",
286-
dependencies: ["SwiftIDEUtils", "SwiftParser", "SwiftSyntax"],
287-
exclude: ["Inputs"]
286+
dependencies: ["_InstructionCounter", "SwiftIDEUtils", "SwiftParser", "SwiftSyntax"],
287+
exclude: ["Inputs", "ci-baselines.json"]
288288
),
289289
]
290290
)

Tests/PerformanceTest/ParsingPerformanceTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class ParsingPerformanceTests: XCTestCase {
2525

2626
func testNativeParsingPerformance() throws {
2727
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
28-
measure {
28+
try measureInstructions {
2929
do {
3030
let source = try String(contentsOf: inputFile)
3131
_ = Parser.parse(source: source)

Tests/PerformanceTest/SyntaxClassifierPerformanceTests.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,16 @@ public class SyntaxClassifierPerformanceTests: XCTestCase {
2626

2727
func testClassifierPerformance() throws {
2828
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
29-
XCTAssertNoThrow(
30-
try {
29+
try measureInstructions {
30+
do {
3131
let source = try String(contentsOf: inputFile)
3232
let parsed = Parser.parse(source: source)
33-
34-
measure {
35-
for _ in 0..<10 {
36-
for _ in parsed.classifications {}
37-
}
33+
for _ in 0..<10 {
34+
for _ in parsed.classifications {}
3835
}
39-
}()
40-
)
36+
} catch {
37+
XCTFail(error.localizedDescription)
38+
}
39+
}
4140
}
4241
}

Tests/PerformanceTest/VisitorPerformanceTests.swift

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,50 +27,48 @@ public class VisitorPerformanceTests: XCTestCase {
2727
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
2828
class EmptyVisitor: SyntaxVisitor {}
2929

30-
XCTAssertNoThrow(
31-
try {
30+
try measureInstructions {
31+
do {
3232
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
3333

3434
let emptyVisitor = EmptyVisitor(viewMode: .sourceAccurate)
35-
36-
measure {
37-
emptyVisitor.walk(parsed)
38-
}
39-
}()
40-
)
35+
emptyVisitor.walk(parsed)
36+
} catch {
37+
XCTFail(error.localizedDescription)
38+
}
39+
}
4140
}
4241

4342
func testEmptyRewriterPerformance() throws {
4443
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
4544
class EmptyRewriter: SyntaxRewriter {}
4645

47-
XCTAssertNoThrow(
48-
try {
46+
try measureInstructions {
47+
do {
4948
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
5049

5150
let emptyRewriter = EmptyRewriter(viewMode: .sourceAccurate)
52-
53-
measure {
54-
_ = emptyRewriter.visit(parsed)
55-
}
56-
}()
57-
)
51+
_ = emptyRewriter.visit(parsed)
52+
} catch {
53+
XCTFail(error.localizedDescription)
54+
}
55+
}
5856
}
5957

6058
func testEmptyAnyVisitorPerformance() throws {
6159
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
6260
class EmptyAnyVisitor: SyntaxAnyVisitor {}
6361

64-
XCTAssertNoThrow(
65-
try {
62+
try measureInstructions {
63+
do {
6664
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
6765

6866
let emptyVisitor = EmptyAnyVisitor(viewMode: .sourceAccurate)
6967

70-
measure {
71-
emptyVisitor.walk(parsed)
72-
}
73-
}()
74-
)
68+
emptyVisitor.walk(parsed)
69+
} catch {
70+
XCTFail(error.localizedDescription)
71+
}
72+
}
7573
}
7674
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import XCTest
14+
import _InstructionCounter
15+
16+
extension XCTestCase {
17+
private var baselineURL: URL {
18+
if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == "1" {
19+
return URL(fileURLWithPath: #file)
20+
.deletingLastPathComponent()
21+
.appendingPathComponent("ci-baselines.json")
22+
} else {
23+
return URL(fileURLWithPath: #file)
24+
.deletingLastPathComponent()
25+
.appendingPathComponent("baselines.json")
26+
}
27+
}
28+
29+
func measureInstructions(_ baselineName: StaticString = #function, block: () -> Void, file: StaticString = #file, line: UInt = #line) throws {
30+
let strippedBaselineName = "\(baselineName)".replacingOccurrences(of: "()", with: "")
31+
var baseline: UInt64? = try baseline(for: strippedBaselineName)
32+
33+
if let environmentBaseline = ProcessInfo.processInfo.environment[strippedBaselineName] {
34+
baseline = UInt64(environmentBaseline)
35+
}
36+
37+
guard let baseline = baseline else {
38+
XCTFail("Missing baseline for \(strippedBaselineName)", file: file, line: line)
39+
return
40+
}
41+
42+
let startInstructions = getInstructionsExecuted()
43+
block()
44+
let endInstructions = getInstructionsExecuted()
45+
let numberOfInstructions = endInstructions - startInstructions
46+
47+
let lowerBaseline = UInt64(Double(baseline) * 0.97)
48+
let upperBaseline = UInt64(Double(baseline) * 1.03)
49+
let baselineRange = lowerBaseline...upperBaseline
50+
51+
XCTAssertTrue(
52+
baselineRange.contains(numberOfInstructions),
53+
"""
54+
Number of instructions '\(numberOfInstructions)'
55+
is not within range \(lowerBaseline)-\(upperBaseline)
56+
""",
57+
file: file,
58+
line: line
59+
)
60+
}
61+
62+
private func baseline(for key: String) throws -> UInt64? {
63+
let baselineData = try? Data(contentsOf: baselineURL)
64+
65+
guard let data = baselineData else {
66+
fatalError("No baseline.json provided")
67+
}
68+
69+
let jsonDecoder = JSONDecoder()
70+
let baselineMap = try jsonDecoder.decode([String: UInt64].self, from: data)
71+
72+
return baselineMap[key]
73+
}
74+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"testNativeParsingPerformance": 5850081534,
3+
"testClassifierPerformance": 27177168966,
4+
"testEmptyVisitorPerformance": 6641646112,
5+
"testEmptyRewriterPerformance": 6641646112,
6+
"testEmptyAnyVisitorPerformance": 6711819049
7+
}

0 commit comments

Comments
 (0)