Skip to content

Commit aa49fec

Browse files
authored
Merge pull request #1863 from kimdv/kimdv/add-baseline-for-performance-tests
2 parents 2132c8b + b07bcdb commit aa49fec

8 files changed

+113
-50
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
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
fileprivate var baselineURL: URL {
17+
if let baselineFile = ProcessInfo.processInfo.environment["BASELINE_FILE"] {
18+
return URL(fileURLWithPath: baselineFile)
19+
} else {
20+
return URL(fileURLWithPath: #file)
21+
.deletingLastPathComponent()
22+
.appendingPathComponent("baselines.json")
23+
}
24+
}
25+
26+
func measureInstructions(_ baselineName: StaticString = #function, block: () -> Void, file: StaticString = #file, line: UInt = #line) throws {
27+
let startInstructions = getInstructionsExecuted()
28+
block()
29+
let endInstructions = getInstructionsExecuted()
30+
let numberOfInstructions = endInstructions - startInstructions
31+
let strippedBaselineName = "\(baselineName)".replacingOccurrences(of: "()", with: "")
32+
33+
#if os(Darwin)
34+
// If the is no data, we just continue the test
35+
guard let data = try? Data(contentsOf: baselineURL) else {
36+
return
37+
}
38+
39+
let jsonDecoder = JSONDecoder()
40+
let baselineMap = try jsonDecoder.decode([String: UInt64].self, from: data)
41+
42+
guard let baseline = baselineMap[strippedBaselineName] else {
43+
XCTFail(
44+
"""
45+
Missing baseline for \(strippedBaselineName)
46+
with number of instructions '\(numberOfInstructions)'
47+
""",
48+
file: file,
49+
line: line
50+
)
51+
return
52+
}
53+
54+
let relativeDeviation = Double(numberOfInstructions) / Double(baseline) - 1
55+
let allowedDeviation = 0.01
56+
57+
XCTAssertTrue(
58+
(-allowedDeviation..<allowedDeviation).contains(relativeDeviation),
59+
"""
60+
Number of instructions '\(numberOfInstructions)' deviated from baseline by \(String(format: "%.4f", relativeDeviation * 100))%.
61+
The maximum allowed deviation for '\(strippedBaselineName)' is \(allowedDeviation * 100)% in either direction.
62+
""",
63+
file: file,
64+
line: line
65+
)
66+
#endif
67+
}

Tests/PerformanceTest/ParsingPerformanceTests.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@ public class ParsingPerformanceTests: XCTestCase {
2525

2626
func testNativeParsingPerformance() throws {
2727
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
28-
measure {
29-
do {
30-
let source = try String(contentsOf: inputFile)
31-
_ = Parser.parse(source: source)
32-
} catch {
33-
XCTFail(error.localizedDescription)
34-
}
28+
29+
let source = try String(contentsOf: inputFile)
30+
31+
try measureInstructions {
32+
_ = Parser.parse(source: source)
3533
}
3634
}
3735
}

Tests/PerformanceTest/SyntaxClassifierPerformanceTests.swift

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

2727
func testClassifierPerformance() throws {
2828
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
29-
XCTAssertNoThrow(
30-
try {
31-
let source = try String(contentsOf: inputFile)
32-
let parsed = Parser.parse(source: source)
3329

34-
measure {
35-
for _ in 0..<10 {
36-
for _ in parsed.classifications {}
37-
}
38-
}
39-
}()
40-
)
30+
let source = try String(contentsOf: inputFile)
31+
let parsed = Parser.parse(source: source)
32+
33+
try measureInstructions {
34+
for _ in 0..<10 {
35+
for _ in parsed.classifications {}
36+
}
37+
}
4138
}
4239
}

Tests/PerformanceTest/VisitorPerformanceTests.swift

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

30-
XCTAssertNoThrow(
31-
try {
32-
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
30+
let source = try String(contentsOf: inputFile)
31+
let parsed = Parser.parse(source: source)
32+
let emptyVisitor = EmptyVisitor(viewMode: .sourceAccurate)
3333

34-
let emptyVisitor = EmptyVisitor(viewMode: .sourceAccurate)
35-
36-
measure {
37-
emptyVisitor.walk(parsed)
38-
}
39-
}()
40-
)
34+
try measureInstructions {
35+
emptyVisitor.walk(parsed)
36+
}
4137
}
4238

