Skip to content

Commit

Permalink
perf: Optimize xrefmap.json file deserialization performance (#9824)
Browse files Browse the repository at this point in the history
perf: optimize xrefmap.json file deserialization
  • Loading branch information
filzrev authored Apr 1, 2024
1 parent 2799e6f commit aa982be
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 8 deletions.
39 changes: 31 additions & 8 deletions src/Docfx.Build/XRefMaps/XRefMapDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,25 @@ protected static IXRefContainer DownloadFromLocal(Uri uri)
private static IXRefContainer ReadLocalFile(string filePath)
{
Logger.LogVerbose($"Reading from file: {filePath}");
if (".zip".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase))

switch (Path.GetExtension(filePath).ToLowerInvariant())
{
return XRefArchive.Open(filePath, XRefArchiveMode.Read);
}
case ".zip":
return XRefArchive.Open(filePath, XRefArchiveMode.Read);

using var sr = File.OpenText(filePath);
return YamlUtility.Deserialize<XRefMap>(sr);
case ".json":
{
using var stream = File.OpenText(filePath);
return JsonUtility.Deserialize<XRefMap>(stream);
}

case ".yml":
default:
{
using var sr = File.OpenText(filePath);
return YamlUtility.Deserialize<XRefMap>(sr);
}
}
}

protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
Expand All @@ -134,10 +146,21 @@ protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
};

using var stream = await httpClient.GetStreamAsync(uri);
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
var map = YamlUtility.Deserialize<XRefMap>(sr);

return map;
switch (Path.GetExtension(uri.AbsolutePath).ToLowerInvariant())
{
case ".json":
{
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
return JsonUtility.Deserialize<XRefMap>(sr);
}
case ".yml":
default:
{
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
return YamlUtility.Deserialize<XRefMap>(sr);
}
}
}

public static void UpdateHref(XRefMap map, Uri uri)
Expand Down
6 changes: 6 additions & 0 deletions src/Docfx.Build/XRefMaps/XRefMapRedirection.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;
using Newtonsoft.Json;
using YamlDotNet.Serialization;

namespace Docfx.Build.Engine;

public class XRefMapRedirection
{
[YamlMember(Alias = "uidPrefix")]
[JsonProperty("uidPrefix")]
[JsonPropertyName("uidPrefix")]
public string UidPrefix { get; set; }

[YamlMember(Alias = "href")]
[JsonProperty("Href")]
[JsonPropertyName("href")]
public string Href { get; set; }
}
10 changes: 10 additions & 0 deletions test/Docfx.Build.Tests/TestData/xrefmap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"references": [
{
"fullName": "str",
"href": "https://docs.python.org/3.5/library/stdtypes.html#str",
"name": "str",
"uid": "str"
}
]
}
15 changes: 15 additions & 0 deletions test/Docfx.Build.Tests/XRefMapDownloaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using FluentAssertions;
using Xunit;

namespace Docfx.Build.Engine.Tests;
Expand Down Expand Up @@ -35,4 +36,18 @@ public async Task ReadLocalXRefMapWithFallback()
Assert.NotNull(xrefSpec);
Assert.Equal("https://docs.python.org/3.5/library/stdtypes.html#str", xrefSpec.Href);
}

[Fact]
public async Task ReadLocalXRefMapJsonFileTest()
{
// Arrange
var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "xrefmap.json");

XRefMapDownloader downloader = new XRefMapDownloader();
var xrefMap = await downloader.DownloadAsync(new Uri(path)) as XRefMap;

// Assert
xrefMap.Should().NotBeNull();
xrefMap.References.Should().HaveCount(1);
}
}
74 changes: 74 additions & 0 deletions test/Docfx.Build.Tests/XRefMapSerializationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Docfx.Common;
using Docfx.Plugins;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;

namespace Docfx.Build.Engine.Tests;

public class XRefMapSerializationTest
{
[Fact]
public void XRefMapSerializationRoundTripTest()
{
var model = new XRefMap
{
BaseUrl = "http://localhost",
Sorted = true,
HrefUpdated = null,
Redirections = new List<XRefMapRedirection>
{
new XRefMapRedirection
{
Href = "Dummy",
UidPrefix = "Dummy"
},
},
References = new List<XRefSpec>
{
new XRefSpec(new Dictionary<string,object>
{
["Additional1"] = "Dummy",
})
{
Uid = "Dummy",
Name = "Dummy",
Href = "Dummy",
CommentId ="Dummy",
IsSpec = true,
},
},
Others = new Dictionary<string, object>
{
["Other1"] = "Dummy",
}
};

// Arrange
var jsonResult = RoundtripByNewtonsoftJson(model);
var yamlResult = RoundtripWithYamlDotNet(model);

// Assert
jsonResult.Should().BeEquivalentTo(model);
yamlResult.Should().BeEquivalentTo(model);
}

private static T RoundtripByNewtonsoftJson<T>(T model)
{
var json = JsonUtility.Serialize(model);
return JsonUtility.Deserialize<T>(new StringReader(json));
}

private static T RoundtripWithYamlDotNet<T>(T model)
{
var sb = new StringBuilder();
using var sw = new StringWriter(sb);
YamlUtility.Serialize(sw, model);
var json = sb.ToString();
return YamlUtility.Deserialize<T>(new StringReader(json));
}
}
4 changes: 4 additions & 0 deletions test/docfx.Tests/Api.verified.cs
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,12 @@ protected override Docfx.Build.Engine.IXRefContainer GetMap(string name) { }
public class XRefMapRedirection
{
public XRefMapRedirection() { }
[Newtonsoft.Json.JsonProperty("Href")]
[System.Text.Json.Serialization.JsonPropertyName("href")]
[YamlDotNet.Serialization.YamlMember(Alias="href")]
public string Href { get; set; }
[Newtonsoft.Json.JsonProperty("uidPrefix")]
[System.Text.Json.Serialization.JsonPropertyName("uidPrefix")]
[YamlDotNet.Serialization.YamlMember(Alias="uidPrefix")]
public string UidPrefix { get; set; }
}
Expand Down

0 comments on commit aa982be

Please sign in to comment.