Skip to content

Commit 8f81b5b

Browse files
authored
Copy GC settings from host process (#1765)
1 parent 8cb701c commit 8f81b5b

File tree

6 files changed

+76
-46
lines changed

6 files changed

+76
-46
lines changed

src/BenchmarkDotNet/Extensions/ProcessExtensions.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ internal static void SetEnvironmentVariables(this ProcessStartInfo start, Benchm
124124
// we have to set "COMPlus_GC*" environment variables as documented in
125125
// https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector
126126
if (benchmarkCase.Job.Infrastructure.Toolchain is CoreRunToolchain _)
127-
start.SetCoreRunEnvironmentVariables(benchmarkCase);
127+
start.SetCoreRunEnvironmentVariables(benchmarkCase, resolver);
128128

129129
if (!benchmarkCase.Job.HasValue(EnvironmentMode.EnvironmentVariablesCharacteristic))
130130
return;
@@ -229,19 +229,21 @@ private static int RunProcessAndIgnoreOutput(string fileName, string arguments,
229229
}
230230
}
231231

232-
private static void SetCoreRunEnvironmentVariables(this ProcessStartInfo start, BenchmarkCase benchmarkCase)
232+
private static void SetCoreRunEnvironmentVariables(this ProcessStartInfo start, BenchmarkCase benchmarkCase, IResolver resolver)
233233
{
234234
var gcMode = benchmarkCase.Job.Environment.Gc;
235-
if (!gcMode.HasChanges)
236-
return; // do nothing for the default settings
237-
238-
start.EnvironmentVariables["COMPlus_gcServer"] = gcMode.Server ? "1" : "0";
239-
start.EnvironmentVariables["COMPlus_gcConcurrent"] = gcMode.Concurrent ? "1" : "0";
240-
start.EnvironmentVariables["COMPlus_GCCpuGroup"] = gcMode.CpuGroups ? "1" : "0";
241-
start.EnvironmentVariables["COMPlus_gcAllowVeryLargeObjects"] = gcMode.AllowVeryLargeObjects ? "1" : "0";
242-
start.EnvironmentVariables["COMPlus_GCRetainVM"] = gcMode.RetainVm ? "1" : "0";
243-
start.EnvironmentVariables["COMPlus_GCNoAffinitize"] = gcMode.NoAffinitize ? "1" : "0";
244235

236+
start.EnvironmentVariables["COMPlus_gcServer"] = gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver) ? "1" : "0";
237+
start.EnvironmentVariables["COMPlus_gcConcurrent"] = gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver) ? "1" : "0";
238+
239+
if (gcMode.HasValue(GcMode.CpuGroupsCharacteristic))
240+
start.EnvironmentVariables["COMPlus_GCCpuGroup"] = gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver) ? "1" : "0";
241+
if (gcMode.HasValue(GcMode.AllowVeryLargeObjectsCharacteristic))
242+
start.EnvironmentVariables["COMPlus_gcAllowVeryLargeObjects"] = gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver) ? "1" : "0";
243+
if (gcMode.HasValue(GcMode.RetainVmCharacteristic))
244+
start.EnvironmentVariables["COMPlus_GCRetainVM"] = gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver) ? "1" : "0";
245+
if (gcMode.HasValue(GcMode.NoAffinitizeCharacteristic))
246+
start.EnvironmentVariables["COMPlus_GCNoAffinitize"] = gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver) ? "1" : "0";
245247
if (gcMode.HasValue(GcMode.HeapAffinitizeMaskCharacteristic))
246248
start.EnvironmentVariables["COMPlus_GCHeapAffinitizeMask"] = gcMode.HeapAffinitizeMask.ToString("X");
247249
if (gcMode.HasValue(GcMode.HeapCountCharacteristic))

