Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f2343fb

Browse files
authored
Improve the performance of Environment.WorkingSet in Windows (#26522) (#27212)
* Use win32 api directly for workingset counter * Fix build warnings * Removing useless code * more cleanup * remove size annotation * remove useless comment * Move all the changes to Environment.WorkingSet and remove it from RuntimeEventSourceHelper * removing useless usings * Use kernel32.dll instead of psapi.dll * Code review feedback * Remove newline change * More code review nits
1 parent c2bb306 commit f2343fb

File tree

5 files changed

+65
-24
lines changed

5 files changed

+65
-24
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Kernel32
11+
{
12+
[StructLayout(LayoutKind.Sequential)]
13+
internal struct PROCESS_MEMORY_COUNTERS
14+
{
15+
public uint cb;
16+
public uint PageFaultCount;
17+
public UIntPtr PeakWorkingSetSize;
18+
public UIntPtr WorkingSetSize;
19+
public UIntPtr QuotaPeakPagedPoolUsage;
20+
public UIntPtr QuotaPagedPoolUsage;
21+
public UIntPtr QuotaPeakNonPagedPoolUsage;
22+
public UIntPtr QuotaNonPagedPoolUsage;
23+
public UIntPtr PagefileUsage;
24+
public UIntPtr PeakPagefileUsage;
25+
}
26+
27+
[DllImport(Libraries.Kernel32, EntryPoint="K32GetProcessMemoryInfo")]
28+
internal static extern bool GetProcessMemoryInfo(IntPtr Process, ref PROCESS_MEMORY_COUNTERS ppsmemCounters, uint cb);
29+
}
30+
}

src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@
10471047
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" />
10481048
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" />
10491049
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLogicalDrives.cs" />
1050+
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessMemoryInfo.cs" />
10501051
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessTimes.cs" />
10511052
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs" />
10521053
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />

src/System.Private.CoreLib/shared/System/Environment.Unix.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,5 +444,24 @@ private static int CheckedSysConf(Interop.Sys.SysConfName name)
444444
}
445445
return (int)result;
446446
}
447+
448+
public static long WorkingSet
449+
{
450+
get
451+
{
452+
Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
453+
if (processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) is IDisposable currentProcess)
454+
{
455+
using (currentProcess)
456+
{
457+
object? result = processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null);
458+
if (result is long) return (long)result;
459+
}
460+
}
461+
462+
// Could not get the current working set.
463+
return 0;
464+
}
465+
}
447466
}
448467
}

src/System.Private.CoreLib/shared/System/Environment.Windows.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,20 @@ public static string SystemDirectory
119119
return builder.ToString();
120120
}
121121
}
122+
123+
public static unsafe long WorkingSet
124+
{
125+
get
126+
{
127+
Interop.Kernel32.PROCESS_MEMORY_COUNTERS memoryCounters = default;
128+
memoryCounters.cb = (uint)(sizeof(Interop.Kernel32.PROCESS_MEMORY_COUNTERS));
129+
130+
if (!Interop.Kernel32.GetProcessMemoryInfo(Interop.Kernel32.GetCurrentProcess(), ref memoryCounters, memoryCounters.cb))
131+
{
132+
return 0;
133+
}
134+
return (long)memoryCounters.WorkingSetSize;
135+
}
136+
}
122137
}
123138
}

src/System.Private.CoreLib/shared/System/Environment.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -153,30 +153,6 @@ public static Version Version
153153
}
154154
}
155155

156-
public static long WorkingSet
157-
{
158-
get
159-
{
160-
// Use reflection to access the implementation in System.Diagnostics.Process.dll. While far from ideal,
161-
// we do this to avoid duplicating the Windows, Linux, macOS, and potentially other platform-specific implementations
162-
// present in Process. If it proves important, we could look at separating that functionality out of Process into
163-
// Common files which could also be included here.
164-
Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
165-
IDisposable? currentProcess = processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) as IDisposable;
166-
if (currentProcess != null)
167-
{
168-
using (currentProcess)
169-
{
170-
object? result = processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null);
171-
if (result is long) return (long)result;
172-
}
173-
}
174-
175-
// Could not get the current working set.
176-
return 0;
177-
}
178-
}
179-
180156
private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target)
181157
{
182158
Debug.Assert(target != EnvironmentVariableTarget.Process);

0 commit comments

Comments
 (0)