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?
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);
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With