You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
RFC: Use AdvancedDLSupport instead of P/Invoke or GetFunctionPointerForDelegate #987
It showcases https://github.com/Firwood-Software/AdvancedDLSupport and also benchmarks the individual options, which look quite devastating for the combination GetFunctionPointerForDelegate+Mono and at least "concerning" for the other frameworks.
The library looks really slick and solves exactly our problem while (seemingly) delivering slightly better performance. It has however the catch of being LGPL. While this is perfectly fine for me, this means there might be a problem for applications trying to embed Python.Runtime ahead-of-time compiled or publishing to a closed app-store that prevents you from exchanging the LGPLed library locally.
Additionally, .NET Core lacks a way to set the unmanaged calling convention at runtime, resulting in unreliable results when running as a 32-bit process on Windows. This is, fortunately, a very uncommon configuration.
I don't know how much is this applicable to our users. We should discuss at the next call.
Additionally, reading this I wonder if the calling convention is the reason for crash in #980@Sngmng
This is a pure .NET Core problem AFAIK (and the article is from 2018, it could be that this is fixed on .NET Core 3). Also ADL allows you to choose the style and fall back to delegates for unsupported cases.
Well they mentioned if you dont want LGPL you can still ask them for a custom license:
"For companies, established open-source projects, or individuals wanting a custom license, please chuck us an email."
Additionally, reading this I wonder if the calling convention is the reason for crash in #980@Sngmng
This could be the issue, maybe. Should i try to use LoadLibrary and GetProcAdress instead?
Run my own tests on Windows for DllImport vs GetDelegateForFunctionPointer and results came a bit surprising: GetDelegateForFunctionPointer is 2x faster than DllImport on CoreRT and 2x slower on regular .NET Framework (for a function with no return value and a pointer parameter).
BTW, I prototyped and benchmarked a simple PInvoke generator called EmitBinder, that uses Reflection.Emit (for AOT, Marshal.GetDelegateForFunctionPointer seem to be a necessary fallback). Added it to the benchmark:
Actually, I think the way to go forward is pretty straightforward:
Create a separate package like in the prototype, that exposes more or less the same interface as P/Invoke methods from the current runtime.cs
Begin with Marshal.GetDelegateForFunctionPointer implementation, because it works on all platforms (the only one, that works with CoreRT), has no extra dependencies in .NET Standard 2.0, and is only really slow on .NET Core 3.0 (what a surprise!)
If we have resources for better performance make special implementations with EmitBinder and/or Calli. The later does not have to use AdvancedDLSupport package, as implementation would be very similar to EmitBinder, so probably under 100 lines of code.
Sounds like a good plan, but I think if it's not too much work, we should have all of this directly in the main repository (at least after a bit of experimentation).
I suggest we start a modernisation branch that is supposed to be 2.5 in the end (or 3.0 if we break backwards-compatibility too much) where we do only the following:
Drop clrmodule and monoclr
Drop console
Drop non-.NET Standard build
Restructure the repository into a more standard C# style (projects Python.Runtime, Python.Runtime.Interop, embed and perf tests, helper library for python tests, all of these under a common Python.Test namespace)
Separate the (few) python only parts into a pythonnet directory
That way we can do major changes to the architecture without dropping all of the current PRs. As long as we don't change Python.Runtime itself significantly, most of them can probably be reapplied manually for 2.5.
BTW, I prototyped and benchmarked a simple PInvoke generator called EmitBinder, that uses Reflection.Emit (for AOT, Marshal.GetDelegateForFunctionPointer seem to be a necessary fallback). Added it to the benchmark:
Method Runtime Mean Error StdDev Median Ratio RatioSD
RefCounting_PInvoke .NET 4.8 10.157 ms 0.3036 ms 0.8759 ms 9.977 ms 1.00 0.00
RefCounting_Marshal .NET 4.8 10.026 ms 0.2758 ms 0.8046 ms 9.906 ms 0.99 0.11
RefCounting_Calli .NET 4.8 9.562 ms 0.2809 ms 0.8013 ms 9.319 ms 0.95 0.11
RefCounting_EmitBinder .NET 4.8 11.460 ms 0.3938 ms 1.1612 ms 11.379 ms 1.14 0.13
RefCounting_PInvoke .NET Core 3.0 6.486 ms 0.1296 ms 0.3781 ms 6.455 ms 1.00 0.00
RefCounting_Marshal .NET Core 3.0 13.229 ms 0.2612 ms 0.6553 ms 13.154 ms 2.04 0.14
RefCounting_Calli .NET Core 3.0 8.777 ms 0.2472 ms 0.7288 ms 8.592 ms 1.36 0.14
RefCounting_EmitBinder .NET Core 3.0 7.168 ms 0.1483 ms 0.4374 ms 7.177 ms 1.11 0.10
RefCounting_PInvoke CoreRt 3.0 7.009 ms 0.2007 ms 0.5853 ms 6.853 ms 1.00 0.00
RefCounting_Marshal CoreRt 3.0 7.617 ms 0.3072 ms 0.9011 ms 7.422 ms 1.10 0.16
RefCounting_Calli CoreRt 3.0 NA NA NA NA ? ?
RefCounting_EmitBinder CoreRt 3.0 NA NA NA NA ? ?
#997 @filmor Would u consider removing Reflection.Emit in Modernisation branch?
@sekkit would Unity AOT allow choosing Emit vs non-Emit implementation at runtime? The former is required for better performance.
Reflectoin.Emit is not allowed. The above fork I posted: https://github.com/joonhwan/pythonnet is a AOT compatible implementation. No reflection overhead( fast-call branch)
Well, we can still depend on it for Mono and .NET Core as both implement the __InternalDllImport target. It's just .NET Framework that requires a different approach.
Activity
lostmsu commentedon Nov 13, 2019
I am a bit concerned about
I don't know how much is this applicable to our users. We should discuss at the next call.
Additionally, reading this I wonder if the calling convention is the reason for crash in #980 @Sngmng
filmor commentedon Nov 14, 2019
This is a pure .NET Core problem AFAIK (and the article is from 2018, it could be that this is fixed on .NET Core 3). Also ADL allows you to choose the style and fall back to delegates for unsupported cases.
thesn10 commentedon Nov 14, 2019
Well they mentioned if you dont want LGPL you can still ask them for a custom license:
"For companies, established open-source projects, or individuals wanting a custom license, please chuck us an email."
This could be the issue, maybe. Should i try to use LoadLibrary and GetProcAdress instead?
lostmsu commentedon Nov 14, 2019
@Sngmng you can try fiddling with DllImportAttribute.CallingConvention
lostmsu commentedon Nov 22, 2019
I started prototyping Python C API interop layer: https://github.com/losttech/Python.Runtime.Native/tree/InterfaceBasedApiSets
Run my own tests on Windows for
DllImportvsGetDelegateForFunctionPointerand results came a bit surprising:GetDelegateForFunctionPointeris 2x faster thanDllImporton CoreRT and 2x slower on regular .NET Framework (for a function with no return value and a pointer parameter).lostmsu commentedon Nov 22, 2019
Hm, nevermind, longer reruns normalize it to 1.1-2.0x on all platforms.
For some reason BenchmarkDotNet did not pick up my local Mono installation.
lostmsu commentedon Nov 22, 2019
After some tweaking this is what I got (Calli through AdvancedDLSupport):
filmor commentedon Nov 22, 2019
Thank you very much for running these :)
What is the difference between CoreRt 3.0 and .NET Core 3.0 here?
/edit: nvm, it's clear now :)
0xBEEBAD commentedon Nov 23, 2019
This might solve the problem of Unity AOT compatibility issue
lostmsu commentedon Nov 24, 2019
@sekkit which issue are you referring to?
BTW, I prototyped and benchmarked a simple PInvoke generator called
EmitBinder, that usesReflection.Emit(for AOT,Marshal.GetDelegateForFunctionPointerseem to be a necessary fallback). Added it to the benchmark:lostmsu commentedon Nov 24, 2019
Finally, tested on Linux x64 (this one is in VM, so results might be inaccurate):
lostmsu commentedon Nov 24, 2019
Actually, I think the way to go forward is pretty straightforward:
runtime.csMarshal.GetDelegateForFunctionPointerimplementation, because it works on all platforms (the only one, that works with CoreRT), has no extra dependencies in .NET Standard 2.0, and is only really slow on .NET Core 3.0 (what a surprise!)EmitBinderand/orCalli. The later does not have to useAdvancedDLSupportpackage, as implementation would be very similar toEmitBinder, so probably under 100 lines of code.filmor commentedon Nov 24, 2019
Sounds like a good plan, but I think if it's not too much work, we should have all of this directly in the main repository (at least after a bit of experimentation).
I suggest we start a
modernisationbranch that is supposed to be 2.5 in the end (or 3.0 if we break backwards-compatibility too much) where we do only the following:clrmoduleandmonoclrconsolePython.Runtime,Python.Runtime.Interop, embed and perf tests, helper library for python tests, all of these under a commonPython.Testnamespace)pythonnetdirectoryThat way we can do major changes to the architecture without dropping all of the current PRs. As long as we don't change
Python.Runtimeitself significantly, most of them can probably be reapplied manually for 2.5.filmor commentedon Nov 27, 2019
I started this now, are you okay with me merging your work into the branch?
0xBEEBAD commentedon Nov 28, 2019
#997
@filmor Would u consider removing Reflection.Emit in Modernisation branch?
lostmsu commentedon Nov 28, 2019
@sekkit would Unity AOT allow choosing Emit vs non-Emit implementation at runtime? The former is required for better performance.
0xBEEBAD commentedon Nov 28, 2019
Reflectoin.Emit is not allowed. The above fork I posted: https://github.com/joonhwan/pythonnet is a AOT compatible implementation. No reflection overhead( fast-call branch)
lostmsu commentedon Nov 28, 2019
@sekkit it still relies on
DllImport, which we want to stop depending on.filmor commentedon Nov 28, 2019
Well, we can still depend on it for Mono and .NET Core as both implement the
__InternalDllImporttarget. It's just .NET Framework that requires a different approach.lostmsu commentedon Nov 28, 2019
Sure, go ahead.
But which work are you referring to?
lostmsu commentedon Nov 28, 2019
Wait, on Windows too?
filmor commentedon Nov 28, 2019
Ah, sorry, I again fell for CoreRt vs CoreClr :/
Nevertheless, Mono and CoreRt support
__Internaland CoreClr has at least an open issue on this (https://github.com/dotnet/coreclr/issues/9038) and https://github.com/dotnet/corefx/issues/32015 which (as far as I understand) allows us to just hook into the library loading mechanism to make__Internalpoint at the Python DLL of our liking.filmor commentedon Feb 4, 2021
Since the unmanaged delegate changes are now merged, this one is obsolete.