src/BenchmarkDotNet/Toolchains/AppConfigGenerator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ private static void GenerateJitSettings(XmlDocument xmlDocument, XmlNode runtime
100100

101101
private static void GenerateGCSettings(XmlDocument xmlDocument, XmlNode runtimeElement, GcMode gcMode, IResolver resolver)
102102
{
103-
if (!gcMode.HasChanges)
104-
return;
105-
106103
CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcConcurrent", "enabled", gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase());
107104
CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcServer", "enabled", gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase());
108-
CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCCpuGroup", "enabled", gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver).ToLowerCase());
109-
CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcAllowVeryLargeObjects", "enabled", gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver).ToLowerCase());
110-
CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCNoAffinitize", "enabled", gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver).ToLowerCase());
111105

106+
if (gcMode.HasValue(GcMode.CpuGroupsCharacteristic))
107+
CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCCpuGroup", "enabled", gcMode.ResolveValue(GcMode.CpuGroupsCharacteristic, resolver).ToLowerCase());
108+
if (gcMode.HasValue(GcMode.AllowVeryLargeObjectsCharacteristic))
109+
CreateNodeWithAttribute(xmlDocument, runtimeElement, "gcAllowVeryLargeObjects", "enabled", gcMode.ResolveValue(GcMode.AllowVeryLargeObjectsCharacteristic, resolver).ToLowerCase());
110+
if (gcMode.HasValue(GcMode.NoAffinitizeCharacteristic))
111+
CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCNoAffinitize", "enabled", gcMode.ResolveValue(GcMode.NoAffinitizeCharacteristic, resolver).ToLowerCase());
112112
if (gcMode.HasValue(GcMode.HeapAffinitizeMaskCharacteristic))
113113
CreateNodeWithAttribute(xmlDocument, runtimeElement, "GCHeapAffinitizeMask", "enabled", gcMode.ResolveValue(GcMode.HeapAffinitizeMaskCharacteristic, resolver).ToString());
114114
if (gcMode.HasValue(GcMode.HeapCountCharacteristic))

src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,15 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts
8383
[PublicAPI]
8484
protected virtual string GetRuntimeSettings(GcMode gcMode, IResolver resolver)
8585
{
86-
if (!gcMode.HasChanges)
87-
return string.Empty;
88-
89-
return new StringBuilder(80)
86+
var builder = new StringBuilder(80)
9087
.AppendLine("<PropertyGroup>")
91-
.AppendLine($"<ServerGarbageCollection>{gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase()}</ServerGarbageCollection>")
92-
.AppendLine($"<ConcurrentGarbageCollection>{gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase()}</ConcurrentGarbageCollection>")
93-
.AppendLine($"<RetainVMGarbageCollection>{gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver).ToLowerCase()}</RetainVMGarbageCollection>")
94-
.AppendLine("</PropertyGroup>")
95-
.ToString();
88+
.AppendLine($"<ServerGarbageCollection>{gcMode.ResolveValue(GcMode.ServerCharacteristic, resolver).ToLowerCase()}</ServerGarbageCollection>")
89+
.AppendLine($"<ConcurrentGarbageCollection>{gcMode.ResolveValue(GcMode.ConcurrentCharacteristic, resolver).ToLowerCase()}</ConcurrentGarbageCollection>");
90+
91+
if (gcMode.HasValue(GcMode.RetainVmCharacteristic))
92+
builder.AppendLine($"<RetainVMGarbageCollection>{gcMode.ResolveValue(GcMode.RetainVmCharacteristic, resolver).ToLowerCase()}</RetainVMGarbageCollection>");
93+
94+
return builder.AppendLine("</PropertyGroup>").ToString();
9695
}
9796

9897
// the host project or one of the .props file that it imports might contain some custom settings that needs to be copied, sth like

tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<DebugType>pdbonly</DebugType>
1313
<DebugSymbols>true</DebugSymbols>
1414
<NoWarn>$(NoWarn);CA2007</NoWarn>
15+
<ServerGarbageCollection>true</ServerGarbageCollection>
1516
</PropertyGroup>
1617
<ItemGroup>
1718
<Content Include="xunit.runner.json">

tests/BenchmarkDotNet.IntegrationTests/GcModeTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ public GcModeTests(ITestOutputHelper outputHelper) : base(outputHelper) { }
1818
private IConfig CreateConfig(GcMode gc) => ManualConfig.CreateEmpty().AddJob(new Job(Job.Dry, gc));
1919

2020
[Fact]
21-
public void CanHostGcMode()
21+
public void HostProcessSettingsAreCopiedByDefault()
2222
{
2323
var config = CreateConfig(GcMode.Default);
24-
CanExecute<WorkstationGcOnly>(config);
24+
25+
if (GCSettings.IsServerGC)
26+
CanExecute<ServerModeEnabled>(config);
27+
else
28+
CanExecute<WorkstationGcOnly>(config);
2529
}
2630

2731
[Fact]

tests/BenchmarkDotNet.Tests/AppConfigGeneratorTests.cs

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
using System.Text;
66
using BenchmarkDotNet.Characteristics;
77
using BenchmarkDotNet.Environments;
8+
using BenchmarkDotNet.Extensions;
89
using BenchmarkDotNet.Running;
910
using Xunit;
1011
using BenchmarkDotNet.Tests.XUnit;
12+
using System.Runtime;
1113

1214
namespace BenchmarkDotNet.Tests
1315
{
@@ -20,10 +22,10 @@ public void GeneratesMinimalRequiredAppConfigForEmptySource()
2022
{
2123
using (var destination = new Utf8StringWriter())
2224
{
23-
const string expectedMinimal =
25+
string expectedMinimal =
2426
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
2527
"<configuration>" +
26-
"<runtime/>" +
28+
$"<runtime>{GcSettings}</runtime>" +
2729
"</configuration>";
2830

2931
AppConfigGenerator.Generate(Job.Default, TextReader.Null, destination, Resolver);
@@ -38,10 +40,10 @@ public void GeneratesMinimalRequiredAppConfigForAlmostEmptySource()
3840
using (var source = new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))
3941
using (var destination = new Utf8StringWriter())
4042
{
41-
const string expectedMinimal =
43+
string expectedMinimal =
4244
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
4345
"<configuration>" +
44-
"<runtime/>" +
46+
$"<runtime>{GcSettings}</runtime>" +
4547
"</configuration>";
4648

4749
AppConfigGenerator.Generate(Job.Default, source, destination, Resolver);
@@ -53,50 +55,69 @@ public void GeneratesMinimalRequiredAppConfigForAlmostEmptySource()
5355
[Fact]
5456
public void RewritesCustomSettings()
5557
{
56-
const string customSettings =
58+
string customSettingsWithoutRuntimeNode =
5759
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
5860
"<!--" +
5961
"commentsAreSupported" +
6062
"-->" +
6163
"<configuration>" +
6264
"<someConfig>withItsValue</someConfig>" +
63-
"<runtime/>" +
6465
"</configuration>";
6566

66-
using (var source = new StringReader(customSettings))
67+
string customSettingsWithRuntimeNode =
68+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
69+
"<!--" +
70+
"commentsAreSupported" +
71+
"-->" +
72+
"<configuration>" +
73+
"<someConfig>withItsValue</someConfig>" +
74+
$"<runtime>{GcSettings}</runtime>" +
75+
"</configuration>";
76+
77+
using (var source = new StringReader(customSettingsWithoutRuntimeNode))
6778
using (var destination = new Utf8StringWriter())
6879
{
6980
AppConfigGenerator.Generate(Job.Default, source, destination, Resolver);
7081

71-
AssertAreEqualIgnoringWhitespacesAndCase(customSettings, destination.ToString());
82+
AssertAreEqualIgnoringWhitespacesAndCase(customSettingsWithRuntimeNode, destination.ToString());
7283
}
7384
}
7485

7586
[Fact]
7687
public void RewritesCustomRuntimeSettings()
7788
{
78-
const string customSettings =
89+
string customSettingsBefore =
7990
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
8091
"<!--" +
8192
"commentsAreSupported" +
8293
"-->" +
8394
"<configuration>" +
8495
"<someConfig>withItsValue</someConfig>" +
85-
"<runtime><AppContextSwitchOverrides value=\"Switch.System.IO.UseLegacyPathHandling=false\"/></runtime>" +
96+
$"<runtime><AppContextSwitchOverrides value=\"Switch.System.IO.UseLegacyPathHandling=false\"/></runtime>" +
8697
"</configuration>";
8798

88-
using (var source = new StringReader(customSettings))
99+
string customSettingsAfter =
100+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
101+
"<!--" +
102+
"commentsAreSupported" +
103+
"-->" +
104+
"<configuration>" +
105+
"<someConfig>withItsValue</someConfig>" +
106+
$"<runtime><AppContextSwitchOverrides value=\"Switch.System.IO.UseLegacyPathHandling=false\"/>{GcSettings}</runtime>" +
107+
"</configuration>";
108+
109+
using (var source = new StringReader(customSettingsBefore))
89110
using (var destination = new Utf8StringWriter())
90111
{
91112
AppConfigGenerator.Generate(Job.Default, source, destination, Resolver);
92113

93-
AssertAreEqualIgnoringWhitespacesAndCase(customSettings, destination.ToString());
114+
AssertAreEqualIgnoringWhitespacesAndCase(customSettingsAfter, destination.ToString());
94115
}
95116
}
96117

97118
[Theory]
98-
[InlineData(Jit.LegacyJit, "<runtime><useLegacyJit enabled=\"1\" /></runtime>")]
99-
[InlineData(Jit.RyuJit, "<runtime><useLegacyJit enabled=\"0\" /></runtime>")]
119+
[InlineData(Jit.LegacyJit, "<useLegacyJit enabled=\"1\" />")]
120+
[InlineData(Jit.RyuJit, "<useLegacyJit enabled=\"0\" />")]
100121
public void GeneratesRightJitSettings(Jit jit, string expectedRuntimeNode)
101122
{
102123
const string customSettings =
@@ -109,7 +130,7 @@ public void GeneratesRightJitSettings(Jit jit, string expectedRuntimeNode)
109130
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
110131
"<configuration>" +
111132
"<someConfig>withItsValue</someConfig>" +
112-
expectedRuntimeNode +
133+
$"<runtime>{expectedRuntimeNode}{GcSettings}</runtime>" +
113134
"</configuration>" + Environment.NewLine;
114135

115136
using (var source = new StringReader(customSettings))
@@ -133,7 +154,7 @@ public void RemovesStartupSettingsForPrivateBuildsOfClr()
133154
string withoutStartup =
134155
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
135156
"<configuration>" +
136-
"<runtime/>" +
157+
$"<runtime>{GcSettings}</runtime>" +
137158
"</configuration>" + Environment.NewLine;
138159

139160
using (var source = new StringReader(input))
@@ -158,7 +179,7 @@ public void LeavsStartupSettingsIntactForNonPrivateBuildsOfClr()
158179
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
159180
"<configuration>" +
160181
"<startup><supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.6.1\" /></startup>" +
161-
"<runtime/>" +
182+
$"<runtime>{GcSettings}</runtime>" +
162183
"</configuration>" + Environment.NewLine;
163184

164185
using (var source = new StringReader(input))
@@ -186,7 +207,7 @@ public void RewritesCustomAssemblyBindingRedirects()
186207
"</runtime>" +
187208
"</configuration>";
188209

189-
const string settingsWithBindingsAndJit =
210+
string settingsWithBindingsAndJit =
190211
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
191212
"<configuration>" +
192213
"<runtime>" +
@@ -197,6 +218,7 @@ public void RewritesCustomAssemblyBindingRedirects()
197218
"</dependentAssembly>" +
198219
"</assemblyBinding>" +
199220
"<useLegacyJit enabled =\"0\" />" +
221+
GcSettings +
200222
"</runtime>" +
201223
"</configuration>";
202224

@@ -234,6 +256,8 @@ private static string RemoveWhiteSpaces(string input)
234256
}
235257
return buffer.ToString();
236258
}
259+
260+
private static readonly string GcSettings = $"<gcConcurrentenabled=\"{(GCSettings.LatencyMode != GCLatencyMode.Batch).ToLowerCase()}\"/><gcServerenabled=\"{GCSettings.IsServerGC.ToLowerCase()}\"/>";
237261
}
238262

239263
internal class Utf8StringWriter : StringWriter

0 commit comments

Comments
 (0)