How to pass a shared array to another type?

I'm still trying to find a quick way to convert a general array of type TOutput into another array of type TInput. All my arrays always have a numeric data type, but since C # does not have a Numeric type restriction, as is often requested, I currently have to live with this restriction. Proposed methods, such as casting for an object before, seem to slow down my cast significantly. I currently have a big if / else construct that checks the type and applies a specific type using pointer arithmetic, but this is a way to handle a great future. Parallel.For seems like a good way to get rid of pointers and speed things up, but still the general limitations of C # seem to be a problem, but still Tout in the code below is a problem. Here is 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 arrays are about 512x512 images, but there are about 1000 slices on the stack in the volume. Do you see any chance of having an easy way to accomplish 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)); } } 

Thanks. Martin

+6
source share
4 answers

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

Personally, I would just use:

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

This uploads the conversion logic to the compiler (to use the correct conversion from float to int without intermediaries or reflection). However, it cannot be used inside generics - i.e. x => (OutputType)x will not work.

+8
source

Have you tried this?

 public static TOut[] Cast<TOut,TIn>(TIn[] arr) { return arr.Select(x => (TOut)Convert.ChangeType(x,typeof(TOut))).ToArray(); } 
+2
source

Why don't you use just Cast for lists, this is an element of the LINQ Cast method from System.Linq.Enumerable :

 float[] A = { 0.1f, 0.2f, 0.6f }; int[] B = A.Cast(Of int).ToArray(); 

You can read about it here: Enumerable.Cast method (from TResult)

+1
source

I did three trivial experiments on an array with a float with 38988 elements in it (I just cut and pasted a bunch of arbitrary values ​​again and again)

 //_a = array of floats // pretty standard way of doing it 4ms _result = (from m in _a select (int)m).ToArray(); // I was rather disappointed with this 35ms _result = (from m in _a.AsParallel() select (int)m).ToArray(); // using a list rather surprised me 1ms _result = new List<int>(_a.Select(p => (int)p)).ToArray(); 

therefore, I don’t know how they compare with your tests, but I would say that the selection in the general list is quite effective.

EDIT:

I am adding my code. I have to miss something, because I have completely different results compared to my example.

 class Program { static void Main(string[] args) { using (var x = new ArrayCast()) { x.Run(); } using (var x = new ArrayCastList()) { x.Run(); } using (var x = new ArrayCastAsParallel()) { x.Run(); } while (Console.Read() != 'q') { ; // do nothing... } } } public abstract class Experiment : IAmATest, IDisposable { private Stopwatch _timer; protected bool IgnoreAssert { get; set; } #region IAmATest Members public abstract void Arrange(); public abstract void Act(); public abstract void Assert(); #endregion public void Run() { _timer = Stopwatch.StartNew(); Arrange(); _timer.Stop(); Console.WriteLine(String.Join(":", "Arrange", _timer.ElapsedMilliseconds)); _timer = Stopwatch.StartNew(); for (int i = 1; i < 1000; i++) Act(); _timer.Stop(); Console.WriteLine(String.Join(":", "Act", _timer.ElapsedMilliseconds)); if (IgnoreAssert) { return; } _timer = Stopwatch.StartNew(); Assert(); _timer.Stop(); Console.WriteLine(String.Join(":", "Assert", _timer.ElapsedMilliseconds)); } public abstract void Dispose(); } public class ArrayCast : Experiment { private int[] _a; double[] _result; public override void Arrange() { IgnoreAssert = true; _a = new int[100000]; } public override void Act() { _result = (from m in _a select (double)m).ToArray(); } public override void Assert() { } public override void Dispose() { _a = null; } } public class ArrayCastAsParallel : Experiment { private int[] _a; double[] _result; public override void Arrange() { IgnoreAssert = true; _a = new int[100000]; } public override void Act() { _result = (from m in _a.AsParallel() select (double)m).ToArray(); } public override void Assert() { } public override void Dispose() { _a = null; } } public class ArrayCastList : Experiment { private int[] _a; double[] _result; public override void Arrange() { IgnoreAssert = true; _a = new int[100000]; } public override void Act() { var x = new List<double>(_a.Select(p => (double)p)); } public override void Assert() { } public override void Dispose() { _a = null; } } 
0
source

Source: https://habr.com/ru/post/910681/


All Articles