4339
func testEmptyRewriterPerformance() throws {
4440
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
4541
class EmptyRewriter: SyntaxRewriter {}
4642

47-
XCTAssertNoThrow(
48-
try {
49-
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
50-
51-
let emptyRewriter = EmptyRewriter(viewMode: .sourceAccurate)
43+
let source = try String(contentsOf: inputFile)
44+
let parsed = Parser.parse(source: source)
45+
let emptyRewriter = EmptyRewriter(viewMode: .sourceAccurate)
5246

53-
measure {
54-
_ = emptyRewriter.visit(parsed)
55-
}
56-
}()
57-
)
47+
try measureInstructions {
48+
_ = emptyRewriter.visit(parsed)
49+
}
5850
}
5951

6052
func testEmptyAnyVisitorPerformance() throws {
6153
try XCTSkipIf(ProcessInfo.processInfo.environment["SKIP_LONG_TESTS"] == "1")
6254
class EmptyAnyVisitor: SyntaxAnyVisitor {}
6355

64-
XCTAssertNoThrow(
65-
try {
66-
let parsed = Parser.parse(source: try String(contentsOf: inputFile))
67-
68-
let emptyVisitor = EmptyAnyVisitor(viewMode: .sourceAccurate)
56+
let source = try String(contentsOf: inputFile)
57+
let parsed = Parser.parse(source: source)
58+
let emptyVisitor = EmptyAnyVisitor(viewMode: .sourceAccurate)
6959

70-
measure {
71-
emptyVisitor.walk(parsed)
72-
}
73-
}()
74-
)
60+
try measureInstructions {
61+
emptyVisitor.walk(parsed)
62+
}
7563
}
7664
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"testNativeParsingPerformance": 307024328,
3+
"testClassifierPerformance": 2624475912,
4+
"testEmptyVisitorPerformance": 48502831,
5+
"testEmptyRewriterPerformance": 54438776,
6+
"testEmptyAnyVisitorPerformance": 49123765
7+
}

build-script.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
WORKSPACE_DIR = os.path.dirname(PACKAGE_DIR)
1717
EXAMPLES_DIR = os.path.join(PACKAGE_DIR, "Examples")
1818
SOURCES_DIR = os.path.join(PACKAGE_DIR, "Sources")
19+
TESTS_DIR = os.path.join(PACKAGE_DIR, "Tests")
1920
SWIFTIDEUTILS_DIR = os.path.join(SOURCES_DIR, "SwiftIDEUtils")
2021
SWIFTSYNTAX_DIR = os.path.join(SOURCES_DIR, "SwiftSyntax")
2122
SWIFTSYNTAX_DOCUMENTATION_DIR = \
@@ -236,6 +237,7 @@ def __build(self, package_dir: str, name: str, is_product: bool) -> None:
236237
env["SWIFTCI_USE_LOCAL_DEPS"] = "1"
237238
env["SWIFT_SYNTAX_PARSER_LIB_SEARCH_PATH"] = \
238239
os.path.join(self.toolchain, "lib", "swift", "macosx")
240+
239241
check_call(command, env=env, verbose=self.verbose)
240242

241243

@@ -463,6 +465,8 @@ def run_xctests(
463465
env["SWIFTCI_USE_LOCAL_DEPS"] = "1"
464466
env["SWIFT_SYNTAX_PARSER_LIB_SEARCH_PATH"] = \
465467
os.path.join(toolchain, "lib", "swift", "macosx")
468+
env["BASELINE_FILE"] = os.path.join(TESTS_DIR, "PerformanceTest", "ci-baselines.json")
469+
466470
check_call(swiftpm_call, env=env, verbose=verbose)
467471

468472
# -----------------------------------------------------------------------------

0 commit comments

Comments
 (0)