【问题标题】:DllImport or LoadLibrary for best performanceDllImport 或 LoadLibrary 以获得最佳性能
【发布时间】:2013-05-07 07:16:57
【问题描述】:

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

【问题讨论】:

  • 这些 big 方法是只被调用几次,还是这些 slim 方法经常从托管代码中调用?
  • 我记得SharpDX 的人分析了DLLImport 生成的代码,最大的性能问题是某种(不需要的)参数检查。由于这个事实,他们使用Reflection.Emit() 生成与DLLImport 相同的代码,但没有进行检查,从而提高了性能。我认为这是它的一位创作者的博客文章,但我现在找不到。
  • @Olivier,这些方法例如填充 1024 字节缓冲区。

标签: c# dllimport loadlibrary


【解决方案1】:

你的 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);
    }
} 

【讨论】:

【解决方案2】:

假设您的目标平台与所说的本机 dll 相同。您可以使用 DLLImport 来调用 LoadLibrary 并使用 LoadLibrary 将本机 dll 加载到您的进程中。然后使用 DllImport 调用 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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • “实际上,您也可以动态加载本机程序集”。显然@zgnilec 已经知道这一点。
    • 链接已损坏。
    【解决方案4】:

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

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

    【讨论】:

    • 我会检查这个,也许我没有把问题说得太清楚,我要求使用外部 DDL 的不同方法。我“知道”只有这两个。我在某处读到 DllImport 在里面做一些事情,比如为垃圾收集器固定托管对象等。我认为有一种特殊的方法可以调用本机汇编程序方法。如果我要让 dll 提供函数 y = x^2 (只是示例),然后使用 DllImport 调用它,这将在 DllImport 机制上产生 99% 的 cpu,然后为我的汇编程序函数提供 1%,那么使用外部 dll 是没有意义的: /
    【解决方案5】:

    做了一个快速测试。向下滚动查看结论。

    标题:

    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 毫秒。

    【讨论】:

      猜你喜欢
      • 2018-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 1970-01-01
      • 2022-01-23
      • 2013-09-11
      • 1970-01-01
      相关资源
      最近更新 更多