Paul Kirth 1deab59f6f
[clang-doc] Migrate Namespaces to arena allocation (#190048)
This patch allocates the NamespaceInfo types in the local arenas, and
adapts the merging logic for the new list type and its children.
Memory use and performance improve slightly. Micro-benchmarks show a
regression in merge operations due to the more complex list operations.

 ## Build Clang-Doc Documentation
| Metric | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| Time | 920.5s | 1009.2s | 1002.4s | +8.9% | -0.7% |
| Memory | 86.0G | 43.2G | 43.9G | -49.0% | +1.6% |

 ## Microbenchmarks (Filtered for >1% Delta)
| Benchmark | Baseline | Prev | This | Culm% | Seq% |
| :--- | :--- | :--- | :--- | :--- | :--- |
| BM_BitcodeReader_Scale/10 | 67.9us | 69.7us | 69.3us | +1.9% | -0.7% |
| BM_BitcodeReader_Scale/10000 | 70.5ms | 22.3ms | 24.8ms | -64.8% |
+11.4% |
| BM_BitcodeReader_Scale/4096 | 23.2ms | 4.7ms | 4.4ms | -80.9% | -5.7%
|
| BM_BitcodeReader_Scale/512 | 509.4us | 558.7us | 540.2us | +6.0% |
-3.3% |
| BM_BitcodeReader_Scale/64 | 114.8us | 119.2us | 117.0us | +1.9% |
-1.8% |
| BM_EmitInfoFunction | 1.6us | 1.6us | 1.6us | -1.1% | +0.8% |
| BM_Index_Insertion/10 | 2.3us | 4.2us | 3.7us | +61.7% | -11.3% |
| BM_Index_Insertion/10000 | 3.1ms | 5.4ms | 4.9ms | +56.8% | -9.1% |
| BM_Index_Insertion/4096 | 1.3ms | 2.2ms | 2.0ms | +54.5% | -8.2% |
| BM_Index_Insertion/512 | 153.6us | 259.6us | 236.7us | +54.1% | -8.8%
|
| BM_Index_Insertion/64 | 18.1us | 30.7us | 27.9us | +54.5% | -9.0% |
| BM_JSONGenerator_Scale/10 | 36.8us | 37.6us | 36.6us | -0.7% | -2.7% |
| BM_JSONGenerator_Scale/4096 | 33.7ms | 34.3ms | 34.2ms | +1.4% | -0.3%
|
| BM_JSONGenerator_Scale/64 | 222.4us | 225.6us | 220.2us | -1.0% |
-2.4% |
| BM_Mapper_Scale/10 | 2.5ms | 2.5ms | 2.5ms | -1.4% | -1.7% |
| BM_Mapper_Scale/10000 | 104.3ms | 109.3ms | 105.9ms | +1.6% | -3.1% |
| BM_Mapper_Scale/4096 | 44.3ms | 43.9ms | 44.7ms | +0.8% | +1.8% |
| BM_Mapper_Scale/512 | 7.6ms | 7.6ms | 7.6ms | +0.8% | +1.2% |
| BM_MergeInfos_Scale/10000 | 12.2ms | 1.6ms | 2.0ms | -83.6% | +28.7% |
| BM_MergeInfos_Scale/2 | 1.9us | 1.7us | 1.7us | -8.1% | +0.9% |
| BM_MergeInfos_Scale/4096 | 2.8ms | 520.5us | 557.4us | -79.9% | +7.1%
|
| BM_MergeInfos_Scale/512 | 68.9us | 37.9us | 39.9us | -42.0% | +5.3% |
| BM_MergeInfos_Scale/64 | 10.3us | 6.3us | 6.5us | -36.7% | +2.7% |
| BM_MergeInfos_Scale/8 | 2.8us | 2.2us | 2.2us | -20.7% | +1.2% |
| BM_SerializeFunctionInfo | 25.5us | 25.8us | 26.1us | +2.1% | +1.1% |
2026-04-03 20:33:56 +00:00

278 lines
7.0 KiB
C++

#include "ClangDocTest.h"
#include "Generators.h"
#include "Representation.h"
#include "gtest/gtest.h"
namespace clang {
namespace doc {
static std::unique_ptr<Generator> getJSONGenerator() {
auto G = doc::findGeneratorByName("json");
if (!G)
return nullptr;
return std::move(G.get());
}
class JSONGeneratorTest : public ClangDocContextTest {};
TEST_F(JSONGeneratorTest, emitRecordJSON) {
RecordInfo I;
I.Name = "Foo";
I.IsTypeDef = false;
I.Namespace.emplace_back(EmptySID, "GlobalNamespace", InfoType::IT_namespace);
I.Path = "GlobalNamespace";
I.DefLoc = Location(1, 1, "main.cpp");
I.TagType = TagTypeKind::Class;
I.Template = TemplateInfo();
I.Template->Params.emplace_back("class T");
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "Color";
I.Children.Enums.back().Scoped = false;
I.Children.Enums.back().Members.emplace_back();
I.Children.Enums.back().Members.back().Name = "RED";
I.Children.Enums.back().Members.back().Value = "0";
I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_protected);
I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
AccessSpecifier::AS_public, true);
I.Bases.back().Children.Functions.emplace_back();
I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne";
I.Bases.back().Members.emplace_back(TypeInfo("int"), "N",
AccessSpecifier::AS_public);
// F is in the global namespace
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
"path::to::G::G", "path/to/G");
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path::to::A::r::ChildStruct", "path/to/A/r");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
auto G = getJSONGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, getClangDocContext());
assert(!Err);
std::string Expected = R"raw({
"Bases": [
{
"Access": "public",
"End": true,
"HasMembers": true,
"HasPublicMembers": true,
"HasPublicMethods": true,
"InfoType": "record",
"IsParent": true,
"IsTypedef": false,
"IsVirtual": true,
"MangledName": "",
"Name": "F",
"Path": "path/to/F",
"PublicMembers": [
{
"IsStatic": false,
"Name": "N",
"Type": "int"
}
],
"PublicMethods": [
{
"InfoType": "function",
"IsStatic": false,
"Name": "InheritedFunctionOne",
"ReturnType": {
"IsBuiltIn": false,
"IsTemplate": false,
"Name": "",
"QualName": "",
"USR": "0000000000000000000000000000000000000000"
}
}
],
"TagType": "struct"
}
],
"Enums": [
{
"End": true,
"InfoType": "enum",
"Members": [
{
"End": true,
"Name": "RED",
"Value": "0"
}
],
"Name": "Color",
"Scoped": false
}
],
"HasEnums": true,
"HasMembers": true,
"HasParents": true,
"HasProtectedMembers": true,
"HasPublicMethods": true,
"HasRecords": true,
"HasVirtualParents": true,
"InfoType": "record",
"IsTypedef": false,
"Location": {
"Filename": "main.cpp",
"LineNumber": 1
},
"MangledName": "",
"Name": "Foo",
"Namespace": [
"GlobalNamespace"
],
"Parents": [
{
"End": true,
"Name": "F",
"QualName": "",
"USR": "0000000000000000000000000000000000000000"
}
],
"Path": "GlobalNamespace",
"ProtectedMembers": [
{
"IsStatic": false,
"Name": "X",
"Type": "int"
}
],
"PublicMethods": [
{
"InfoType": "function",
"IsStatic": false,
"Name": "OneFunction",
"ReturnType": {
"IsBuiltIn": false,
"IsTemplate": false,
"Name": "",
"QualName": "",
"USR": "0000000000000000000000000000000000000000"
}
}
],
"Records": [
{
"End": true,
"Name": "ChildStruct",
"Path": "path/to/A/r",
"QualName": "path::to::A::r::ChildStruct",
"USR": "0000000000000000000000000000000000000000"
}
],
"TagType": "class",
"Template": {
"Parameters": [
{
"End": true,
"Param": "class T"
}
],
"VerticalDisplay": false
},
"VirtualParents": [
{
"End": true,
"Name": "G",
"Path": "path/to/G",
"QualName": "path::to::G::G",
"USR": "0000000000000000000000000000000000000000"
}
]
})raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST_F(JSONGeneratorTest, emitNamespaceJSON) {
NamespaceInfo I;
I.Name = "Namespace";
I.Path = "path/to/A";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
Reference NewNamespace(EmptySID, "ChildNamespace", InfoType::IT_namespace,
"path::to::A::Namespace::ChildNamespace",
"path/to/A/Namespace");
I.Children.Namespaces.push_back(NewNamespace);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
"path::to::A::Namespace::ChildStruct",
"path/to/A/Namespace");
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getJSONGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, getClangDocContext());
assert(!Err);
std::string Expected = R"raw({
"Enums": [
{
"End": true,
"InfoType": "enum",
"Name": "OneEnum",
"Scoped": false
}
],
"Functions": [
{
"End": true,
"InfoType": "function",
"IsStatic": false,
"Name": "OneFunction",
"ReturnType": {
"IsBuiltIn": false,
"IsTemplate": false,
"Name": "",
"QualName": "",
"USR": "0000000000000000000000000000000000000000"
}
}
],
"HasEnums": true,
"HasFunctions": true,
"HasNamespaces": true,
"HasRecords": true,
"InfoType": "namespace",
"Name": "Global Namespace",
"Namespace": [
"A"
],
"Namespaces": [
{
"End": true,
"Name": "ChildNamespace",
"Path": "path/to/A/Namespace",
"QualName": "path::to::A::Namespace::ChildNamespace",
"USR": "0000000000000000000000000000000000000000"
}
],
"Path": "path/to/A",
"Records": [
{
"End": true,
"Name": "ChildStruct",
"Path": "path/to/A/Namespace",
"QualName": "path::to::A::Namespace::ChildStruct",
"USR": "0000000000000000000000000000000000000000"
}
]
})raw";
EXPECT_EQ(Expected, Actual.str());
}
} // namespace doc
} // namespace clang