Skip to content

Commit e01df92

Browse files
committed
[swift-parse-test] A parser performance check utility
``` % swift-parse-test -swift-parser -lib-parse -n 20 ../swift-syntax/Sources files count: 228 total bytes: 5662399 total lines: 158428 iteration: 20 ---- parser: SwiftParser wall clock time (ms): 3848 cpu time (ms): 3843 throughput (byte/sec): 355824 throughput (line/sec): 41225 ---- parser: libParse wall clock time (ms): 296 cpu time (ms): 290 throughput (byte/sec): 4715281 throughput (line/sec): 546303 ```
1 parent 708f059 commit e01df92

File tree

9 files changed

+319
-73
lines changed

9 files changed

+319
-73
lines changed

include/swift/Bridging/ASTGen.h

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//===--- ASTGen.h -----------------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 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+
#include "swift/AST/CASTBridging.h"
14+
15+
#ifdef __cplusplus
16+
extern "C" {
17+
#endif
18+
19+
void *_Nonnull swift_ASTGen_createQueuedDiagnostics();
20+
void swift_ASTGen_destroyQueuedDiagnostics(void *_Nonnull queued);
21+
void swift_ASTGen_addQueuedSourceFile(
22+
void *_Nonnull queuedDiagnostics, ssize_t bufferID,
23+
void *_Nonnull sourceFile, const uint8_t *_Nonnull displayNamePtr,
24+
intptr_t displayNameLength, ssize_t parentID, ssize_t positionInParent);
25+
void swift_ASTGen_addQueuedDiagnostic(
26+
void *_Nonnull queued, const char *_Nonnull text, ptrdiff_t textLength,
27+
BridgedDiagnosticSeverity severity, const void *_Nullable sourceLoc,
28+
const void *_Nullable *_Nullable highlightRanges,
29+
ptrdiff_t numHighlightRanges);
30+
void swift_ASTGen_renderQueuedDiagnostics(
31+
void *_Nonnull queued, ssize_t contextSize, ssize_t colorize,
32+
BridgedString *_Nonnull renderedString);
33+
34+
// FIXME: Hack because we cannot easily get to the already-parsed source
35+
// file from here. Fix this egregious oversight!
36+
void *_Nullable swift_ASTGen_parseSourceFile(const char *_Nonnull buffer,
37+
size_t bufferLength,
38+
const char *_Nonnull moduleName,
39+
const char *_Nonnull filename,
40+
void *_Nullable ctx);
41+
void swift_ASTGen_destroySourceFile(void *_Nonnull sourceFile);
42+
43+
void *_Nullable swift_ASTGen_resolveMacroType(const void *_Nonnull macroType);
44+
void swift_ASTGen_destroyMacro(void *_Nonnull macro);
45+
46+
void swift_ASTGen_freeBridgedString(BridgedString);
47+
48+
void *_Nonnull swift_ASTGen_resolveExecutableMacro(
49+
const char *_Nonnull moduleName, const char *_Nonnull typeName,
50+
void *_Nonnull opaquePluginHandle);
51+
void swift_ASTGen_destroyExecutableMacro(void *_Nonnull macro);
52+
53+
ptrdiff_t swift_ASTGen_checkMacroDefinition(
54+
void *_Nonnull diagEngine, void *_Nonnull sourceFile,
55+
const void *_Nonnull macroSourceLocation,
56+
BridgedString *_Nonnull expansionSourceOutPtr,
57+
ptrdiff_t *_Nullable *_Nonnull replacementsPtr,
58+
ptrdiff_t *_Nonnull numReplacements);
59+
void swift_ASTGen_freeExpansionReplacements(
60+
ptrdiff_t *_Nullable replacementsPtr, ptrdiff_t numReplacements);
61+
62+
ptrdiff_t swift_ASTGen_expandFreestandingMacro(
63+
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
64+
const char *_Nonnull discriminator, uint8_t rawMacroRole,
65+
void *_Nonnull sourceFile, const void *_Nullable sourceLocation,
66+
BridgedString *_Nonnull evaluatedSourceOut);
67+
68+
ptrdiff_t swift_ASTGen_expandAttachedMacro(
69+
void *_Nonnull diagEngine, const void *_Nonnull macro, uint8_t externalKind,
70+
const char *_Nonnull discriminator, const char *_Nonnull qualifiedType,
71+
const char *_Nonnull conformances, uint8_t rawMacroRole,
72+
void *_Nonnull customAttrSourceFile,
73+
const void *_Nullable customAttrSourceLocation,
74+
void *_Nonnull declarationSourceFile,
75+
const void *_Nullable declarationSourceLocation,
76+
void *_Nullable parentDeclSourceFile,
77+
const void *_Nullable parentDeclSourceLocation,
78+
BridgedString *_Nonnull evaluatedSourceOut);
79+
80+
bool swift_ASTGen_initializePlugin(void *_Nonnull handle,
81+
void *_Nullable diagEngine);
82+
void swift_ASTGen_deinitializePlugin(void *_Nonnull handle);
83+
bool swift_ASTGen_pluginServerLoadLibraryPlugin(
84+
void *_Nonnull handle, const char *_Nonnull libraryPath,
85+
const char *_Nonnull moduleName, BridgedString *_Nullable errorOut);
86+
87+
#ifdef __cplusplus
88+
}
89+
#endif

