我有一个包含快速汇编代码的外部.DLL文件。调用这个.DLL文件中的函数以获得最佳性能的最佳方法是什么?
我有一个包含快速汇编代码的外部.DLL文件。调用这个.DLL文件中的函数以获得最佳性能的最佳方法是什么?
您的DLL可能是用Python或C++编写的,无论哪种语言,请按照以下步骤操作:
这是您在C++中的DLL文件。
头文件:
extern "C" __declspec(dllexport) int MultiplyByTen(int numberToMultiply);
源代码文件
#include "DynamicDLLToCall.h"
int MultiplyByTen(int numberToMultiply)
{
int returnValue = numberToMultiply * 10;
return returnValue;
}
请看下面的C#代码:
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MultiplyByTen(int numberToMultiply);
static void Main(string[] args)
{
IntPtr pDll = NativeMethods.LoadLibrary(@"PathToYourDll.DLL");
//oh dear, error handling here
//if (pDll == IntPtr.Zero)
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "MultiplyByTen");
//oh dear, error handling here
//if(pAddressOfFunctionToCall == IntPtr.Zero)
MultiplyByTen multiplyByTen = (MultiplyByTen)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(MultiplyByTen));
int theResult = multiplyByTen(10);
bool result = NativeMethods.FreeLibrary(pDll);
//remaining code here
Console.WriteLine(theResult);
}
}
回答这个问题的唯一方法是对两个选项进行计时,这是一个极为简单的任务。没有计时的性能预测是毫无意义的。
由于我们没有您的代码,只有您自己才能回答您的问题。
我进行了一个快速测试。向下滚动查看结论。
标题:
struct Vector2
{
public:
float X;
float Y;
float GetMagnitude() const;
};
extern "C" __declspec(dllexport) float GetMagnitude(const Vector2& InVector);
来源:
#include <cmath>
float Vector2::GetMagnitude() const
{
return sqrt((X * X) + (Y * Y));
}
管理:
// #define IMPORT // <-- comment/uncomment this to switch
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
namespace InteropTest
{
public struct Vector2
{
public Vector2(float x, float y)
{
(_x, _y) = (x, y);
}
private float _x;
private float _y;
}
[SuppressUnmanagedCodeSecurity]
internal class Program
{
#if IMPORT
[DllImport("InteropLibrary", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
private static extern float GetMagnitude(ref Vector2 vector);
#else
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(
string path);
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(
IntPtr libraryHandle,
string symbolName);
[DllImport("kernel32")]
public static extern bool FreeLibrary(
IntPtr libraryHandle);
private static IntPtr LibraryHandle;
[UnmanagedFunctionPointer(CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
private delegate float GetMagnitudeDelegate(ref Vector2 vector2);
private static GetMagnitudeDelegate GetMagnitude;
#endif
public static void Main(string[] args)
{
#if !IMPORT
LibraryHandle = LoadLibrary("./InteropLibrary.dll");
IntPtr symbol = GetProcAddress(LibraryHandle, "GetMagnitude");
GetMagnitude = Marshal.GetDelegateForFunctionPointer(
symbol,
typeof(GetMagnitudeDelegate)) as GetMagnitudeDelegate;
#endif
var random = new Random(234);
var sw = new Stopwatch();
sw.Start();
{
for (var i = 0; i < 1000000; i++)
{
var vector = new Vector2(random.Next(400), random.Next(400));
GetMagnitude(ref vector);
}
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
sw = null;
random = null;
#if !IMPORT
CloseLibrary(LibraryHandle);
LibraryHandle = IntPtr.Zero;
GetMagnitude = null;
#endif
}
}
}
结论
手动加载/卸载DLL的方式大约比DllImport慢20%。 在不同尝试中,DllImport需要约99-105毫秒。 在不同尝试中,Marshal.GetDelegateForFuncitonPointer需要约120-125毫秒。
DllImport
属性几乎没有什么区别。为了简单起见,我会继续使用 DllImport
。 - baltermia
DLLImport
生成的代码,发现最大的性能问题是某种(不必要的)参数检查。因此,他们使用Reflection.Emit()
来生成与DLLImport
相同但没有这些检查的代码,从而提高了性能。我认为这是其中一位创作者的博客文章,但我目前找不到它。 - Oliver