2012-03-14 13 views
6

मैं अभी भी एक सामान्य सरणी प्रकार Tutput को TInput प्रकार के अन्य सरणी में परिवर्तित करने का एक तेज़ तरीका ढूंढने का प्रयास कर रहा हूं। मेरे सभी सरणी हमेशा एक संख्यात्मक डेटाटाइप के होते हैं, लेकिन सी # के पास संख्यात्मक रूप से अनुरोध किए जाने पर संख्यात्मक बाधा नहीं होती है, इसलिए मुझे वर्तमान में इस बाधा के साथ रहना है। सुझाव दिया गया है, जैसे किसी ऑब्जेक्ट को कास्ट करना, मेरी कास्ट को बहुत धीमा करना प्रतीत होता है। वर्तमान में मेरे पास एक बड़ा है/अन्यथा उस प्रकार के चेक को टाइप करें और पॉइंटर अंकगणितीय का उपयोग करके परिभाषित प्रकार पर डाला जाए, लेकिन भविष्य के लिए इसे संभालने के लिए यह बहुत बड़ा तरीका है। समांतर। पॉइंटर्स से छुटकारा पाने और चीज को तेज करने के लिए एक अच्छा तरीका प्रतीत होता है, लेकिन फिर भी सी # जेनेरिक बाधाएं एक समस्या प्रतीत होती हैं, लेकिन फिर भी नीचे दिए गए कोड में टाउट एक समस्या है। यहाँ मेरी कोड है:एक जेनेरिक सरणी को किसी अन्य प्रकार में कैसे डाला जाए?

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

उदाहरण:

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

सभी मामलों में मेरी सरणी प्रकार संख्यात्मक मानों (नाव, लघु, डबल, ...) और समय के सबसे अधिक कर रहे हैं, सरणियों के बारे में हैं 512x512 छवियां, लेकिन वॉल्यूम में लगभग 1000 स्लाइसों के ढेर में। क्या आपको ऐसा करने का एक आसान तरीका रखने का कोई मौका मिलता है?

टेस्ट कोड

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

धन्यवाद मार्टिन

+2

आप एक विशिष्ट उदाहरण दे सकते हैं यहां 'इनपुट टाइप', 'आउटपुट टाइप' और 'टाउट' क्या हो सकता है? –

+0

हाय मार्क, मुझे खेद है कि मैंने कोड में गलती की। मैंने एक अद्यतन किया। – msedi

+0

इसे linq में कर रहा है एक फर्क पड़ता है? (मैं देव पर्यावरण के पास नहीं हूं लेकिन यह मोटे तौर पर इसके बारे में है) var परिणाम = (एम से लेकर श्रेणी में। एस्परेल() चयन (int) एम) .ToArray(); – Peter

उत्तर

2

आप इस कोशिश की?

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

हाँ, मैंने किया। लेकिन मेरे उद्देश्य के लिए यह धीमा है। अपनी पॉइंटर विधियों में अपनी विधि की तुलना प्रदर्शन में 10 के कारक के आसपास है। – msedi

+0

मुझे नहीं लगता कि असुरक्षित कोड का उपयोग किए बिना एक तेज़ तरीका है। यदि आपको प्रदर्शन की आवश्यकता है तो पॉइंटर्स के साथ आपका दृष्टिकोण सर्वोत्तम –

+0

@ user1077243 है। आप निश्चित रूप से सही हैं। यही वह है जो मैं वर्तमान में करता हूं, लेकिन इससे मुझे सिरदर्द का कारण बनता है क्योंकि सी # पॉइंटर जेनरिक को अनुमति नहीं देता है और मुझे अपने लिए बहुत कुछ करना है। – msedi

8

इस तरह के संख्यात्मक प्रकारों के बीच कोई जादू रूपांतरण (जेनिक्स आदि का उपयोग करते समय) कोई जादू रूपांतरण नहीं है; Convert.ChangeType, या dynamic जैसे चाल हैं, लेकिन दोनों में एक मध्यवर्ती बॉक्स/अनबॉक्स शामिल है।

व्यक्तिगत रूप से, मैं तो बस का उपयोग कर होगा:

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

इस संकलक (बिचौलियों या प्रतिबिंब के बिना int को float से सही रूपांतरण का उपयोग करने) के लिए रूपांतरण तर्क offloads। हालांकि, यह जेनेरिक के अंदर प्रयोग योग्य नहीं है - यानी x => (OutputType)x काम नहीं करेगा।

+0

हाय मार्क, अन्य सभी विधियों की तुलना में यह अन्य सभी तरीकों की तुलना में 1.5 से 2 धीमी गति से है। मैं आप सभी को सत्यापित करने के लिए यहां एक टेस्ट कोड पोस्ट करूंगा। – msedi

+0

@msedi क्या आप कह रहे हैं कि 'Array.ConvertAll'' कनवर्ट। चेंज टाइप 'से धीमा है? इसके अलावा: आपका सरणी क्या आकार है? –

+0

नहीं, बिल्कुल नहीं ;-) मैंने एक टेस्ट कोड पोस्ट किया है। आप परिणाम कहां देख सकते हैं। दिलचस्प बात यह है कि वीएस में या वीएस के बिना इसे चलाने में मतभेद हैं। X86 और x64 में अंतर, और पाठ्यक्रम के डीबग मोड में रिलीज में भी। – msedi

0

मैं इसमें 38,988 आइटम के साथ एक नाव सरणी पर तीन तुच्छ प्रयोगों था (मैं बस में कटौती और बार-बार मनमाने ढंग से मूल्यों का एक समूह चिपकाया)

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

इसलिए मैं नहीं जानता कि इन कैसे तुलना आपके परीक्षणों के साथ, लेकिन मैं कहूंगा कि जेनेरिक सूची में चयन करना बहुत प्रभावी है।

संपादित करें:

मैं अपने कोड जोड़ने हूँ। मुझे कुछ याद आना चाहिए क्योंकि मुझे आपके उदाहरण को चलाने के मुकाबले मेरे उदाहरण से काफी अलग परिणाम मिलते हैं।

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

हाय पीटर, आपके उदाहरण के लिए धन्यवाद। मैंने इसे अपने टेस्ट कोड में जोड़ा है जो सभी मामलों पर चलाया गया है। ऐसा लगता है कि लिंक के साथ जो परीक्षण करना है वह बेहद धीमी है। क्या आप कृपया अपनी मशीन पर टेस्ट कोड के साथ दौड़ लेंगे? धन्यवाद मार्टिन – msedi

1

आप बस सूचियों के लिए Cast क्यों उपयोग नहीं करते हैं, यह System.Linq.Enumerable की LINQ कास्ट विधि सदस्य है:

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

आप इसके बारे में यहाँ पढ़ सकते हैं: Enumerable.Cast(Of TResult) Method

+0

यह सही है, लेकिन कास्ट विधि का उपयोग करके मुझे उन मामलों में त्रुटियां मिलती हैं जहां एक कास्ट संभव नहीं है, उदा। फ्लोट से int तक। अगर मैं इसे आसानी से डालना चाहता हूं तो यह काम नहीं करता है। – msedi

संबंधित मुद्दे