Author: Paul Kirth Date: 2026-03-03T08:55:50-08:00 New Revision: b33c7db8eb630384d40acbd753f5bfbc80d0d2f0
URL: https://github.com/llvm/llvm-project/commit/b33c7db8eb630384d40acbd753f5bfbc80d0d2f0 DIFF: https://github.com/llvm/llvm-project/commit/b33c7db8eb630384d40acbd753f5bfbc80d0d2f0.diff LOG: [clang-doc] Add basic benchmarks for library functionality (#182620) clang-doc's performance is good, but we suspect it could be better. To track this with more fidelity, we can add a set of GoogleBenchmarks that exercise portions of the library. To start we try to track high level items that we monitor via the TimeTrace functions, and give them their own micro benchmarks. This should give us more confidence that switching out data structures or updating algorthms will have a positive performance impact. Note that an LLM helped generate portions of the benchmarks and parameterize them. Most of the internal logic was written by me, but the LLM was used to handle boilerplate and adaptation to the harness. Added: clang-tools-extra/clang-doc/benchmarks/CMakeLists.txt clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp Modified: clang-tools-extra/clang-doc/CMakeLists.txt Removed: ################################################################################ diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt index 7a375d7cd0524..3a6e9ad8fb62b 100644 --- a/clang-tools-extra/clang-doc/CMakeLists.txt +++ b/clang-tools-extra/clang-doc/CMakeLists.txt @@ -43,3 +43,7 @@ target_link_libraries(clangDoc ) add_subdirectory(tool) + +if (LLVM_INCLUDE_BENCHMARKS) + add_subdirectory(benchmarks) +endif() diff --git a/clang-tools-extra/clang-doc/benchmarks/CMakeLists.txt b/clang-tools-extra/clang-doc/benchmarks/CMakeLists.txt new file mode 100644 index 0000000000000..c3ae6fde8eeaf --- /dev/null +++ b/clang-tools-extra/clang-doc/benchmarks/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_benchmark(ClangDocBenchmark ClangDocBenchmark.cpp) + +target_include_directories(ClangDocBenchmark + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +target_link_libraries(ClangDocBenchmark + PRIVATE + clangDoc + clangTooling + clangBasic + clangAST +) diff --git a/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp new file mode 100644 index 0000000000000..652000b15dc5f --- /dev/null +++ b/clang-tools-extra/clang-doc/benchmarks/ClangDocBenchmark.cpp @@ -0,0 +1,234 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains basic benchmarks for clang-doc's implementation and +/// library components. +/// +//===----------------------------------------------------------------------===// + +#include "BitcodeReader.h" +#include "BitcodeWriter.h" +#include "ClangDoc.h" +#include "Generators.h" +#include "Representation.h" +#include "Serialize.h" +#include "benchmark/benchmark.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Tooling/Execution.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include <string> +#include <vector> + +namespace clang { +namespace doc { + +class BenchmarkVisitor : public RecursiveASTVisitor<BenchmarkVisitor> { +public: + explicit BenchmarkVisitor(const FunctionDecl *&Func) : Func(Func) {} + + bool VisitFunctionDecl(const FunctionDecl *D) { + if (D->getName() == "f") { + Func = D; + return false; + } + return true; + } + +private: + const FunctionDecl *&Func; +}; + +// --- Mapper Benchmarks --- + +static void BM_EmitInfoFunction(benchmark::State &State) { + std::string Code = "void f() {}"; + std::unique_ptr<clang::ASTUnit> AST = clang::tooling::buildASTFromCode(Code); + const FunctionDecl *Func = nullptr; + BenchmarkVisitor Visitor(Func); + Visitor.TraverseDecl(AST->getASTContext().getTranslationUnitDecl()); + assert(Func); + + clang::comments::FullComment *FC = nullptr; + Location Loc; + + for (auto _ : State) { + auto Result = serialize::emitInfo(Func, FC, Loc, /*PublicOnly=*/false); + benchmark::DoNotOptimize(Result); + } +} +BENCHMARK(BM_EmitInfoFunction); + +static void BM_Mapper_Scale(benchmark::State &State) { + std::string Code; + for (int i = 0; i < State.range(0); ++i) { + Code += "void f" + std::to_string(i) + "() {}\n"; + } + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()); + + for (auto _ : State) { + tooling::InMemoryToolResults Results; + tooling::ExecutionContext ECtx(&Results); + ClangDocContext CDCtx(&ECtx, "test-project", false, "", "", "", "", "", {}, + Diags, false); + auto ActionFactory = doc::newMapperActionFactory(CDCtx); + std::unique_ptr<FrontendAction> Action = ActionFactory->create(); + tooling::runToolOnCode(std::move(Action), Code, "test.cpp"); + } +} +BENCHMARK(BM_Mapper_Scale)->Range(10, 10000); + +// --- Reducer Benchmarks --- + +static void BM_SerializeFunctionInfo(benchmark::State &State) { + auto I = std::make_unique<FunctionInfo>(); + I->Name = "f"; + I->DefLoc = Location(0, 0, "test.cpp"); + I->ReturnType = TypeInfo("void"); + I->IT = InfoType::IT_function; + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()); + + std::unique_ptr<Info> InfoPtr = std::move(I); + + for (auto _ : State) { + auto Result = serialize::serialize(InfoPtr, Diags); + benchmark::DoNotOptimize(Result); + } +} +BENCHMARK(BM_SerializeFunctionInfo); + +static void BM_MergeInfos_Scale(benchmark::State &State) { + SymbolID USR = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; + + for (auto _ : State) { + State.PauseTiming(); + std::vector<std::unique_ptr<Info>> Input; + Input.reserve(State.range(0)); + for (int i = 0; i < State.range(0); ++i) { + auto I = std::make_unique<FunctionInfo>(); + I->Name = "f"; + I->USR = USR; + I->DefLoc = Location(10, i, "test.cpp"); + Input.push_back(std::move(I)); + } + State.ResumeTiming(); + + auto Result = doc::mergeInfos(Input); + if (!Result) { + State.SkipWithError("mergeInfos failed"); + llvm::consumeError(Result.takeError()); + } + benchmark::DoNotOptimize(Result); + } +} +BENCHMARK(BM_MergeInfos_Scale)->Range(2, 10000); + +static void BM_BitcodeReader_Scale(benchmark::State &State) { + int NumRecords = State.range(0); + + SmallString<0> Buffer; + llvm::BitstreamWriter Stream(Buffer); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()); + + ClangDocBitcodeWriter Writer(Stream, Diags); + for (int i = 0; i < NumRecords; ++i) { + RecordInfo RI; + RI.Name = "Record" + std::to_string(i); + RI.USR = {(uint8_t)(i & 0xFF)}; + Writer.emitBlock(RI); + } + + std::string BitcodeData = Buffer.str().str(); + + for (auto _ : State) { + llvm::BitstreamCursor Cursor(llvm::ArrayRef<uint8_t>( + (const uint8_t *)BitcodeData.data(), BitcodeData.size())); + ClangDocBitcodeReader Reader(Cursor, Diags); + auto Result = Reader.readBitcode(); + if (!Result) { + State.SkipWithError("readBitcode failed"); + llvm::consumeError(Result.takeError()); + } + benchmark::DoNotOptimize(Result); + } +} +BENCHMARK(BM_BitcodeReader_Scale)->Range(10, 10000); + +// --- Generator Benchmarks --- + +static void BM_JSONGenerator_Scale(benchmark::State &State) { + auto G = doc::findGeneratorByName("json"); + if (!G) { + State.SkipWithError("JSON Generator not found"); + llvm::consumeError(G.takeError()); + return; + } + + int NumRecords = State.range(0); + auto NI = std::make_unique<NamespaceInfo>(); + NI->Name = "GlobalNamespace"; + for (int i = 0; i < NumRecords; ++i) { + NI->Children.Records.emplace_back(SymbolID{(uint8_t)(i & 0xFF)}, + "Record" + std::to_string(i), + InfoType::IT_record); + } + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diags(DiagID, DiagOpts, new IgnoringDiagConsumer()); + ClangDocContext CDCtx(nullptr, "test-project", false, "", "", "", "", "", {}, + Diags, false); + + std::string Output; + llvm::raw_string_ostream OS(Output); + + for (auto _ : State) { + Output.clear(); + auto Err = (*G)->generateDocForInfo(NI.get(), OS, CDCtx); + if (Err) { + State.SkipWithError("generateDocForInfo failed"); + llvm::consumeError(std::move(Err)); + } + benchmark::DoNotOptimize(Output); + } +} +BENCHMARK(BM_JSONGenerator_Scale)->Range(10, 10000); + +// --- Index Benchmarks --- + +static void BM_Index_Insertion(benchmark::State &State) { + for (auto _ : State) { + Index Idx; + for (int i = 0; i < State.range(0); ++i) { + RecordInfo I; + I.Name = "Record" + std::to_string(i); + // Vary USR to ensure unique entries + I.USR = {(uint8_t)(i & 0xFF), (uint8_t)((i >> 8) & 0xFF)}; + I.Path = "path/to/record"; + Generator::addInfoToIndex(Idx, &I); + } + benchmark::DoNotOptimize(Idx); + } +} +BENCHMARK(BM_Index_Insertion)->Range(10, 10000); + +} // namespace doc +} // namespace clang + +BENCHMARK_MAIN(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
