Skip to content

Commit dc29c21

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 dc29c21

File tree

9 files changed

+309
-73
lines changed

9 files changed

+309
-73
lines changed

include/swift/Bridging/ASTGen.h

Lines changed: 89 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 7 additions & 0 deletions
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
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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+
struct SwiftParseTestOptions {
31+
llvm::cl::opt<bool> SwiftParser =
32+
llvm::cl::opt<bool>("swift-parser", llvm::cl::desc("Use SwiftParser"));
33+
34+
llvm::cl::opt<bool> LibParse =
35+
llvm::cl::opt<bool>("lib-parse", llvm::cl::desc("Use iibParse"));
36+
37+
llvm::cl::opt<unsigned int> Iteration = llvm::cl::opt<unsigned int>(
38+
"n", llvm::cl::desc("iteration"), llvm::cl::init(1));
39+
40+
llvm::cl::list<std::string> InputPaths = llvm::cl::list<std::string>(
41+
llvm::cl::Positional, llvm::cl::desc("input paths"));
42+
};
43+
44+
namespace {
45+
enum class ParseMode {
46+
SwiftParser,
47+
LibParse,
48+
};
49+
50+
struct LibParseExecutor {
51+
constexpr static StringRef name = "libParse";
52+
53+
static void performParse(llvm::MemoryBufferRef buffer) {
54+
SourceManager SM;
55+
unsigned bufferID =
56+
SM.addNewSourceBuffer(llvm::MemoryBuffer::getMemBuffer(buffer));
57+
DiagnosticEngine diagEngine(SM);
58+
LangOptions langOpts;
59+
TypeCheckerOptions typeckOpts;
60+
SILOptions silOpts;
61+
SearchPathOptions searchPathOpts;
62+
ClangImporterOptions clangOpts;
63+
symbolgraphgen::SymbolGraphOptions symbolOpts;
64+
std::unique_ptr<ASTContext> ctx(
65+
ASTContext::get(langOpts, typeckOpts, silOpts, searchPathOpts,
66+
clangOpts, symbolOpts, SM, diagEngine));
67+
68+
SourceFile::ParsingOptions parseOpts;
69+
// Always disable body skipping because SwiftParser currently don't have it.
70+
// When SwiftParser implements delayed parsing, this should be a command
71+
// line option.
72+
parseOpts |= SourceFile::ParsingFlags::DisableDelayedBodies;
73+
parseOpts |= SourceFile::ParsingFlags::DisablePoundIfEvaluation;
74+
75+
ModuleDecl *M = ModuleDecl::create(Identifier(), *ctx);
76+
SourceFile *SF =
77+
new (*ctx) SourceFile(*M, SourceFileKind::Library, bufferID, parseOpts);
78+
79+
Parser parser(bufferID, *SF, /*SILParserState=*/nullptr);
80+
SmallVector<ASTNode> items;
81+
parser.parseTopLevelItems(items);
82+
}
83+
};
84+
85+
struct SwiftParserExecutor {
86+
constexpr static StringRef name = "SwiftParser";
87+
88+
static void performParse(llvm::MemoryBufferRef buffer) {
89+
auto sourceFile = swift_ASTGen_parseSourceFile(
90+
buffer.getBufferStart(), buffer.getBufferSize(), "",
91+
buffer.getBufferIdentifier().data(), nullptr);
92+
swift_ASTGen_destroySourceFile(sourceFile);
93+
}
94+
};
95+
96+
static void _loadSwiftFilesRecursively(
97+
StringRef path,
98+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
99+
if (llvm::sys::fs::is_directory(path)) {
100+
std::error_code err;
101+
for (auto I = llvm::sys::fs::directory_iterator(path, err),
102+
E = llvm::sys::fs::directory_iterator();
103+
I != E; I.increment(err)) {
104+
_loadSwiftFilesRecursively(I->path(), buffers);
105+
}
106+
} else if (path.endswith(".swift")) {
107+
if (auto buffer = llvm::MemoryBuffer::getFile(path)) {
108+
buffers.push_back(std::move(*buffer));
109+
}
110+
}
111+
}
112+
113+
/// Load all '.swift' files in the specified \p filePaths into \p buffers.
114+
/// If the path is a directory, this recursively collects the files in it.
115+
static void
116+
loadSources(ArrayRef<std::string> filePaths,
117+
SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers) {
118+
for (auto path : filePaths) {
119+
_loadSwiftFilesRecursively(path, buffers);
120+
}
121+
}
122+
123+
/// Measure the duration of \p body execution.
124+
template <typename Duration>
125+
static std::pair<Duration, Duration> measure(llvm::function_ref<void()> body) {
126+
auto cStart = std::clock();
127+
auto tStart = std::chrono::steady_clock::now();
128+
body();
129+
auto cEnd = std::clock();
130+
auto tEnd = std::chrono::steady_clock::now();
131+
132+
std::chrono::milliseconds cDuration(1000 * (cEnd - cStart) / CLOCKS_PER_SEC);
133+
return {std::chrono::duration_cast<Duration>(tEnd - tStart),
134+
std::chrono::duration_cast<Duration>(cDuration)};
135+
}
136+
137+
/// Perform the performance measurement using \c Executor .
138+
/// Parse all \p buffers using \c Executor , \p iteration times, and print out
139+
/// the measurement to the \c stdout.
140+
template <typename Executor>
141+
static void
142+
perform(const SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &buffers,
143+
unsigned iteration, unsigned totalBytes, unsigned totalLines) {
144+
145+
llvm::outs() << "----\n";
146+
llvm::outs() << "parser: " << Executor::name << "\n";
147+
148+
using duration_t = std::chrono::milliseconds;
149+
auto tDuration = duration_t::zero();
150+
auto cDuration = duration_t::zero();
151+
152+
for (unsigned i = 0; i < iteration; i++) {
153+
for (auto &buffer : buffers) {
154+
std::pair<duration_t, duration_t> elapsed = measure<duration_t>(
155+
[&] { Executor::performParse(buffer->getMemBufferRef()); });
156+
tDuration += elapsed.first;
157+
cDuration += elapsed.second;
158+
}
159+
}
160+
161+
llvm::outs() << "wall clock time (ms): " << tDuration.count() << "\n";
162+
llvm::outs() << "cpu time (ms): " << cDuration.count() << "\n";
163+
llvm::outs() << "throughput (byte/s): "
164+
<< (totalBytes * 1000 / cDuration.count()) << "\n";
165+
llvm::outs() << "throughput (line/s): "
166+
<< (totalLines * 1000 / cDuration.count()) << "\n";
167+
}
168+
169+
} // namespace
170+
171+
int swift_parse_test_main(ArrayRef<const char *> args, const char *argv0,
172+
void *mainAddr) {
173+
SwiftParseTestOptions options;
174+
llvm::cl::ParseCommandLineOptions(args.size(), args.data(),
175+
"Swift parse test\n");
176+
177+
unsigned iteration = options.Iteration;
178+
179+
SmallVector<std::unique_ptr<llvm::MemoryBuffer>> buffers;
180+
loadSources(options.InputPaths, buffers);
181+
unsigned int byteCount = 0;
182+
unsigned int lineCount = 0;
183+
for (auto &buffer : buffers) {
184+
byteCount += buffer->getBufferSize();
185+
lineCount += buffer->getBuffer().count('\n');
186+
}
187+
188+
llvm::outs() << "file count: " << buffers.size() << "\n";
189+
llvm::outs() << "total bytes: " << byteCount << "\n";
190+
llvm::outs() << "total lines: " << lineCount << "\n";
191+
llvm::outs() << "iterations: " << iteration << "\n";
192+
193+
if (options.SwiftParser)
194+
perform<SwiftParserExecutor>(buffers, iteration, byteCount, lineCount);
195+
if (options.LibParse)
196+
perform<LibParseExecutor>(buffers, iteration, byteCount, lineCount);
197+
198+
return 0;
199+
}

