diff --git a/Samples/ILGPU.Samples.sln b/Samples/ILGPU.Samples.sln index fac69c6bc..1651504ce 100644 --- a/Samples/ILGPU.Samples.sln +++ b/Samples/ILGPU.Samples.sln @@ -125,6 +125,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorSampleApp", "BlazorSa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemoryBufferStrides", "MemoryBufferStrides\MemoryBufferStrides.csproj", "{2EF99A5B-9AAE-44A8-BB41-923DF66A7EAB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StaticAbstractInterfaceMembers", "StaticAbstractInterfaceMembers\StaticAbstractInterfaceMembers.csproj", "{28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -351,6 +353,10 @@ Global {2EF99A5B-9AAE-44A8-BB41-923DF66A7EAB}.Debug|Any CPU.Build.0 = Debug|Any CPU {2EF99A5B-9AAE-44A8-BB41-923DF66A7EAB}.Release|Any CPU.ActiveCfg = Release|Any CPU {2EF99A5B-9AAE-44A8-BB41-923DF66A7EAB}.Release|Any CPU.Build.0 = Release|Any CPU + {28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126}.Debug|Any CPU.Build.0 = Debug|Any CPU + {28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126}.Release|Any CPU.ActiveCfg = Release|Any CPU + {28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -414,6 +420,7 @@ Global {029C809E-4EBF-4437-A4B2-BA7E14A9C023} = {C1D99632-ED4A-4B08-A14D-4C8DB375934F} {1A909DA2-15AE-466F-8BBE-C3F676C39812} = {30F390DB-B823-40A2-A881-382B9EF36C07} {2EF99A5B-9AAE-44A8-BB41-923DF66A7EAB} = {C1D99632-ED4A-4B08-A14D-4C8DB375934F} + {28FD07DE-7B7D-46C3-9EE1-5D50C0E4F126} = {C1D99632-ED4A-4B08-A14D-4C8DB375934F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {30E502BD-3826-417F-888F-1CE19CF5C6DA} diff --git a/Samples/StaticAbstractInterfaceMembers/Program.cs b/Samples/StaticAbstractInterfaceMembers/Program.cs new file mode 100644 index 000000000..f11a3e8a8 --- /dev/null +++ b/Samples/StaticAbstractInterfaceMembers/Program.cs @@ -0,0 +1,159 @@ +// --------------------------------------------------------------------------------------- +// ILGPU Samples +// Copyright (c) 2023 ILGPU Project +// www.ilgpu.net +// +// File: Program.cs +// +// This file is part of ILGPU and is distributed under the University of Illinois Open +// Source License. See LICENSE.txt for details. +// --------------------------------------------------------------------------------------- + +using ILGPU; +using ILGPU.Runtime; +using System; +using System.Linq; + +namespace StaticAbstractInterfaceMembers +{ + class Program + { + // .NET 7 introduced Static Abstract Interface Members. These can either be used + // by themselves, or combined with Generic Math, to provide the ability to write + // generic kernels with different implementations for some functionality. + // + // Before .NET 7, a more restricted option is to use a struct that implements + // the generic interface. + // +#if NET7_0_OR_GREATER + public interface ICalculatorOperation + where T : System.Numerics.INumber + { + static abstract T Calculate(T left, T right); + } + + // Interface can be implemented by a 'class' or 'struct'. + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Performance", + "CA1812:Avoid uninstantiated internal classes", + Justification = "Using static method in ILGPU kernel")] + public class AdditionOp : ICalculatorOperation + { + public static int Calculate(int left, int right) => left + right; + } + + // Interface can be implemented by a 'class' or 'struct'. + public struct MultiplyOp : ICalculatorOperation + { + public static float Calculate(float left, float right) => left * right; + } + + public static void CalculatorKernel( + Index1D index, + ArrayView1D input, + ArrayView1D output) + where T : unmanaged, System.Numerics.INumber + where TOp : ICalculatorOperation + { + // Calls the static abstract method on the class or struct. + output[index] = TOp.Calculate(input[index], input[index]); + } + +#else + + public interface ICalculatorOperation + where T : unmanaged + { + T Calculate(T left, T right); + } + + // Interface must be implemented by a 'struct'. + public struct AdditionOp : ICalculatorOperation + { + public int Calculate(int left, int right) => left + right; + } + + // Interface must be implemented by a 'struct'. + public struct MultiplyOp : ICalculatorOperation + { + public float Calculate(float left, float right) => left * right; + } + + public static void CalculatorKernel( + Index1D index, + ArrayView1D input, + ArrayView1D output) + where T : unmanaged + where TOp : struct, ICalculatorOperation + { + // Creates a new instance of the struct, and calls the method. + output[index] = default(TOp).Calculate(input[index], input[index]); + } + +#endif + + public static void UsingAbstractFunction(Accelerator accelerator) +#if NET7_0_OR_GREATER + where T : unmanaged, System.Numerics.INumber + where TOp : ICalculatorOperation +#else + where T : unmanaged + where TOp : struct, ICalculatorOperation +#endif + { + var values = + Enumerable.Range(0, 16) +#if NET7_0_OR_GREATER + .Select(x => T.CreateChecked(x)) +#else + .Select(x => (T)Convert.ChangeType( + x, + typeof(T), + System.Globalization.CultureInfo.InvariantCulture)) +#endif + .ToArray(); + using var inputBuffer = accelerator.Allocate1D(values); + using var outputBuffer = accelerator.Allocate1D(values); + + outputBuffer.MemSetToZero(); + + var kernel = accelerator.LoadAutoGroupedStreamKernel< + Index1D, + ArrayView1D, + ArrayView1D>( + CalculatorKernel); + + kernel( + (int)inputBuffer.Length, + inputBuffer.View, + outputBuffer.View); + + accelerator.Synchronize(); + + var result = outputBuffer.GetAsArray1D(); + for (var i = 0; i < result.Length; i++) + Console.WriteLine($"[{i}] = {result[i]}"); + Console.WriteLine(); + } + + /// + /// Demonstrates static abstract members in interfaces. + /// + static void Main() + { + // Create main context + using var context = Context.CreateDefault(); + + // For each available device... + foreach (var device in context) + { + // Create accelerator for the given device + using var accelerator = device.CreateAccelerator(context); + Console.WriteLine($"Performing operations on {accelerator}"); + + UsingAbstractFunction(accelerator); + UsingAbstractFunction(accelerator); + } + } + } +} diff --git a/Samples/StaticAbstractInterfaceMembers/StaticAbstractInterfaceMembers.csproj b/Samples/StaticAbstractInterfaceMembers/StaticAbstractInterfaceMembers.csproj new file mode 100644 index 000000000..7afa0f7dd --- /dev/null +++ b/Samples/StaticAbstractInterfaceMembers/StaticAbstractInterfaceMembers.csproj @@ -0,0 +1,16 @@ + + + $(LibrarySamplesTargetFrameworks) + Exe + 11.0 + + + + true + AllEnabledByDefault + + + + + +