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

[3.1 port] Improve the performance of Environment.WorkingSet in Windows (#26522) #27212

Merged
merged 1 commit into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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