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
Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.
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.
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With