.NET 7์ LibraryImport ํน์ฑ์ด ์ถ๊ฐ๋์์ต๋๋ค. ๊ธฐ์กด DllImport ํน์ฑ์ผ๋ก ๋ค์์ DllImport ์ฅ์์
[DllImport("Kernel32.dll")]
public extern static bool Beep(uint freq, uint duration);
๋ค์์ LibraryImport ํน์ฑ์ผ๋ก ์ฅ์ํ ์ ์์ต๋๋ค.
[LibraryImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool Beep(uint freq, uint duration);
LibraryImport๊ฐ ์๋ก ์๊ฒจ๋ ์ด์ ๋ DllImport๊ฐ ๋ง์ฌ๋ง์ ๋ฐํ์์์ ์ํํด์ IL ์ฝ๋๋ฅผ emitํ๋ค๊ณ ํ๋๋ฐ NativeAOT ๋ฑ ๋์ ์ผ๋ก IL ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ ํ๊ฒฝ์์ ์ธ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
LibraryImport๋ ์์ค ์์ฑ๊ธฐ ๊ธฐ๋ฅ์ ์ด์ฉํด ์ปดํ์ผ ์์ ์์ ๋ง์ฌ๋ง ์ฝ๋๋ฅผ ์ฝ์ ํฉ๋๋ค.
obj/Debug/net7.0/generated/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs
(csproj์ EmitCompilerGeneratedFiles ์ค์ ์ true๋ก ์ค์ผ ํด๋น ๊ฒฝ๋ก์ ํ์ผ๋ก ์์ฑ๋์ด ํ์ธํ ์ ์์ต๋๋ค.)
// <auto-generated/>
static unsafe partial class ExternDll
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.6.42610")]
[System.Runtime.CompilerServices.SkipLocalsInitAttribute]
public static partial bool Beep(uint freq, uint duration)
{
bool __retVal;
int __retVal_native;
{
__retVal_native = __PInvoke(freq, duration);
}
// Unmarshal - Convert native data to managed data.
__retVal = __retVal_native != 0;
return __retVal;
// Local P/Invoke
[System.Runtime.InteropServices.DllImportAttribute("Kernel32.dll", EntryPoint = "Beep", ExactSpelling = true)]
static extern unsafe int __PInvoke(uint freq, uint duration);
}
}
๋ญ๊ฐ ์ฝ๋๊ฐ ์ฝ์ ๋๊ณ ์ฌ์ง์ด ์ต์ข ๋ค์ DllImport๋ฅผ ์ฌ์ฉํ๋๊ตฐ์? ๊ฒฐ๊ตญ์ LibraryImport ํน์ฑ์ผ๋ก ์ธํด ์์ฑ๋ ์ฝ๋๋ ๋ง์ฌ๋ง ๊ด๋ จ ์ฝ๋๋ฅผ ์ฝ์ ํ๋ ์ญํ ๋ง ๋ฑ ํ๊ณ ๊ธฐ์กด DllImport๋ฅผ ์ฌ์ฉํฉ๋๋ค.
// Unmarshal - Convert native data to managed data.
__retVal = __retVal_native != 0;
์ ๋ง๋ก ๋ง์ฌ๋ง ์ฒ๋ฆฌ๋ฅผ ํ๋์ง ๋ค๋ฅธ ์์ ๋ก ํ์ธํด ๋ดค์ต๋๋ค. LZ4 DLL์ ๋ค์๊ณผ ๊ฐ์ด LibraryImport ํน์ฑ์ ์ฌ์ฉํด
static partial class ExternDll
{
[LibraryImport("msys-lz4-1.dll")]
public static partial int LZ4_compressBound(int inputSize);
[LibraryImport("msys-lz4-1.dll")]
public static partial int LZ4_compress_default(byte[] src, byte[] dst, int srcSize, int dstCapacity);
[LibraryImport("msys-lz4-1.dll")]
public static partial int LZ4_decompress_safe(byte[] src, byte[] dst, int compressedSize, int dstCapacity);
}
byte[] ์ ๊ด๋ฆฌ ํ ๋ฉ๋ชจ๋ฆฌ์ ์์นํ๋ฏ๋ก ๋น๊ด๋ฆฌ ๋ชจ๋์ ๊ทธ๋๋ก ์ ๋ฌํ๋ฉด ์๋ฉ๋๋ค. LibraryImport ์ฅ์์ ์ํด ์์ฑ๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด,
static unsafe partial class ExternDll
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.6.42610")]
[System.Runtime.CompilerServices.SkipLocalsInitAttribute]
public static partial int LZ4_compress_default(byte[] src, byte[] dst, int srcSize, int dstCapacity)
{
int __retVal;
// Pin - Pin data in preparation for calling the P/Invoke.
fixed (void* __src_native = &global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<byte, byte>.ManagedToUnmanagedIn.GetPinnableReference(src))
fixed (void* __dst_native = &global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<byte, byte>.ManagedToUnmanagedIn.GetPinnableReference(dst))
{
__retVal = __PInvoke((byte*)__src_native, (byte*)__dst_native, srcSize, dstCapacity);
}
return __retVal;
// Local P/Invoke
[System.Runtime.InteropServices.DllImportAttribute("msys-lz4-1.dll", EntryPoint = "LZ4_compress_default", ExactSpelling = true)]
static extern unsafe int __PInvoke(byte* src, byte* dst, int srcSize, int dstCapacity);
}
}
์์ ๊ฐ์ด ๋ง์ฌ๋ง์ ํ๋ ์ฝ๋๊ฐ ์ฝ์ ๋จ์ ๋ณผ ์ ์์ต๋๋ค.
๊ฐ๋จํ๊ฒ ํ ์คํธ ์ฝ๋๋ฅผ ๋ง๋ ํ,
using System.Runtime.InteropServices;
using System.Text;
// TestBeep()
TestLZ4Compress();
static void TestBeep()
{
for (uint i = 0; i < 100; i++)
{
ExternDll.Beep(i * 100, 50);
}
}
static void TestLZ4Compress()
{
var sourceText = """
๋ท๋ท ์ฝ์ด๋ ASP.NET Core ์น ์์ฉ ํ๋ก๊ทธ๋จ, ๋ช
๋ น์ค ์์ฉ ํ๋ก๊ทธ๋จ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ์ ๋๋ฒ์
์๋์ฐ ํ๋ซํผ ์ฑ, ์์ฉ ํ๋ก๊ทธ๋จ ๋ฑ ์ด 4๊ฐ์ง๋ก ํฌ๋ก์ค ํ๋ซํผ ์๋๋ฆฌ์ค๋ฅผ ์ง์ํ๋ค. ๋ค๋ง, ํ์ฌ ์๋์ฐ์ ๋ฐ์คํฌํฑ ์ํํธ์จ์ด์ฉ ํ์ค GUI๋ฅผ ๋ ๋๋งํ๋ ์๋์ฐ ํผ ๋๋ WPF๋ ๊ตฌํ๋์ด ์์ง ์๋ค.[3][4] ์ด์ ๋ง์ดํฌ๋ก์ํํธ๋ ๋ท๋ท ์ฝ์ด3์์ ์๋์ฐ ํผ, WPF์ ์ ๋๋ฒ์
์๋์ฐ ํ๋ซํผ ์ฑ๊ณผ ํจ๊ป ์ง์ํ ๋ฐฉ์นจ์ด๋ค.[5] ์ฌ๊ธฐ์ ๋ท๋ท ์ฝ์ด๋ NuGet ํจํค์ง์ ์ฌ์ฉ์ ์ง์ํ๋ค. ์๋์ฐ ๋ฒ์ ์ ๋ท๋ท ํ๋ ์์ํฌ์๋ ๋ฌ๋ฆฌ ์
๋ฐ์ดํธ๋ ์๋์ฐ ์
๋ฐ์ดํธ์์๋ง ์ฃผ๋ก ์ด๋ฃจ์ด์ง๋ง, ๋ท๋ท ์ฝ์ด๋ ์
๋ฐ์ดํธ๋ฅผ ํจํค์ง ๊ด๋ฆฌ์ ํ์์ผ๋ก ์
๋ฐ์ดํธ๋ฅผ ํ๋ ์ฅ์ ์ด ์๋ค.[3][4]
๋ท๋ท ์ฝ์ด๋ ๊ณตํต ์ธ์ด ๋ฐํ์(CLR)์ ์์ ํ ๋ฐํ์ํ๊ฒฝ์ ๊ตฌํ์ํจ CoreCLR๋ก ๊ตฌ์ฑ๋์ด์๋ค. ์ด ๋ฐํ์์ ๋ท๋ท ํ๋ก๊ทธ๋จ ์คํ ๊ด๋ฆฌ๋ฅผ ์ํ ๊ฐ์ ์ปดํจํฐ๋ก ๋ง์ดํฌ๋ก์ํํธ์์ ์์ํ์ฌ, RyuJIT๋ผ๋ JIT ์ปดํ์ผ์ ํฌํจํ๋ค.[6] ๋ํ, AOT ์ปดํ์ผ ๋ ์์ ๋ฐ์ด๋๋ฆฌ์ ํตํฉ๋๋๋ก ์ต์ ํ ๋ ๋ท๋ท ์์ ๋ฐํ์์ธ CoreRT๋ฅผ ํฌํจํ๋ค.
๋ท๋ท ์ฝ์ด๋ ๋ท๋ท ํ๋ ์์ํฌ์ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ผ๋ถ ํฌํฌ์ธ CoreFX๋ ํฌํจ๋์ด ์์ผ๋ฉฐ,[7] ๋ท๋ท ์ฝ์ด์ API์ ์ผ๋ถ๋ถ์ ๋ท๋ท ํ๋ ์์ํฌ์ API๊ณผ ๋์ผํ ๋ถ๋ถ๋ ์์ผ๋, ๋ท๋ท ํ๋ ์์ํฌ์๋ ์ ํ ๋ค๋ฅธ ์ ์ฉ API์ ์ฌ์ฉํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ท๋ท ์ฝ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณํ์์ผ UWP์ ๊ฐ๋ฐ์ ํ์ฉํ ์ ์๋ค.[8]
๋ท๋ท ์ฝ์ด์ ๋ช
๋ น ์ค ์ธํฐํ์ด์ค๋ ์ด์ ์ฒด์ ์ ๋ํ ์คํ ์ง์
์ ์ ์ ๊ณตํ๊ณ ์ปดํ์ผ ๋ฐ ํจํค์ง ๊ด๋ฆฌ์ ๊ฐ์ ๊ฐ๋ฐ์ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ค.[9]
""";
Console.WriteLine(sourceText);
Console.WriteLine("------");
var source = Encoding.Default.GetBytes(sourceText);
var maxDstSize = ExternDll.LZ4_compressBound(source.Length);
//Console.WriteLine(maxDstSize);
var target = new byte[maxDstSize];
var compressedSize = ExternDll.LZ4_compress_default(source, target, source.Length, maxDstSize);
Console.WriteLine($"์์ถ (LZ4 ๊ธฐ๋ณธ)");
Console.WriteLine($"์์ค ํฌ๊ธฐ = {source.Length}");
Console.WriteLine($"์์ถ๋ ํฌ๊ธฐ = {compressedSize}");
Console.WriteLine($"์์ถ(%) = {(1 - (float)compressedSize / source.Length) * 100} %");
Console.WriteLine("------");
var decompressedTarget = new byte[source.Length];
var decompressedSize = ExternDll.LZ4_decompress_safe(target, decompressedTarget, compressedSize, decompressedTarget.Length);
Console.WriteLine($"์์ถ ํด์ (LZ4 ๊ธฐ๋ณธ)");
Console.WriteLine($"์์ถ ํด์ ํฌ๊ธฐ = {decompressedSize}");
Console.WriteLine("------");
var decompressedText = Encoding.Default.GetString(decompressedTarget);
Console.WriteLine(decompressedText);
}
NativeAOT๋ก ๋ค์์ ํ๋ก์ ํธ ์ค์ ์ผ๋ก ๊ฒ์ํ์ฌ,
...
<PublishAot>true</PublishAot>
<InvariantGlobalization>true</InvariantGlobalization>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
<DebuggerSupport>false</DebuggerSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
...
2M ์ฌ์ด์ฆ .NET ๋ฐํ์ ์์กด์ฑ ์๋ ์คํํ์ผ๊ณผ 500K ์ฌ์ด์ฆ์ LZ4 DLL์ ๋ณผ ์ ์๊ณ ,
$ .\No19.LibraryImportAttributeTest.exe
๋ท๋ท ์ฝ์ด๋ ASP.NET Core ์น ์์ฉ ํ๋ก๊ทธ๋จ, ๋ช
๋ น์ค ์์ฉ ํ๋ก๊ทธ๋จ...
------
์์ถ (LZ4 ๊ธฐ๋ณธ)
์์ค ํฌ๊ธฐ = 1857
์์ถ๋ ํฌ๊ธฐ = 1216
์์ถ(%) = 34.51804 %
------
์์ถ ํด์ (LZ4 ๊ธฐ๋ณธ)
์์ถ ํด์ ํฌ๊ธฐ = 1857
------
๋ท๋ท ์ฝ์ด๋ ASP.NET Core ์น ์์ฉ ํ๋ก๊ทธ๋จ, ๋ช
๋ น์ค ์์ฉ ํ๋ก๊ทธ๋จ...
์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์์ต๋๋ค.
https://github.com/dimohy/csharp-check/tree/main/No19.LibraryImportAttributeTest