Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast a generic array into another type?

I'm still trying to find a fast way of how to convert a generic array of type TOutput to another array of type TInput. All my arrays are always of a numeric datatype, but since C# has no type constraint to Numeric as often requested, I currently have to live with this constraint. The suggested methods, like casting to an object before, seems to slow down my cast tremendously. Currently I have a large if/else construct that check for a type and cast to a defined type using pointer arithmetic, but this is way to large to handle for the future. Parallel.For seems a good way to get rid of pointers and to speed up thing, but still the C# generic constraints seem to be a problem, but still the Tout in the code below is a problem. Here's my code:

    public static OutputType[] Cast<InputType, OutputType>(InputType[] inputArray_in)
    {
        var aRange = Partitioner.Create(0, inputArray_in.Length);
        OutputType[] aResult = new OutputType[inputArray_in.Length];

        Parallel.ForEach(aRange, (r) =>
        {
            for (int i = r.Item1; i < r.Item2; i++)
            {
                aResult[i] = (OutputType)(inputArray_in[i]);
            }
        });

        return aResult;
    }

Example:

float[] A = { 0.1f, 0.2f, 0.6f };
int []B = Cast<float, int>(A);

In all cases my array types are numerical values (float, short, double,...) and most of the time, the arrays are about 512x512 images, but in a stack of about 1000 slices in a volume. Do you see any chance to have a simple way of performing this?

Test Code

public static class CastTest
{
    delegate double[] CastMethod(int[] input);

    public static unsafe double[] Cast1(int[] input)
    {
        int N = input.Length;
        double[] output = new double[N];

        for (int i = 0; i < N; i++) output[i] = (double)(input[i]);

        return output;
    }

    public static unsafe double[] Cast2(int[] input)
    {
        int N = input.Length;
        double[] output = new double[N];

        fixed (double* output_pinned = output)
        {
            double* outp = output_pinned;

            fixed (int* input_pinned = input)
            {
                int* inp = input_pinned;

                for (int i = 0; i < N; i++, inp++, outp++) *outp = (double)(*inp);
            }

            return output;
        }
    }

    public static unsafe double[] Cast3(int[] input)
    {
        int N = input.Length;
        double[] output = new double[N];

        fixed (double* output_pinned = output)
        {
            double* outp = output_pinned;

            fixed (int* input_pinned = input)
            {
                int* inp = input_pinned;

                for (int i = 0; i < N; i++) outp[i] = (double)(inp[i]);
            }

            return output;
        }
    }

    public static unsafe double[] Cast4(int[] input)
    {
        int N = input.Length;
        double[] output = new double[N];

        fixed (double* output_pinned = output)
        {
            fixed (int* input_pinned = input)
            {
                for (int i = 0; i < N; i++) output_pinned[i] = (double)(input_pinned[i]);
            }
        }

        return output;
    }

    public static unsafe double[] Cast5(int[] input)
    {
        return Array.ConvertAll<int, double>(input, x => (double)x);
    }

    public static double[] Cast6(int[] input)
    {
        var aRange = Partitioner.Create(0, input.Length);

        int N = input.Length;
        double[] output = new double[N];

        Parallel.ForEach(aRange, (r) =>
            {
                for (int i = r.Item1; i < r.Item2; i++) output[i] = (double)(input[i]);
            });

        return output;
    }

    public unsafe static double[] Cast7(int[] input)
    {
        var aRange = Partitioner.Create(0, input.Length);

        int N = input.Length;
        double[] output = new double[N];

        Parallel.ForEach(aRange, (r) =>
        {
            fixed (double* output_pinned = output)
            {
                double* outp = output_pinned + r.Item1;

                fixed (int* input_pinned = input)
                {
                    int* inp = input_pinned + r.Item1;

                    for (int i = r.Item1; i < r.Item2; i++, outp++, inp++) *outp = (double)(*inp);
                }
            }
        });

        return output;
    }

    public unsafe static double[] Cast8(int[] input)
    {
        var result = (from m in input.AsParallel() select (double)m).ToArray();

        return result;
    }


    public static double[] Cast9(int[] input)
    {
        return  (from m in input select (double)m).ToArray(); 
    }

    public static double[] Cast10(int[] input)
    {
        return (from m in input.AsParallel() select (double)m).ToArray(); 
    }

    public static double[] Cast11(int[] input)
    {
        return new List<double>(input.Select(p => (double)p)).ToArray(); 
    }

    static int[] A = new int[100000];
    const int runs = 10000;

    public static void StartTest()
    {
        TestMethod("1", Cast1);
        TestMethod("2", Cast2);
        TestMethod("3", Cast3);
        TestMethod("4", Cast4);
        TestMethod("5", Cast5);
        TestMethod("6", Cast6);
        TestMethod("7", Cast7);
        TestMethod("8", Cast8);
        TestMethod("9", Cast9);
        TestMethod("10", Cast10);
        TestMethod("11", Cast11);
    }

    static void TestMethod(string Name, CastMethod method)
    {
        var timer = Stopwatch.StartNew();

        for (int i = 0; i < runs; i++) { double[] res = method(A); }

        timer.Stop();

        Console.WriteLine(String.Format("{0}: {1}ms", Name, timer.ElapsedMilliseconds));
    }
}

Thank you Martin

like image 764
msedi Avatar asked Mar 14 '12 08:03

msedi


People also ask

Can you instantiate generic arrays?

Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.

Why generic arrays are not allowed?

If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.

Can we use generic with array in Java?

Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic. The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime.

How do you make a generic 2d array?

There are two ways to solve this: 1) data = (T[][])new Object[rows][columns]; 2) declare data as an Object[][], then cast to T when retrieving elements from the matrix. Yes, both will generate compiler warnings (note: warnings, not errors).


1 Answers

There is no magic conversion (when using generics etc) between numeric types like this; there are tricks like Convert.ChangeType, or dynamic, but both involve an intermediate box/unbox.

Personally, I'd just be using:

float[] A = { 0.1f, 0.2f, 0.6f };
int[] B = Array.ConvertAll(A, x => (int)x);

This offloads the conversion logic to the compiler (to use the correct conversion from float to int without intermediaries or reflection). It is, however, not usable inside generics - i.e. x => (OutputType)x will not work.

like image 154
Marc Gravell Avatar answered Oct 15 '22 09:10

Marc Gravell