lib/Frontend/PrintingDiagnosticConsumer.cpp

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/DiagnosticsCommon.h"
2121
#include "swift/Basic/LLVM.h"
2222
#include "swift/Basic/SourceManager.h"
23+
#include "swift/Bridging/ASTGen.h"
2324
#include "swift/Markup/Markup.h"
2425
#include "llvm/ADT/SmallString.h"
2526
#include "llvm/ADT/StringRef.h"
@@ -34,39 +35,6 @@
3435
using namespace swift;
3536
using namespace swift::markup;
3637

37-
extern "C" void *swift_ASTGen_createQueuedDiagnostics();
38-
extern "C" void swift_ASTGen_destroyQueuedDiagnostics(void *queued);
39-
extern "C" void swift_ASTGen_addQueuedSourceFile(
40-
void *queuedDiagnostics,
41-
ssize_t bufferID,
42-
void *sourceFile,
43-
const uint8_t *displayNamePtr,
44-
intptr_t displayNameLength,
45-
ssize_t parentID,
46-
ssize_t positionInParent);
47-
extern "C" void swift_ASTGen_addQueuedDiagnostic(
48-
void *queued,
49-
const char* text, ptrdiff_t textLength,
50-
BridgedDiagnosticSeverity severity,
51-
const void *sourceLoc,
52-
const void **highlightRanges,
53-
ptrdiff_t numHighlightRanges
54-
);
55-
extern "C" void
56-
swift_ASTGen_renderQueuedDiagnostics(void *queued, ssize_t contextSize,
57-
ssize_t colorize,
58-
BridgedString *renderedString);
59-
extern "C" void swift_ASTGen_freeBridgedString(BridgedString);
60-
61-
// FIXME: Hack because we cannot easily get to the already-parsed source
62-
// file from here. Fix this egregious oversight!
63-
extern "C" void *swift_ASTGen_parseSourceFile(const char *buffer,
64-
size_t bufferLength,
65-
const char *moduleName,
66-
const char *filename,
67-
void *_Nullable ctx);
68-
extern "C" void swift_ASTGen_destroySourceFile(void *sourceFile);
69-
7038
namespace {
7139
class ColoredStream : public raw_ostream {
7240
raw_ostream &Underlying;

0 commit comments

Comments
 (0)