|
| 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