9

我有内部带有快速汇编代码的外部 .DLL 文件。调用此 .DLL 文件中的函数以获得最佳性能的最佳方法是什么?

4

5 回答 5

21

您的 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);
    }
} 
于 2014-05-13T05:12:12.117 回答
3

假设您的目标平台与所说的本机 dll 相同。您可以使用 DLLImport 来调用 LoadLibrary 并使用 LoadLibrary 将本机 dll 加载到您的进程中。然后使用 DllImport pinvoke GetProcAddress。

然后,您可以为要调用的所述 dll 中导出的所有方法定义委托。

接下来,您使用 Marshal.GetDelegateForFunctionPointer 从 GetProcAddress 设置您的委托。

您创建一个静态类,它在构造函数中执行此操作一次。然后,您可以调用您的委托来调用 dll 中的本机导出函数,而无需在所有内容上使用 DllImport。更干净,而且我很确定它要快得多,并且可能会在提到的参数检查之前完全绕过。

所以你会有一个缓慢的初始化,但一旦加载,就会快速运行 imo。没有测试过这个。

这是来自我的来源的博客。

http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx

于 2013-05-24T21:53:04.700 回答
3

我认为 DLLImport 和 LoadLibrary 有不同的目标。如果您使用本机 .dll,则应使用 DllImport。如果您使用 .NET 程序集,则应使用 LoadAssembly。

实际上,您也可以动态加载本机程序集,请参见此示例: dynamic-calling-an-unmanaged-dll-from-.net

于 2013-05-13T11:01:39.580 回答
2

回答这个问题的唯一方法是为这两个选项计时,这是一项非常容易的任务。在没有时间的情况下进行性能预测是没有意义的。

由于我们没有您的代码,因此只有您可以回答您的问题。

于 2013-05-13T11:06:06.483 回答
1

做了一个快速测试。向下滚动以得出结论。

标题:

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 的速度大约慢 20%。DllImport 在不同的尝试中花费了大约 99-105 毫秒。Marshal.GetDelegateForFuncitonPointer 在不同的尝试中花费了大约 120-125 毫秒。

于 2021-02-23T02:47:22.440 回答