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

Commit

Permalink
Improve the performance of Environment.WorkingSet in Windows (#26522) (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
sywhang authored Oct 16, 2019
1 parent c2bb306 commit f2343fb
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal partial class Interop
{
internal partial class Kernel32
{
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_MEMORY_COUNTERS
{
public uint cb;
public uint PageFaultCount;
public UIntPtr PeakWorkingSetSize;
public UIntPtr WorkingSetSize;
public UIntPtr QuotaPeakPagedPoolUsage;
public UIntPtr QuotaPagedPoolUsage;
public UIntPtr QuotaPeakNonPagedPoolUsage;
public UIntPtr QuotaNonPagedPoolUsage;
public UIntPtr PagefileUsage;
public UIntPtr PeakPagefileUsage;
}

[DllImport(Libraries.Kernel32, EntryPoint="K32GetProcessMemoryInfo")]
internal static extern bool GetProcessMemoryInfo(IntPtr Process, ref PROCESS_MEMORY_COUNTERS ppsmemCounters, uint cb);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetFullPathNameW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLongPathNameW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetLogicalDrives.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessMemoryInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessTimes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />
Expand Down
19 changes: 19 additions & 0 deletions src/System.Private.CoreLib/shared/System/Environment.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -444,5 +444,24 @@ private static int CheckedSysConf(Interop.Sys.SysConfName name)
}
return (int)result;
}

public static long WorkingSet
{
get
{
Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
if (processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) is IDisposable currentProcess)
{
using (currentProcess)
{
object? result = processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null);
if (result is long) return (long)result;
}
}

// Could not get the current working set.
return 0;
}
}
}
}
15 changes: 15 additions & 0 deletions src/System.Private.CoreLib/shared/System/Environment.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,20 @@ public static string SystemDirectory
return builder.ToString();
}
}

public static unsafe long WorkingSet
{
get
{
Interop.Kernel32.PROCESS_MEMORY_COUNTERS memoryCounters = default;
memoryCounters.cb = (uint)(sizeof(Interop.Kernel32.PROCESS_MEMORY_COUNTERS));

if (!Interop.Kernel32.GetProcessMemoryInfo(Interop.Kernel32.GetCurrentProcess(), ref memoryCounters, memoryCounters.cb))
{
return 0;
}
return (long)memoryCounters.WorkingSetSize;
}
}
}
}
24 changes: 0 additions & 24 deletions src/System.Private.CoreLib/shared/System/Environment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,30 +153,6 @@ public static Version Version
}
}

public static long WorkingSet
{
get
{
// Use reflection to access the implementation in System.Diagnostics.Process.dll. While far from ideal,
// we do this to avoid duplicating the Windows, Linux, macOS, and potentially other platform-specific implementations
// present in Process. If it proves important, we could look at separating that functionality out of Process into
// Common files which could also be included here.
Type? processType = Type.GetType("System.Diagnostics.Process, System.Diagnostics.Process, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
IDisposable? currentProcess = processType?.GetMethod("GetCurrentProcess")?.Invoke(null, BindingFlags.DoNotWrapExceptions, null, null, null) as IDisposable;
if (currentProcess != null)
{
using (currentProcess)
{
object? result = processType!.GetMethod("get_WorkingSet64")?.Invoke(currentProcess, BindingFlags.DoNotWrapExceptions, null, null, null);
if (result is long) return (long)result;
}
}

// Could not get the current working set.
return 0;
}
}

private static bool ValidateAndConvertRegistryTarget(EnvironmentVariableTarget target)
{
Debug.Assert(target != EnvironmentVariableTarget.Process);
Expand Down

0 comments on commit f2343fb

Please sign in to comment.