In C, there is a function that accepts a pointer to a function to perform comparison:
在C中,有一个函数接受一个指向函数的指针来执行比较:
[DllImport("mylibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int set_compare(IntPtr id, MarshalAs(UnmanagedType.FunctionPtr)]CompareFunction cmp);
In C#, a delegate is passed to the C function:
在C#中,委托被传递给C函数:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CompareFunction(ref IntPtr left, ref IntPtr right);
Currently, I accept Func<T,T,int> comparer
in a constructor of a generic class and convert it to the delegate. "mylibrary.dll" owns data, managed C# library knows how to convert pointers to T
and then compare T
s.
目前,我在泛型类的构造函数中接受Func
//.in ctor
CompareFunction cmpFunc = (ref IntPtr left, ref IntPtr right) => {
var l = GenericFromPointer<T>(left);
var r = GenericFromPointer<T>(right);
return comparer(l, r);
};
I also have an option to write a CompareFunction in C for most important data types that are used in 90%+ cases, but I hope to avoid modifications to the native library.
对于在90%以上的情况下使用的大多数重要数据类型,我还可以选择在C中编写CompareFunction,但我希望避免修改本机库。
The question is, when setting the compare function with P/Invoke, does every subsequent call to that function from C code incurs marshaling overheads, or the delegate is called from C as if it was initially written in C?
问题是,当用P / Invoke设置比较函数时,从C代码对该函数的每次后续调用是否会产生编组开销,或者从C调用委托,就好像它最初是用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?
我想,在编译时,委托是内存中的一系列机器指令,但不明白是否/为什么C代码需要让.NET进行实际比较,而不仅仅是执行这些指令?
I am mostly interested in better understanding how interop works. However, this delegate is used for binary search on big data sets, and if every subsequent call has some overheads as a single P/Invoke, rewriting comparers in native C could be a good option.
我最感兴趣的是更好地理解互操作是如何工作的。但是,此委托用于对大数据集进行二进制搜索,如果每个后续调用都有一些开销作为单个P / Invoke,则在本机C中重写比较器可能是一个不错的选择。
1 个解决方案
#1
3
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?
我想,在编译时,委托是内存中的一系列机器指令,但不明白是否/为什么C代码需要让.NET进行实际比较,而不仅仅是执行这些指令?
I guess you're a bit confused about how .NET works. C doesn't ask .NET to execute code.
我猜你对.NET的运作方式有点困惑。 C不要求.NET执行代码。
First, your lambda is turned into a compiler-generated class instance (because you're closing over the comparer
variable), and then a delegate to a method of this class is used. And it's an instance method since your lambda is a closure.
首先,将lambda转换为编译器生成的类实例(因为您将关闭比较器变量),然后使用该类的方法的委托。它是一个实例方法,因为你的lambda是一个闭包。
A delegate is similar to a function pointer. So, like you say, it points to executable code. Whether this code is generated from a C source or a .NET source is irrelevant at this point.
委托类似于函数指针。所以,就像你说的,它指向可执行代码。此代码是从C源代码生成还是从.NET源生成此代码无关紧要。
It's in the interop case when this starts to matter. P/Invoke won't pass your delegate as-is as a function pointer to C code. It will pass a function pointer to a thunk which calls the delegate. Visual Studio will display this as a [Native to Managed Transition] stack frame. This is needed for different reasons such as marshaling or passing additional parameters (like the instance of the class backing your lambda for instance).
当这开始变得重要时,它处于互操作的情况下。 P / Invoke不会将您的委托原样作为C代码的函数指针传递。它会将一个函数指针传递给调用委托的thunk。 Visual Studio将此显示为[Native to Managed Transition]堆栈帧。这是出于不同的原因需要的,例如编组或传递其他参数(例如支持lambda的类的实例)。
As to the performance considerations of this, here's what MSDN says, quite obviously:
至于性能方面的考虑,这就是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.
置信转换。无论使用何种互操作性技术,每次托管函数调用本机函数时都需要特殊的转换序列(称为thunks),反之亦然。由于thunking有助于在托管代码和本机代码之间进行互操作所需的总时间,因此这些转换的累积会对性能产生负面影响。
So, if your code requires a lot of transitions between managed and native code, you should get better performance by doing your comparisons on the C side if possible, so that you avoid the transitions.
因此,如果您的代码需要在托管代码和本机代码之间进行大量转换,则应尽可能通过在C端进行比较来获得更好的性能,以避免转换。
#1
3
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?
我想,在编译时,委托是内存中的一系列机器指令,但不明白是否/为什么C代码需要让.NET进行实际比较,而不仅仅是执行这些指令?
I guess you're a bit confused about how .NET works. C doesn't ask .NET to execute code.
我猜你对.NET的运作方式有点困惑。 C不要求.NET执行代码。
First, your lambda is turned into a compiler-generated class instance (because you're closing over the comparer
variable), and then a delegate to a method of this class is used. And it's an instance method since your lambda is a closure.
首先,将lambda转换为编译器生成的类实例(因为您将关闭比较器变量),然后使用该类的方法的委托。它是一个实例方法,因为你的lambda是一个闭包。
A delegate is similar to a function pointer. So, like you say, it points to executable code. Whether this code is generated from a C source or a .NET source is irrelevant at this point.
委托类似于函数指针。所以,就像你说的,它指向可执行代码。此代码是从C源代码生成还是从.NET源生成此代码无关紧要。
It's in the interop case when this starts to matter. P/Invoke won't pass your delegate as-is as a function pointer to C code. It will pass a function pointer to a thunk which calls the delegate. Visual Studio will display this as a [Native to Managed Transition] stack frame. This is needed for different reasons such as marshaling or passing additional parameters (like the instance of the class backing your lambda for instance).
当这开始变得重要时,它处于互操作的情况下。 P / Invoke不会将您的委托原样作为C代码的函数指针传递。它会将一个函数指针传递给调用委托的thunk。 Visual Studio将此显示为[Native to Managed Transition]堆栈帧。这是出于不同的原因需要的,例如编组或传递其他参数(例如支持lambda的类的实例)。
As to the performance considerations of this, here's what MSDN says, quite obviously:
至于性能方面的考虑,这就是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.
置信转换。无论使用何种互操作性技术,每次托管函数调用本机函数时都需要特殊的转换序列(称为thunks),反之亦然。由于thunking有助于在托管代码和本机代码之间进行互操作所需的总时间,因此这些转换的累积会对性能产生负面影响。
So, if your code requires a lot of transitions between managed and native code, you should get better performance by doing your comparisons on the C side if possible, so that you avoid the transitions.
因此,如果您的代码需要在托管代码和本机代码之间进行大量转换,则应尽可能通过在C端进行比较来获得更好的性能,以避免转换。