加速C#原生调用pinvoke
Speed up C# native call pinvoke
mod 注意:我不认为这是重复的,因为我已经尝试了下面描述的几种提到的解决方案。
有什么办法可以加快速度吗?我已经在这方面遵循了 Microsoft 指南,这是我所做的:
- 添加了 SuppressUnmanagedCodeSecurity
- 将其放在名为 UnsafeNativeMethods.cs
的文件中
- 为方法存根定义特定类型
方法如下:
[DllImport("kernel32.dll")]
[SuppressUnmanagedCodeSecurity]
public static extern bool DeviceIoControl(
IntPtr hDevice,
uint IoControlCode,
[In] MemoryManager.MemOperation InBuffer,
int nInBufferSize,
[Out] byte[] OutBuffer,
uint nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped
);
这是 MemOperation 的内容(我猜是必须编组的输入缓冲区):
public struct MemOperation
{
public int Pid;
public int UserPid;
public int Size;
public int protection_mode;
public int allocation_type;
public IntPtr Addr;
public IntPtr WriteBuffer;
[MarshalAs(UnmanagedType.LPWStr)]
public string module_selection;
}
这是一个用法示例:
public UnsafeNativeMethods.MEMORY_BASIC_INFORMATION QueryVirtualMemory(IntPtr address) {
var memOperation = new MemOperation();
byte[] buffer = new byte[48]; // 8 + 8 + 4 + 8 + 4 + 4 + 4 MEMORY_BASIC_INFORMATION
memOperation.Pid = this.Pid;
memOperation.Addr = address;
int bytes = 0;
bool res = UnsafeNativeMethods.DeviceIoControl(this.Handle, CtlCode(0x00000022, this.IOCTL_QUERY, 2, 0), memOperation, Marshal.SizeOf(memOperation), buffer, (uint)buffer.Length, ref bytes, IntPtr.Zero);
return GetStructure<UnsafeNativeMethods.MEMORY_BASIC_INFORMATION>(buffer);
}
在探查器中,我的热路径是 pinvoke。我的应用程序运行得非常快,我认为它可以在 C# 中运行。然而,由于应用程序正在执行多少内存操作,几乎所有执行时间的三分之一都花在了调用上。我想以任何可能的方式缩短这段时间,包括不安全的方式。
我看到您可以改为编写 DeviceIoControl 包装器并从 C++ dll 导入它,但这对我来说没有任何改变,它的功能似乎完全相同。这是来源:
devicecontrol.cpp
#include <iostream>
#include "DeviceControl.h"
bool __cdecl DeviceIoRequestWrapper(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlappedk)
{
return DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlappedk);
}
devicecontrol.h
#pragma once
#include <Windows.h>
extern "C" {
__declspec(dllexport) bool __cdecl DeviceIoRequestWrapper(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlappedk);
}
我正在使用 .net 6.0 和最新版本的 C#。
建议更改输出缓冲区:
public unsafe UnsafeNativeMethods.MEMORY_BASIC_INFORMATION QueryVirtualMemory(IntPtr address) {
var memOperation = new MemOperation();
byte* buffer = stackalloc byte[48];
memOperation.Pid = this.Pid;
memOperation.Addr = address;
int bytes = 0;
bool res = UnsafeNativeMethods.DeviceIoControl(this.Handle, CtlCode(0x00000022, this.IOCTL_QUERY, 2, 0), memOperation, Marshal.SizeOf(memOperation), (IntPtr)buffer, 48, ref bytes, IntPtr.Zero);
return GetStructure<UnsafeNativeMethods.MEMORY_BASIC_INFORMATION>(buffer);
}
GetStructure 现在在哪里:
public static unsafe T GetStructure<T>(byte* bytes) where T: unmanaged {
T structure = *(T*)bytes;
return structure;
}
以下是最有帮助的内容,我会在几个小时后接受它作为我自己的主题的答案。
除了我提到的上述步骤外,我现在还做了以下工作:
- 已将
MemOperation
转换为完全 blittable。字符串“module_selection”在每次程序启动时只使用过一次,所以我现在添加了一个完全 blittable 版本的 MemOperation
。谢谢@Flydog57
- 将缓冲区更改为快速分配(非清零),并传递 PTR,而不是分配然后将字节 [] 编组到 void/char*(我认为这可能发生在你传递给它一个 byte[] 对象)。谢谢@Charlieface
运行 visual studio 中的分析器,它似乎已经消除了热路径。现在总 CPU % 正确位于 kernel32.dll 内(对设备的 deviceiocontrol 调用)。这是否会带来切实的性能优势,我不知道,没有为此设置完整基准的简单方法。
mod 注意:我不认为这是重复的,因为我已经尝试了下面描述的几种提到的解决方案。
有什么办法可以加快速度吗?我已经在这方面遵循了 Microsoft 指南,这是我所做的:
- 添加了 SuppressUnmanagedCodeSecurity
- 将其放在名为 UnsafeNativeMethods.cs 的文件中
- 为方法存根定义特定类型
方法如下:
[DllImport("kernel32.dll")]
[SuppressUnmanagedCodeSecurity]
public static extern bool DeviceIoControl(
IntPtr hDevice,
uint IoControlCode,
[In] MemoryManager.MemOperation InBuffer,
int nInBufferSize,
[Out] byte[] OutBuffer,
uint nOutBufferSize,
ref int pBytesReturned,
IntPtr Overlapped
);
这是 MemOperation 的内容(我猜是必须编组的输入缓冲区):
public struct MemOperation
{
public int Pid;
public int UserPid;
public int Size;
public int protection_mode;
public int allocation_type;
public IntPtr Addr;
public IntPtr WriteBuffer;
[MarshalAs(UnmanagedType.LPWStr)]
public string module_selection;
}
这是一个用法示例:
public UnsafeNativeMethods.MEMORY_BASIC_INFORMATION QueryVirtualMemory(IntPtr address) {
var memOperation = new MemOperation();
byte[] buffer = new byte[48]; // 8 + 8 + 4 + 8 + 4 + 4 + 4 MEMORY_BASIC_INFORMATION
memOperation.Pid = this.Pid;
memOperation.Addr = address;
int bytes = 0;
bool res = UnsafeNativeMethods.DeviceIoControl(this.Handle, CtlCode(0x00000022, this.IOCTL_QUERY, 2, 0), memOperation, Marshal.SizeOf(memOperation), buffer, (uint)buffer.Length, ref bytes, IntPtr.Zero);
return GetStructure<UnsafeNativeMethods.MEMORY_BASIC_INFORMATION>(buffer);
}
在探查器中,我的热路径是 pinvoke。我的应用程序运行得非常快,我认为它可以在 C# 中运行。然而,由于应用程序正在执行多少内存操作,几乎所有执行时间的三分之一都花在了调用上。我想以任何可能的方式缩短这段时间,包括不安全的方式。
我看到您可以改为编写 DeviceIoControl 包装器并从 C++ dll 导入它,但这对我来说没有任何改变,它的功能似乎完全相同。这是来源:
devicecontrol.cpp
#include <iostream>
#include "DeviceControl.h"
bool __cdecl DeviceIoRequestWrapper(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlappedk)
{
return DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned, lpOverlappedk);
}
devicecontrol.h
#pragma once
#include <Windows.h>
extern "C" {
__declspec(dllexport) bool __cdecl DeviceIoRequestWrapper(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlappedk);
}
我正在使用 .net 6.0 和最新版本的 C#。
建议更改输出缓冲区:
public unsafe UnsafeNativeMethods.MEMORY_BASIC_INFORMATION QueryVirtualMemory(IntPtr address) {
var memOperation = new MemOperation();
byte* buffer = stackalloc byte[48];
memOperation.Pid = this.Pid;
memOperation.Addr = address;
int bytes = 0;
bool res = UnsafeNativeMethods.DeviceIoControl(this.Handle, CtlCode(0x00000022, this.IOCTL_QUERY, 2, 0), memOperation, Marshal.SizeOf(memOperation), (IntPtr)buffer, 48, ref bytes, IntPtr.Zero);
return GetStructure<UnsafeNativeMethods.MEMORY_BASIC_INFORMATION>(buffer);
}
GetStructure 现在在哪里:
public static unsafe T GetStructure<T>(byte* bytes) where T: unmanaged {
T structure = *(T*)bytes;
return structure;
}
以下是最有帮助的内容,我会在几个小时后接受它作为我自己的主题的答案。
除了我提到的上述步骤外,我现在还做了以下工作:
- 已将
MemOperation
转换为完全 blittable。字符串“module_selection”在每次程序启动时只使用过一次,所以我现在添加了一个完全 blittable 版本的MemOperation
。谢谢@Flydog57 - 将缓冲区更改为快速分配(非清零),并传递 PTR,而不是分配然后将字节 [] 编组到 void/char*(我认为这可能发生在你传递给它一个 byte[] 对象)。谢谢@Charlieface
运行 visual studio 中的分析器,它似乎已经消除了热路径。现在总 CPU % 正确位于 kernel32.dll 内(对设备的 deviceiocontrol 调用)。这是否会带来切实的性能优势,我不知道,没有为此设置完整基准的简单方法。