作为 UnmanagedFunctionPointer 传递给 C 的函数在哪里执行?
Where function passed as UnmanagedFunctionPointer to C is executed?
在C语言中,有一个函数接受一个指向函数的指针来进行比较:
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int set_compare(IntPtr id, MarshalAs(UnmanagedType.FunctionPtr)]CompareFunction cmp);
在 C# 中,将委托传递给 C 函数:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CompareFunction(ref IntPtr left, ref IntPtr right);
目前,我在泛型 class 的构造函数中接受 Func<T,T,int> comparer
并将其转换为委托。 "mylibrary.dll" 拥有数据,托管 C# 库知道如何将指针转换为 T
然后比较 T
s.
//.in ctor
CompareFunction cmpFunc = (ref IntPtr left, ref IntPtr right) => {
var l = GenericFromPointer<T>(left);
var r = GenericFromPointer<T>(right);
return comparer(l, r);
};
我还可以选择用 C 语言为 90% 以上情况下使用的大多数重要数据类型编写 CompareFunction,但我希望避免修改本机库。
问题是,当使用 P/Invoke 设置比较函数时,是否每次从 C 代码对该函数的后续调用都会产生封送处理开销,或者委托是从 C 调用的,就好像它最初是用 C 编写的一样?
我想,在编译时,委托是内存中的一系列机器指令,但不理解if/why C 代码需要让 .NET 进行实际比较,而不是仅仅执行这些说明到位了吗?
我最感兴趣的是更好地了解互操作的工作原理。但是,此委托用于大数据集的二进制搜索,如果每个后续调用都有一些开销作为单个 P/Invoke,用原生 C 重写比较器可能是一个不错的选择。
I imagine that, when compiled, the delegate is a sequence of machine instructions in memory, but do not understand if/why C code would need to ask .NET to make the actual comparison, instead of just executing these instructions in place?
我猜您对 .NET 的工作原理有点困惑。 C 不会要求.NET 来执行代码。
首先,您的 lambda 被转换为编译器生成的 class 实例(因为您正在关闭 comparer
变量),然后是此 [=36] 方法的委托=]被使用。这是一个实例方法,因为您的 lambda 是一个闭包。
委托类似于函数指针。所以,就像你说的,它指向可执行代码。此代码是从 C 源代码还是从 .NET 源代码生成是无关紧要的此时。
在互操作情况下,这开始变得重要。 P/Invoke 不会将您的委托作为指向 C 代码的函数指针按原样传递。它会将函数指针传递给调用委托的 thunk。 Visual Studio 会将其显示为 [从本地到托管的转换] 堆栈框架。这是出于不同的原因需要的,例如封送处理或传递附加参数(例如 class 支持您的 lambda 的实例)。
至于性能方面的考虑,这里是MSDN所说的,很明显:
Thunking. Regardless of the interoperability technique used, special transition sequences, which are known as thunks, are required each time a managed function calls an native function, and vice-versa. Because thunking contributes to the overall time that it takes to interoperate between managed code and native code, the accumulation of these transitions can negatively affect performance.
因此,如果您的代码需要在托管代码和本机代码之间进行大量转换,您应该尽可能通过在 C 端进行比较来获得更好的性能,从而避免转换。
在C语言中,有一个函数接受一个指向函数的指针来进行比较:
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int set_compare(IntPtr id, MarshalAs(UnmanagedType.FunctionPtr)]CompareFunction cmp);
在 C# 中,将委托传递给 C 函数:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CompareFunction(ref IntPtr left, ref IntPtr right);
目前,我在泛型 class 的构造函数中接受 Func<T,T,int> comparer
并将其转换为委托。 "mylibrary.dll" 拥有数据,托管 C# 库知道如何将指针转换为 T
然后比较 T
s.
//.in ctor
CompareFunction cmpFunc = (ref IntPtr left, ref IntPtr right) => {
var l = GenericFromPointer<T>(left);
var r = GenericFromPointer<T>(right);
return comparer(l, r);
};
我还可以选择用 C 语言为 90% 以上情况下使用的大多数重要数据类型编写 CompareFunction,但我希望避免修改本机库。
问题是,当使用 P/Invoke 设置比较函数时,是否每次从 C 代码对该函数的后续调用都会产生封送处理开销,或者委托是从 C 调用的,就好像它最初是用 C 编写的一样?
我想,在编译时,委托是内存中的一系列机器指令,但不理解if/why C 代码需要让 .NET 进行实际比较,而不是仅仅执行这些说明到位了吗?
我最感兴趣的是更好地了解互操作的工作原理。但是,此委托用于大数据集的二进制搜索,如果每个后续调用都有一些开销作为单个 P/Invoke,用原生 C 重写比较器可能是一个不错的选择。
I imagine that, when compiled, the delegate is a sequence of machine instructions in memory, but do not understand if/why C code would need to ask .NET to make the actual comparison, instead of just executing these instructions in place?
我猜您对 .NET 的工作原理有点困惑。 C 不会要求.NET 来执行代码。
首先,您的 lambda 被转换为编译器生成的 class 实例(因为您正在关闭 comparer
变量),然后是此 [=36] 方法的委托=]被使用。这是一个实例方法,因为您的 lambda 是一个闭包。
委托类似于函数指针。所以,就像你说的,它指向可执行代码。此代码是从 C 源代码还是从 .NET 源代码生成是无关紧要的此时。
在互操作情况下,这开始变得重要。 P/Invoke 不会将您的委托作为指向 C 代码的函数指针按原样传递。它会将函数指针传递给调用委托的 thunk。 Visual Studio 会将其显示为 [从本地到托管的转换] 堆栈框架。这是出于不同的原因需要的,例如封送处理或传递附加参数(例如 class 支持您的 lambda 的实例)。
至于性能方面的考虑,这里是MSDN所说的,很明显:
Thunking. Regardless of the interoperability technique used, special transition sequences, which are known as thunks, are required each time a managed function calls an native function, and vice-versa. Because thunking contributes to the overall time that it takes to interoperate between managed code and native code, the accumulation of these transitions can negatively affect performance.
因此,如果您的代码需要在托管代码和本机代码之间进行大量转换,您应该尽可能通过在 C 端进行比较来获得更好的性能,从而避免转换。