Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a hardware-specific delegate for an extension method

Tags:

c#

.net

delegates

I am writing a library of extension methods for primitive numeric arrays. The extension methods have both regular and hardware-accelerated versions. I am trying to use delegates so that I can have a single entry point for each method. For instance, consider a simple extension method that sums an array:

public static class ArrayExtensions
{

  #region Data Members

  private delegate TResult SumArrayDelegate<in TIn, out TResult>( TIn[] array );
  private static SumArrayDelegate<sbyte,int> SumArraySByte_;
  private static SumArrayDelegate<byte,int> SumArrayByte_;
  private static SumArrayDelegate<short,int> SumArrayShort_;
  private static SumArrayDelegate<ushort,int> SumArrayUShort_;
  private static SumArrayDelegate<int,int> SumArrayInt32_;
  private static SumArrayDelegate<uint,uint> SumArrayUInt32_;
  private static SumArrayDelegate<long,long> SumArrayInt64_;
  private static SumArrayDelegate<ulong,ulong> SumArrayUInt64_;
  private static SumArrayDelegate<float,float> SumArrayFloat_;
  private static SumArrayDelegate<double,double> SumArrayDouble_;


  #endregion

  #region Constructor

  static ArrayExtensions()
  {
    if( Avx2.IsSupported )
    {
      SumArraySByte_ = Avx2Utilities.Sum;
      SumArrayByte_ = Avx2Utilities.Sum;
      SumArrayShort_ = Avx2Utilities.Sum;
      SumArrayUShort_ = Avx2Utilities.Sum;
      SumArrayInt32_ = Avx2Utilities.Sum;
      SumArrayUInt32_ = Avx2Utilities.Sum;
      SumArrayInt64_ = Avx2Utilities.Sum;
      SumArrayUInt64_ = Avx2Utilities.Sum;
      SumArrayFloat_ = Avx2Utilities.Sum;
      SumArrayDouble_ = Avx2Utilities.Sum;
    }
    else if( Avx.IsSupported )
    {
      SumArraySByte_ = AvxUtilities.Sum;
      SumArrayByte_ = AvxUtilities.Sum;
      SumArrayShort_ = AvxUtilities.Sum;
      SumArrayUShort_ = AvxUtilities.Sum;
      SumArrayInt32_ = AvxUtilities.Sum;
      SumArrayUInt32_ = AvxUtilities.Sum;
      SumArrayInt64_ = AvxUtilities.Sum;
      SumArrayUInt64_ = AvxUtilities.Sum;
      SumArrayFloat_ = AvxUtilities.Sum;
      SumArrayDouble_ = AvxUtilities.Sum;
    }
    else
    {
      SumArraySByte_ = ArrayUtilities.Sum;
      SumArrayByte_ = ArrayUtilities.Sum;
      SumArrayShort_ = ArrayUtilities.Sum;
      SumArrayUShort_ = ArrayUtilities.Sum;
      SumArrayInt32_ = ArrayUtilities.Sum;
      SumArrayUInt32_ = ArrayUtilities.Sum;
      SumArrayInt64_ = ArrayUtilities.Sum;
      SumArrayUInt64_ = ArrayUtilities.Sum;
      SumArrayFloat_ = ArrayUtilities.Sum;
      SumArrayDouble_ = ArrayUtilities.Sum;
    }
  }

  #endregion

  public static int Sum( this sbyte[] array ) => SumArraySByte_( array );
  public static int Sum( this byte[] array ) => SumArrayByte_( array );
  public static int Sum( this short[] array ) => SumArrayShort_( array );
  public static int Sum( this ushort[] array ) => SumArrayUShort_( array );
  public static int Sum( this int[] array ) => SumArrayInt32_( array );
  public static uint Sum( this uint[] array ) => SumArrayUInt32_( array );
  public static long Sum( this long[] array ) => SumArrayInt64_( array );
  public static ulong Sum( this ulong[] array ) => SumArrayUInt64_( array );
  public static float Sum( this float[] array ) => SumArrayFloat_( array );
  public static double Sum( this double[] array ) => SumArrayDouble_( array );

}

This seems incredibly ugly, redundant, and something that breaks DRY principles. This is only one of tens of methods that I am implementing, and continuing this current pattern will result in a massive ArrayExtensions class.

Is there a way to approach this that won't require explicit definitions? Or rather, is there a way to make this generic, but limited to only the supported types? The extension methods will only be available for the types present in the above code.

like image 352
Haus Avatar asked Jun 06 '26 02:06

Haus


1 Answers

You can simplify this a bit by using inheritance. This would get rid of most of this.

Virtual calls are slightly faster than delegate calls as an added bonus. Use an abstract base class as opposed to an interface for added performance.

A downside of this is that you have less flexibility in how you pick the individual methods you want to use.

As an alternative you could consider generating this rote C# code using T4 templates. They are nicely integrated into the IDE.

Another possibility might be reflection. This would not get rid of all of this but some.

like image 91
usr Avatar answered Jun 08 '26 18:06

usr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!