include/swift/Driver/Driver.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ class Driver {
178178
SymbolGraph, // swift-symbolgraph
179179
APIExtract, // swift-api-extract
180180
APIDigester, // swift-api-digester
181-
CacheTool // swift-cache-tool
181+
CacheTool, // swift-cache-tool
182+
ParseTest, // swift-parse-test
182183
};
183184

184185
class InputInfoMap;

lib/Driver/Driver.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
112112
.Case("swift-api-extract", DriverKind::APIExtract)
113113
.Case("swift-api-digester", DriverKind::APIDigester)
114114
.Case("swift-cache-tool", DriverKind::CacheTool)
115+
.Case("swift-parse-test", DriverKind::ParseTest)
115116
.Default(llvm::None);
116117

117118
if (Kind.has_value())
@@ -3577,6 +3578,7 @@ void Driver::printHelp(bool ShowHidden) const {
35773578
case DriverKind::APIExtract:
35783579
case DriverKind::APIDigester:
35793580
case DriverKind::CacheTool:
3581+
case DriverKind::ParseTest:
35803582
ExcludedFlagsBitmask |= options::NoBatchOption;
35813583
break;
35823584
}

lib/DriverTool/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ set(driver_sources_and_options
1313
swift_cache_tool_main.cpp
1414
swift_indent_main.cpp
1515
swift_symbolgraph_extract_main.cpp
16-
swift_api_extract_main.cpp)
16+
swift_api_extract_main.cpp
17+
swift_parse_test_main.cpp)
1718

