2012-03-14 27 views
6

Tôi vẫn đang cố gắng tìm cách nhanh chóng để chuyển đổi một mảng chung loại TOUTput thành một mảng kiểu TInput khác. Tất cả các mảng của tôi luôn là một kiểu dữ liệu số, nhưng vì C# không có ràng buộc kiểu với Số như thường được yêu cầu, tôi hiện đang phải sống với ràng buộc này. Các phương pháp được đề xuất, chẳng hạn như truyền tới một đối tượng trước đây, dường như làm chậm quá trình diễn xuất của tôi. Hiện tại tôi có một cấu trúc if/else lớn để kiểm tra một kiểu và truyền đến một kiểu xác định bằng cách sử dụng số học con trỏ, nhưng đây là cách để xử lý lớn cho tương lai. Parallel.For có vẻ là một cách tốt để loại bỏ các con trỏ và để tăng tốc độ, nhưng vẫn còn các ràng buộc chung của C# dường như là một vấn đề, nhưng vẫn là Tout trong đoạn mã dưới đây là một vấn đề. Dưới đây là mã của tôi:Làm thế nào để đúc một mảng chung thành một loại khác?

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; 
    } 

Ví dụ:

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

Trong mọi trường hợp loại mảng của tôi là giá trị số (float, ngắn, đôi, ...) và hầu hết thời gian, các mảng khoảng Hình ảnh 512x512, nhưng trong một chồng khoảng 1000 lát trong một ổ đĩa. Bạn có thấy bất kỳ cơ hội nào để có một cách thực hiện đơn giản này không?

Kiểm tra Mã

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)); 
    } 
} 

Cảm ơn bạn Martin

+2

Bạn có thể cho một ví dụ cụ thể ở đây những gì 'InputType',' OutputType' và 'Tout' có thể là gì? –

+0

Xin chào Marc, tôi xin lỗi tôi đã nhầm lẫn trong mã. Tôi đã cập nhật. – msedi

+0

không làm điều đó trong linq tạo sự khác biệt? (Tôi không gần môi trường dev nhưng điều này gần như là ý chính của nó) var result = (từ m trong range.AsParallel() select (int) m) .ToArray(); – Peter

Trả lời

2

Bạn đã thử này?

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

Vâng, tôi đã làm. Nhưng với mục đích của tôi thì nó sẽ chậm lại. So sánh phương pháp của bạn với các phương thức con trỏ của tôi là tròn về một yếu tố của 10 trong hiệu suất. – msedi

+0

Tôi không nghĩ rằng có một cách nhanh hơn mà không sử dụng mã không an toàn. Nếu bạn cần hiệu suất thì cách tiếp cận của bạn với con trỏ là tốt nhất –

+0

@ user1077243. Bạn chắc chắn đúng. Đây là những gì tôi hiện đang làm, nhưng điều này gây cho tôi rất nhiều đau đầu vì C# không cho phép con trỏ đến generics và tôi phải làm rất nhiều cho riêng tôi. – msedi

8

Không có phép chuyển đổi (khi sử dụng Generics ...) giữa các loại số như thế này; có các thủ thuật như Convert.ChangeType hoặc dynamic, nhưng cả hai đều liên quan đến hộp/hộp thư trung gian.

Cá nhân, tôi chỉ muốn được sử dụng:

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

này offloads logic chuyển đổi để trình biên dịch (để sử dụng các chuyển đổi chính xác từ float để int không qua trung gian hoặc phản ánh). Tuy nhiên, không thể sử dụng trong các generics bên trong - tức là x => (OutputType)x sẽ không hoạt động.

+0

Xin chào Marc, so với tất cả các phương pháp khác về yếu tố của nó khoảng 1,5 đến 2 chậm hơn tất cả các phương pháp khác. Tôi sẽ đăng một mã thử nghiệm ở đây để xác minh cho tất cả các bạn. – msedi

+0

@msedi là bạn nói rằng 'Array.ConvertAll' chậm hơn' Convert.ChangeType'? Ngoài ra: kích thước của bạn là gì mảng? –

+0

Không, tất nhiên là không ;-) Tôi đã đăng một mã thử nghiệm. Nơi bạn có thể xem kết quả. Điều thú vị là có một sự khác biệt chạy nó trong VS hoặc không có VS. Sự khác biệt trong x86 và x64, và cũng trong Release trong chế độ Debug của khóa học. – msedi

0

tôi đã làm ba thí nghiệm tầm thường hơn một mảng float với 38.988 mặt hàng trong đó (tôi chỉ cần cắt và dán một loạt các giá trị độc đoán hơn và hơn nữa)

//_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(); 

vì vậy tôi không biết làm thế nào những so sánh với các bài kiểm tra của bạn nhưng tôi muốn nói việc chọn vào danh sách chung là khá hiệu quả.

EDIT:

tôi thêm mã của tôi. Tôi phải mất một cái gì đó bởi vì tôi nhận được kết quả khá khác nhau từ ví dụ của tôi so với chạy ví dụ của bạn.

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

Xin chào Peter, cảm ơn ví dụ của bạn. Tôi đã thêm nó vào mã thử nghiệm của mình, một mã được chạy trên tất cả các trường hợp. Dường như tất cả các thử nghiệm phải làm với LINQ đều cực kỳ chậm. Bạn vui lòng chạy thử với mã kiểm tra trên máy của bạn không? Cảm ơn Martin – msedi

1

Tại sao bạn không sử dụng đơn giản Cast cho các danh sách, đó là LINQ Cast phương pháp viên của System.Linq.Enumerable:

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

Bạn có thể đọc về nó ở đây: Enumerable.Cast(Of TResult) Method

+0

Đúng vậy, nhưng việc sử dụng phương thức .Cast cho tôi lỗi trong trường hợp không thể truyền, ví dụ: từ float đến int. Nếu tôi chỉ đơn giản là đúc nó theo cách nó không hoạt động. – msedi

Các vấn đề liên quan