Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DllImport or LoadLibrary for best performance

I have external .DLL file with fast assembler code inside. What is the best way to call functions in this .DLL file to get best performance?

like image 467
apocalypse Avatar asked May 13 '13 09:05

apocalypse


2 Answers

Your DLL might be in python or c++, whatever , do the same as follow.

This is your DLL file in C++.

header:

extern "C" __declspec(dllexport) int MultiplyByTen(int numberToMultiply);

Source code file

#include "DynamicDLLToCall.h"

int MultiplyByTen(int numberToMultiply)
{
    int returnValue = numberToMultiply * 10;
    return returnValue;
} 

Take a look at the following C# code:

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);
    }
} 
like image 159
Burmese Bug Avatar answered Oct 13 '22 01:10

Burmese Bug


Did a quick test. Scroll down for the conclusion.

Header:

struct Vector2
{
public:
    float X;
    float Y;

    float GetMagnitude() const;
};

extern "C" __declspec(dllexport) float GetMagnitude(const Vector2& InVector);

Source:

#include <cmath>
float Vector2::GetMagnitude() const
{
    return sqrt((X * X) + (Y * Y));
}

Managed:

// #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
        }
    }
}

Conclusion

The one where you manually load/unload DLL is about 20% slower. DllImport took about 99-105 milliseconds on different tries. Marshal.GetDelegateForFuncitonPointer took about 120-125 milliseconds on different tries.

like image 26
somename Avatar answered Oct 13 '22 01:10

somename