1819
set(driver_common_libs
1920
swiftAPIDigester

lib/DriverTool/driver.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ extern int swift_api_extract_main(ArrayRef<const char *> Args,
111111
extern int swift_cache_tool_main(ArrayRef<const char *> Args, const char *Argv0,
112112
void *MainAddr);
113113

114+
/// Run 'swift-parse-test'
115+
extern int swift_parse_test_main(ArrayRef<const char *> Args, const char *Argv0,
116+
void *MainAddr);
117+
114118
/// Determine if the given invocation should run as a "subcommand".
115119
///
116120
/// Examples of "subcommands" are 'swift build' or 'swift test', which are
@@ -379,6 +383,9 @@ static int run_driver(StringRef ExecName,
379383
return swift_cache_tool_main(
380384
TheDriver.getArgsWithoutProgramNameAndDriverMode(argv), argv[0],
381385
(void *)(intptr_t)getExecutablePath);
386+
case Driver::DriverKind::ParseTest:
387+
return swift_parse_test_main(argv, argv[0],
388+
(void *)(intptr_t)getExecutablePath);
382389
default:
383390
break;
384391
}
+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
//===--- swift_parse_test_main.cpp ----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 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+
// A utility tool to measure the parser performance.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/AST/ASTContext.h"
18+
#include "swift/Basic/LLVM.h"
19+
#include "swift/Basic/LangOptions.h"
20+
#include "swift/Bridging/ASTGen.h"
21+
#include "swift/Parse/Parser.h"
22+
#include "llvm/Support/CommandLine.h"
23+
24+
#include <chrono>
25+
#include <ctime>
26+
#include <numeric>
27+
28+
using namespace swift;
29+
30+
namespace {
31+
32+
struct SwiftParseTestOptions {
33+
llvm::cl::opt<bool> SwiftParser =
34+
llvm::cl::opt<bool>("swift-parser", llvm::cl::desc("Use SwiftParser"));
35+
36+
llvm::cl::opt<bool> LibParse =
37+
llvm::cl::opt<bool>("lib-parse", llvm::cl::desc("Use libParse"));
38+
39+
llvm::cl::opt<unsigned int> Iteration = llvm::cl::opt<unsigned int>(
40+
"n", llvm::cl::desc("iteration"), llvm::cl::init(1));
41+
42+
llvm::cl::list<std::string> InputPaths = llvm::cl::list<std::string>(
43+
llvm::cl::Positional, llvm::cl::desc("input paths"));
44+
};
45+
46+
enum class ParseMode {
47+
SwiftParser,
48+
LibParse,
49+
};
50+
51+
struct LibParseExecutor {
52+
constexpr static StringRef name = "libParse";
53+
54+
static void performParse(llvm::MemoryBufferRef buffer) {
55+
SourceManager SM;
56+
unsigned bufferID =
57+
SM.addNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(buffer));
58+
DiagnosticEngine diagEngine(SM);
59+
LangOptions langOpts;
60+
TypeCheckerOptions typeckOpts;
61+
SILOptions silOpts;
62+
SearchPathOptions searchPathOpts;
63+
ClangImporterOptions clangOpts;
64+
symbolgraphgen::SymbolGraphOptions symbolOpts;
65+
std::unique_ptr<ASTContext> ctx(
66+
ASTContext::get(langOpts, typeckOpts, silOpts, searchPathOpts,
67+
clangOpts, symbolOpts, SM, diagEngine));
68+
69+
SourceFile::ParsingOptions parseOpts;
70+
// Always disable body skipping because SwiftParser currently don't have it.
71+
// When SwiftParser implements delayed parsing, this should be a command
72+
// line option.
73+
parseOpts |= SourceFile::ParsingFlags::DisableDelayedBodies;
74+
parseOpts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
75+
76+
ModuleDecl *M = ModuleDecl::create(Identifier(), *ctx);
77+
SourceFile *SF =
78+
new (*ctx) SourceFile(*M, SourceFileKind::Library, bufferID, parseOpts);
79+
80+
Parser parser(bufferID, *SF, /*SILParserState=*/nullptr);
81+
SmallVector<ASTNode> items;
82+
parser.parseTopLevelItems(items);
83+
}
84+
};
85+
86+
struct SwiftParserExecutor {
87+
constexpr static StringRef name = "SwiftParser";
88+
89+
static void performParse(llvm::MemoryBufferRef buffer) {
90+
auto sourceFile = swift_ASTGen_parseSourceFile(
91+
buffer.getBufferStart(), buffer.getBufferSize(), "",
92+
buffer.getBufferIdentifier().data(), nullptr);
93+
swift_ASTGen_destroySourceFile(sourceFile);
94+
}
95+
};
96+
97+
static void _loadSwiftFilesRecursively(
98+
StringRef path,
99+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
100+
if (llvm::sys::fs::is_directory(path)) {
101+
std::error_code err;
102+
for (auto I = llvm::sys::fs::directory_iterator(path, err),
103+
E = llvm::sys::fs::directory_iterator();
104+
I != E; I.increment(err)) {
105+
_loadSwiftFilesRecursively(I->path(), buffers);
106+
}
107+
} else if (path.endswith(".swift")) {
108+
if (auto buffer = llvm::MemoryBuffer::getFile(path)) {
109+
buffers.push_back(std::move(*buffer));
110+
}
111+
}
112+
}
113+
114+
/// Load all '.swift' files in the specified \p filePaths into \p buffers.
115+
/// If the path is a directory, this recursively collects the files in it.
116+
static void
117+
loadSources(ArrayRef<std::string> filePaths,
118+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
119+
for (auto path : filePaths) {
120+
_loadSwiftFilesRecursively(path, buffers);
121+
}
122+
}
123+
124+
/// Measure the duration of \p body execution.
125+
template <typename Duration>
126+
static std::pair<Duration, Duration> measure(llvm::function_ref<void()> body) {
127+
auto cStart = std::clock();
128+
auto tStart = std::chrono::steady_clock::now();
129+
body();
130+
auto cEnd = std::clock();
131+
auto tEnd = std::chrono::steady_clock::now();
132+
133+
auto clockMultiply =
134+
Duration::period::den / CLOCKS_PER_SEC / Duration::period::num;
135+
136+
Duration cDuration((cEnd - cStart) * clockMultiply);
137+
return {std::chrono::duration_cast<Duration>(tEnd - tStart),
138+
std::chrono::duration_cast<Duration>(cDuration)};
139+
}
140+
141+
/// Perform the performance measurement using \c Executor .
142+
/// Parse all \p buffers using \c Executor , \p iteration times, and print out
143+
/// the measurement to the \c stdout.
144+
template <typename Executor>
145+
static void
146+
perform(const SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers,
147+
unsigned iteration, uintmax_t totalBytes, uintmax_t totalLines) {
148+
149+
llvm::outs() << "----\n";
150+
llvm::outs() << "parser: " << Executor::name << "\n";
151+
152+
using duration_t = std::chrono::nanoseconds;
153+
auto tDuration = duration_t::zero();
154+
auto cDuration = duration_t::zero();
155+
156+
for (unsigned i = 0; i < iteration; i++) {
157+
for (auto &buffer : buffers) {
158+
std::pair<duration_t, duration_t> elapsed = measure<duration_t>(
159+
[&] { Executor::performParse(buffer->getMemBufferRef()); });
160+
tDuration += elapsed.first;
161+
cDuration += elapsed.second;
162+
}
163+
}
164+
165+
auto tDisplay =
166+
std::chrono::duration_cast<std::chrono::milliseconds>(tDuration).count();
167+
auto cDisplay =
168+
std::chrono::duration_cast<std::chrono::milliseconds>(cDuration).count();
169+
170+
auto byteTPS = totalBytes * duration_t::period::den / cDuration.count();
171+
auto lineTPS = totalLines * duration_t::period::den / cDuration.count();
172+
173+
llvm::outs() << llvm::format("wall clock time (ms): %8d\n", tDisplay)
174+
<< llvm::format("cpu time (ms): %8d\n", cDisplay)
175+
<< llvm::format("throughput (byte/s): %8d\n", byteTPS)
176+
<< llvm::format("throughput (line/s): %8d\n", lineTPS);
177+
}
178+
179+
} // namespace
180+
181+
int swift_parse_test_main(ArrayRef<const char *> args, const char *argv0,
182+
void *mainAddr) {
183+
SwiftParseTestOptions options;
184+
llvm::cl::ParseCommandLineOptions(args.size(), args.data(),
185+
"Swift parse test\n");
186+
187+
unsigned iteration = options.Iteration;
188+
189+
SmallVector<std::unique_ptr<llvm::MemoryBuffer>> buffers;
190+
loadSources(options.InputPaths, buffers);
191+
unsigned int byteCount = 0;
192+
unsigned int lineCount = 0;
193+
for (auto &buffer : buffers) {
194+
byteCount += buffer->getBufferSize();
195+
lineCount += buffer->getBuffer().count('\n');
196+
}
197+
198+
llvm::outs() << llvm::format("file count: %8d\n", buffers.size())
199+
<< llvm::format("total bytes: %8d\n", byteCount)
200+
<< llvm::format("total lines: %8d\n", lineCount)
201+
<< llvm::format("iterations: %8d\n", iteration);
202+
203+
if (options.SwiftParser)
204+
perform<SwiftParserExecutor>(buffers, iteration, byteCount, lineCount);
205+
if (options.LibParse)
206+
perform<LibParseExecutor>(buffers, iteration, byteCount, lineCount);
207+
208+
return 0;
209+
}

0 commit comments

Comments
 (0)