2008-12-14 14 views
51

मैं सी # में एक वित्तीय आवेदन लिख रहा हूं जहां प्रदर्शन (यानी गति) महत्वपूर्ण है। चूंकि यह एक वित्तीय ऐप है, इसलिए मुझे दशमलव डेटाटाइप का गहन रूप से उपयोग करना होगा।सी # दशमलव डेटाटाइप प्रदर्शन

मैंने प्रोफाइल को अनुकूलित किया है जितना मैं प्रोफाइलर की मदद से कर सकता था। दशमलव का उपयोग करने से पहले, सब कुछ डबल डेटाटाइप के साथ किया गया था और गति कई गुना तेज थी। हालांकि, डबल अपनी बाइनरी प्रकृति के कारण एक विकल्प नहीं है, जिससे कई परिचालनों के दौरान बहुत सटीक त्रुटियां होती हैं।

क्या कोई दशमलव लाइब्रेरी है जिसे मैं सी # के साथ इंटरफ़ेस कर सकता हूं जो मुझे .NET में देशी दशमलव डेटाटाइप पर प्रदर्शन सुधार दे सकता है?

जवाब मैं पहले से ही मिल गया के आधार पर, मैंने देखा मैं तो यहाँ पर्याप्त स्पष्ट नहीं था, कुछ अतिरिक्त विवरण हैं:

  • एप्लिकेशन के रूप में तेजी से के रूप में यह संभवतः (जितनी जल्दी जा सकते हैं यानी हो गया है यह तब था जब दशमलव के बजाय डबल का उपयोग करना एक सपना होगा)। डबल दशमलव से लगभग 15x तेज था, क्योंकि संचालन हार्डवेयर आधारित होते हैं।
  • हार्डवेयर पहले से ही शीर्ष-पायदान (मैं दोहरी ज़ेनॉन क्वाड-कोर पर चल रहा हूं) और एप्लिकेशन थ्रेड का उपयोग करता है, इसलिए मशीन पर CPU उपयोग हमेशा 100% होता है। इसके अतिरिक्त, ऐप 64 बिट मोड में चल रहा है, जो इसे 32 बिट पर एक बेहतर प्रदर्शन लाभ प्रदान करता है।
  • मैंने सैनिटी के बिंदु से पहले अनुकूलित किया है (एक महीने से अधिक आधा अनुकूलन; मान लीजिए या नहीं, अब यह वही गणना करने के लिए लगभग 1/5000 लेता है जो मैंने शुरुआत में संदर्भ के रूप में उपयोग किया था); इस अनुकूलन में सबकुछ शामिल था: स्ट्रिंग प्रसंस्करण, आई/ओ, डेटाबेस एक्सेस और इंडेक्स, मेमोरी, लूप, कुछ चीजों को बदलने के तरीके को बदलना, और यहां तक ​​कि "अगर" पर "स्विच" का उपयोग करके भी यह एक फर्क पड़ता है। प्रोफाइलर अब स्पष्ट रूप से दिखा रहा है कि शेष प्रदर्शन अपराधी दशमलव डेटा ऑपरेटरों पर है। कुछ और समय नहीं जोड़ रहा है।
  • आपको यहां मेरा विश्वास करना है: मैं जहां तक ​​संभवतः सी # .NET के दायरे में जा सकता हूं, एप्लिकेशन को अनुकूलित करने के लिए, और मैं वास्तव में अपने वर्तमान प्रदर्शन पर आश्चर्यचकित हूं। अब मैं डबल के करीब कुछ के लिए दशमलव प्रदर्शन को बेहतर बनाने के लिए एक अच्छा विचार ढूंढ रहा हूं। मुझे पता है कि यह केवल एक सपना है, लेकिन सिर्फ यह देखना चाहता था कि मैंने सबकुछ संभव सोचा। :)

धन्यवाद!

+3

आप समयपूर्व अनुकूलन के कगार पर हैं, बस ऐप को सही तरीके से कोड करें, फिर – TravisO

+0

तथ्य के बाद ट्विक करें आपने सभी निम्न स्तर सी # अनुकूलन किए हैं। कुछ एल्गोरिदमिक सुधार शेष हो सकते हैं (उदाहरण के लिए दशमलव पर कम परिचालन करना)। – Brian

+2

एक समय-संवेदनशील वित्तीय अनुप्रयोग में डेटाबेस संचालन खराब लगता है ... –

उत्तर

7

समस्या मूल रूप से हार्डवेयर में डबल/फ्लोट समर्थित है, जबकि दशमलव और समान नहीं हैं। अर्थात। आपको गति + सीमित परिशुद्धता और अधिक सटीकता + गरीब प्रदर्शन के बीच चयन करना होगा।

22

आप कहते हैं कि इसे तेज़ होने की आवश्यकता है, लेकिन क्या आपके पास ठोस गति की आवश्यकता है? यदि नहीं, तो आप सैनिटी के बिंदु से पहले अनुकूलित कर सकते हैं :)

जैसा कि मेरे बगल में बैठे दोस्त ने सुझाव दिया है, क्या आप इसके बजाय अपने हार्डवेयर को अपग्रेड कर सकते हैं? यह कोड लिखने से सस्ता होने की संभावना है।

सबसे स्पष्ट विकल्प decimals के बजाय पूर्णांक का उपयोग करना है - जहां एक "इकाई" कुछ "एक हज़ारवां प्रतिशत" (या जो भी आप चाहते हैं) - आपको विचार मिलता है)। चाहे वह व्यवहार्य है या नहीं, उन परिचालनों पर निर्भर करेगा जो आप दशमलव मानों पर शुरू कर रहे हैं। आपको बहुत होने पर सावधान रहना होगा - इसे गलती करना आसान है (कम से कम यदि आप मेरे जैसे हैं)।

क्या प्रोफाइलर आपके एप्लिकेशन में विशेष हॉटस्पॉट दिखाता है जिसे आप अलग-अलग अनुकूलित कर सकते हैं?उदाहरण के लिए, यदि आपको कोड के एक छोटे से क्षेत्र में बहुत सी गणना करने की आवश्यकता है, तो आप दशमलव से एक पूर्णांक प्रारूप में परिवर्तित कर सकते हैं, गणना कर सकते हैं और फिर वापस परिवर्तित कर सकते हैं। कोड के बड़े पैमाने पर दशमलव के लिए एपीआई रख सकता है, जो इसे बनाए रखना आसान बना सकता है। हालांकि, अगर आपके पास हॉटस्पॉट नहीं हैं, तो यह संभव नहीं हो सकता है।

+1 रूपरेखा के लिए और हमें उस गति बता, एक निश्चित आवश्यकता है btw :)

+1

@Downvoter: टिप्पणी करने की देखभाल? –

40

आप लंबे समय डेटाप्रकार उपयोग कर सकते हैं। निश्चित रूप से, आप वहां पर भिन्नताओं को स्टोर करने में सक्षम नहीं होंगे, लेकिन यदि आप अपने ऐप को पाउंड के बजाय पेनी स्टोर करने के लिए कोड करते हैं, तो आप ठीक रहेगा। लंबी डेटाटाइप के लिए शुद्धता 100% है, और जब तक आप विशाल संख्याओं के साथ काम नहीं कर रहे हैं (64-बिट लंबे प्रकार का उपयोग करें) तो आप ठीक रहेगा।

यदि आप पेनीज़ को संग्रहीत करने का आदेश नहीं दे सकते हैं, तो कक्षा में एक पूर्णांक लपेटें और इसका उपयोग करें।

+0

मैं सहमत हूं। एक 64-बिट मशीन और लंबे समय का प्रयोग करें। इसके अलावा, यह मेरे दिमाग में आया - मुझे लगता है कि .NET के पास कुछ मशीन-विशिष्ट कोड उत्पन्न करने के लिए कुछ टूल थे। मुझे विश्वास है कि इसे ngen कहा जाता था। शायद वहाँ हासिल करने के लिए प्रदर्शन है ... –

+1

यह निश्चित रूप से जाने का रास्ता है। आपको कितनी सटीकता की आवश्यकता है, इस पर निर्भर करता है कि लंबी या int, और पैनीज़, या पेनीज़ के अंशों का उपयोग करें। कई ऑपरेशन अब युगल का उपयोग करने की तुलना में तेज़ या तेज़ होंगे। – Chris

+1

एक वर्ग में इसे लपेटने से ढेर के ऊपर की ओर बढ़ना होगा, और ऑपरेटर ओवरलोडिंग या फ़ंक्शन कॉल की आवश्यकता होने के कारण भी एक संरचना चीजों को धीमा कर देगी, इसलिए कच्चे लंबे/int भंडारण प्रदर्शन के लिए सबसे अच्छा है। – Chris

1

मैं अभी तक कोई टिप्पणी नहीं दे सकता या वोट नहीं दे सकता क्योंकि मैंने अभी स्टैक ओवरफ़्लो पर शुरुआत की है। एलेक्सस्मार्ट पर मेरी टिप्पणी (23 दिसंबर 2008 12:31 पोस्ट की गई) यह है कि अभिव्यक्ति गोल (एन/परिशुद्धता, परिशुद्धता), जहां एन int और सटीक लंबा है वह वह नहीं करेगा जो वह सोचता है:

1) एन/परिशुद्धता एक पूर्णांक-विभाजन वापस कर देगा, यानी यह पहले ही गोल हो जाएगा लेकिन आप किसी भी दशमलव का उपयोग करने में सक्षम नहीं होंगे। गोलाकार व्यवहार गणित से भी अलग है। राउंड (...)।

2) कोड "वापसी गणित.गोल (n/परिशुद्धता, सटीक) .ToString()" गणित.गोल (डबल, पूर्णांक) और गणित.गोल (दशमलव के बीच एक अस्पष्टता की वजह से संकलन नहीं करता है, पूर्णांक)। आपको दशमलव पर डालना होगा (यह एक वित्तीय ऐप होने के बाद से दोगुना नहीं है) और इसलिए दशमलव के साथ पहले स्थान पर भी जा सकता है।

3) n/परिशुद्धता, जहां परिशुद्धता 4, गणित.गोल ((दशमलव) (1234567/4), 4) रिटर्न 4. उदाहरण के द्वारा चार दशमलव लेकिन विभाजन के काट-छांट नहीं जाएगा 308641. (1234567/4 = 308641.75), जबकि आप शायद 1235000 प्राप्त करना चाहते हैं (पीछे 567 से 4 अंकों की सटीकता के लिए गोल)। ध्यान दें कि Math.Round एक निश्चित बिंदु पर गोल करने की अनुमति देता है, निश्चित परिशुद्धता नहीं।

अद्यतन: मैं अब टिप्पणी जोड़ सकता हूं लेकिन टिप्पणी क्षेत्र में इसे रखने के लिए पर्याप्त जगह नहीं है।

5

एमएमएक्स/एसएसई/एसएसई 2 के बारे में क्या?

मुझे लगता है कि यह मदद मिलेगी ... तो ... दशमलव 128bit डेटाप्रकार है और SSE2 भी उप, div, 1 सीपीयू टिक में mul दशमलव ...

: 128 बिट है ... और यह भी जोड़ सकते हैं,

आप कुलपति का उपयोग कर SSE2 के लिए DLL लिख सकते हैं ++ और फिर अपने आवेदन में है कि DLL का उपयोग

जैसे // आप इस

कुलपति ++

की तरह कुछ कर सकते हैं
#include <emmintrin.h> 
#include <tmmintrin.h> 

extern "C" DllExport __int32* sse2_add(__int32* arr1, __int32* arr2); 

extern "C" DllExport __int32* sse2_add(__int32* arr1, __int32* arr2) 
{ 
    __m128i mi1 = _mm_setr_epi32(arr1[0], arr1[1], arr1[2], arr1[3]); 
    __m128i mi2 = _mm_setr_epi32(arr2[0], arr2[1], arr2[2], arr2[3]); 

    __m128i mi3 = _mm_add_epi32(mi1, mi2); 
    __int32 rarr[4] = { mi3.m128i_i32[0], mi3.m128i_i32[1], mi3.m128i_i32[2], mi3.m128i_i32[3] }; 
    return rarr; 
} 

सी #

[DllImport("sse2.dll")] 
private unsafe static extern int[] sse2_add(int[] arr1, int[] arr2); 

public unsafe static decimal addDec(decimal d1, decimal d2) 
{ 
    int[] arr1 = decimal.GetBits(d1); 
    int[] arr2 = decimal.GetBits(d2); 

    int[] resultArr = sse2_add(arr1, arr2); 

    return new decimal(resultArr); 
} 
2

मुझे नहीं लगता कि कि सका नेट दशमलव मान के साथ आसान काम SSE2 निर्देश। ।नेट दशमलव डेटा प्रकार 128 बिट दशमलव फ़्लोटिंग पॉइंट टाइप http://en.wikipedia.org/wiki/Decimal128_floating-point_format, एसएसई 2 निर्देश 128 बिट पूर्णांक प्रकार के साथ काम करते हैं।

2

पुराना प्रश्न, फिर भी बहुत मान्य है।

लांग का उपयोग करने के विचार का समर्थन करने के लिए यहां कुछ संख्याएं दी गई हैं।

समय संक्षेप में 100'000'000 अतिरिक्त

Long  231 mS 
Double 286 mS 
Decimal 2010 mS 

प्रदर्शन करने के लिए ले लिया, दशमलव ~ 10 बार धीमी है कि लंबे समय तक या डबल।

कोड:

Sub Main() 
    Const TESTS = 100000000 
    Dim sw As Stopwatch 

    Dim l As Long = 0 
    Dim a As Long = 123456 
    sw = Stopwatch.StartNew() 
    For x As Integer = 1 To TESTS 
     l += a 
    Next 
    Console.WriteLine(String.Format("Long {0} mS", sw.ElapsedMilliseconds)) 

    Dim d As Double = 0 
    Dim b As Double = 123456 
    sw = Stopwatch.StartNew() 
    For x As Integer = 1 To TESTS 
     d += b 
    Next 
    Console.WriteLine(String.Format("Double {0} mS", sw.ElapsedMilliseconds)) 

    Dim m As Decimal = 0 
    Dim c As Decimal = 123456 
    sw = Stopwatch.StartNew() 
    For x As Integer = 1 To TESTS 
     m += c 
    Next 
    Console.WriteLine(String.Format("Decimal {0} mS", sw.ElapsedMilliseconds)) 

    Console.WriteLine("Press a key") 
    Console.ReadKey() 
End Sub 
+0

** करने के लिए 2 सेकंड ** 100 मिलियन ** अतिरिक्त। यह "किस तरह से 'लंबे समय तक उपयोग करने के विचार का समर्थन करता है? यह धीमा हो सकता है, लेकिन आप केवल गति की तुलना नहीं कर सकते हैं, प्रति सेकंड 50 मिलियन जोड़ों को अनुकूलन के लायक नहीं है। – weston

+1

231 * मिली * सेकंड, नहीं * सेकंड *। यह धीमा नहीं है, यह तेज़ है, ओपी क्या चाहता है। – smirkingman

+0

दशमलव के लिए 2010 मिलीसेकंड के बारे में बात कर रहे हैं। 2 सेकंड में यह 100 मिलियन है। यह एक तर्क है कि ओपी को बाधाओं के लिए कहीं और देखना चाहिए। यह तर्क नहीं है कि उन्हें 'लम्बे' का उपयोग करने के लिए फिर से लिखना चाहिए, इससे कोई फर्क नहीं पड़ता कि कितना तेज़ 'लंबा' है। – weston

0

की दुकान "पैसे" डबल का उपयोग कर। पार्सिंग इनपुट और प्रिंटिंग आउटपुट के अलावा, आपके पास मापने वाली एक ही गति है। आप 64 बिट पूर्णांक की सीमा को पार करते हैं। आपके पास एक विभाजन नहीं है जो छंटनी नहीं कर रहा है। नोट: डिवीजनों के बाद डबल परिणाम का उपयोग करने के लिए आप पर निर्भर है। यह मुझे आपकी आवश्यकताओं के लिए सबसे आसान दृष्टिकोण लगता है।

2

प्रश्न पर अच्छी तरह से चर्चा की गई है, लेकिन चूंकि मैं थोड़ी देर के लिए इस समस्या को खो रहा था, इसलिए मैं अपने कुछ परिणामों को साझा करना चाहता हूं।

समस्या परिभाषा: दशमलव युगल तुलना में बहुत धीमी होने के लिए जाना जाता है लेकिन वित्तीय अनुप्रयोगों किसी भी कलाकृतियों कि पैदा होती है जब गणना युगल पर प्रदर्शन कर रहे हैं बर्दाश्त नहीं कर सकते।

रिसर्च

मेरा उद्देश्य नाव की दिशा वाला संख्या के भंडारण के विभिन्न दृष्टिकोणों को मापने के लिए और एक निष्कर्ष जो एक हमारे आवेदन के लिए इस्तेमाल किया जाना चाहिए बनाने के लिए किया गया था।

अगर हमारे लिए Int64 का उपयोग निश्चित परिशुद्धता के साथ फ़्लोटिंग पॉइंट नंबरों को स्टोर करने के लिए स्वीकार्य था। 10^6 का गुणक हमें दोनों दे रहा था: बड़ी संख्या में स्टोर करने के लिए भिन्न संख्याओं को भरने और बड़ी मात्रा में स्टोर करने के लिए पर्याप्त अंक। बेशक, आपको इस दृष्टिकोण के साथ सावधान रहना होगा (गुणा और विभाजन संचालन मुश्किल हो सकता है), लेकिन हम तैयार थे और इस दृष्टिकोण को मापना चाहते थे। संभावित गणना त्रुटियों और अतिप्रवाहों को छोड़कर आपको एक बात ध्यान में रखना है, आमतौर पर आप उन लंबी संख्याओं को सार्वजनिक एपीआई में बेनकाब नहीं कर सकते हैं। तो सभी आंतरिक गणना लंबे समय से की जा सकती है लेकिन उपयोगकर्ता को संख्या भेजने से पहले उन्हें कुछ और दोस्ताना रूपांतरित कर दिया जाना चाहिए।

मैंने एक साधारण प्रोटोटाइप वर्ग लागू किया है जो दशमलव-जैसी संरचना के लिए एक लंबा मूल्य लपेटता है (इसे Money कहा जाता है) और इसे माप में जोड़ा गया।

public struct Money : IComparable 
{ 
    private readonly long _value; 

    public const long Multiplier = 1000000; 
    private const decimal ReverseMultiplier = 0.000001m; 

    public Money(long value) 
    { 
     _value = value; 
    } 

    public static explicit operator Money(decimal d) 
    { 
     return new Money(Decimal.ToInt64(d * Multiplier)); 
    } 

    public static implicit operator decimal (Money m) 
    { 
     return m._value * ReverseMultiplier; 
    } 

    public static explicit operator Money(double d) 
    { 
     return new Money(Convert.ToInt64(d * Multiplier)); 
    } 

    public static explicit operator double (Money m) 
    { 
     return Convert.ToDouble(m._value * ReverseMultiplier); 
    } 

    public static bool operator ==(Money m1, Money m2) 
    { 
     return m1._value == m2._value; 
    } 

    public static bool operator !=(Money m1, Money m2) 
    { 
     return m1._value != m2._value; 
    } 

    public static Money operator +(Money d1, Money d2) 
    { 
     return new Money(d1._value + d2._value); 
    } 

    public static Money operator -(Money d1, Money d2) 
    { 
     return new Money(d1._value - d2._value); 
    } 

    public static Money operator *(Money d1, Money d2) 
    { 
     return new Money(d1._value * d2._value/Multiplier); 
    } 

    public static Money operator /(Money d1, Money d2) 
    { 
     return new Money(d1._value/d2._value * Multiplier); 
    } 

    public static bool operator <(Money d1, Money d2) 
    { 
     return d1._value < d2._value; 
    } 

    public static bool operator <=(Money d1, Money d2) 
    { 
     return d1._value <= d2._value; 
    } 

    public static bool operator >(Money d1, Money d2) 
    { 
     return d1._value > d2._value; 
    } 

    public static bool operator >=(Money d1, Money d2) 
    { 
     return d1._value >= d2._value; 
    } 

    public override bool Equals(object o) 
    { 
     if (!(o is Money)) 
      return false; 

     return this == (Money)o; 
    } 

    public override int GetHashCode() 
    { 
     return _value.GetHashCode(); 
    } 

    public int CompareTo(object obj) 
    { 
     if (obj == null) 
      return 1; 

     if (!(obj is Money)) 
      throw new ArgumentException("Cannot compare money."); 

     Money other = (Money)obj; 
     return _value.CompareTo(other._value); 
    } 

    public override string ToString() 
    { 
     return ((decimal) this).ToString(CultureInfo.InvariantCulture); 
    } 
} 

प्रयोग

मैं संचालन निम्नलिखित मापा: इसके अलावा, घटाव, गुणा, विभाजन, समानता तुलना और रिश्तेदार (अधिक से अधिक/कम) तुलना। मैं निम्नलिखित प्रकारों पर परिचालन को माप रहा था: double, long, decimal और Money। प्रत्येक ऑपरेशन 1.000.000 बार किया गया था। सभी संख्याओं को सरणी में पूर्व-आवंटित किया गया था, इसलिए decimal और Money के रचनाकारों में कस्टम कोड को कॉल करना परिणाम को प्रभावित नहीं करना चाहिए।

Added moneys in 5.445 ms 
Added decimals in 26.23 ms 
Added doubles in 2.3925 ms 
Added longs in 1.6494 ms 

Subtracted moneys in 5.6425 ms 
Subtracted decimals in 31.5431 ms 
Subtracted doubles in 1.7022 ms 
Subtracted longs in 1.7008 ms 

Multiplied moneys in 20.4474 ms 
Multiplied decimals in 24.9457 ms 
Multiplied doubles in 1.6997 ms 
Multiplied longs in 1.699 ms 

Divided moneys in 15.2841 ms 
Divided decimals in 229.7391 ms 
Divided doubles in 7.2264 ms 
Divided longs in 8.6903 ms 

Equility compared moneys in 5.3652 ms 
Equility compared decimals in 29.003 ms 
Equility compared doubles in 1.727 ms 
Equility compared longs in 1.7547 ms 

Relationally compared moneys in 9.0285 ms 
Relationally compared decimals in 29.2716 ms 
Relationally compared doubles in 1.7186 ms 
Relationally compared longs in 1.7321 ms 

निष्कर्ष

  1. अलावा, घटाव, गुणा, decimal पर तुलना संचालन long या double पर कार्रवाई की तुलना में ~ 15 गुना धीमी कर रहे हैं; विभाजन ~ 30 गुना धीमा है।
  2. Decimal का प्रदर्शन Decimal के प्रदर्शन से बेहतर है लेकिन सीएलआर से समर्थन की कमी के कारण double और long के प्रदर्शन से काफी खराब है।
  3. पूर्ण संख्या में Decimal पर गणना करना काफी तेज है: प्रति सेकंड 40.000.000 संचालन।

सलाह

  1. जब तक आप एक बहुत भारी गणना मामला है, दशमलव का उपयोग करें। सापेक्ष संख्या में वे लंबे और युगल से धीमे होते हैं, लेकिन पूर्ण संख्या अच्छी लगती है।
  2. सीएलआर से समर्थन के उत्थान के कारण Decimal को अपनी खुद की संरचना के साथ फिर से कार्यान्वित करने में बहुत कुछ नहीं है। आप इसे Decimal से तेज़ी से बना सकते हैं लेकिन यह double जितना तेज़ नहीं होगा।
  3. यदि Decimal का प्रदर्शन आपके आवेदन के लिए पर्याप्त नहीं है, तो आप निश्चित गणना के साथ अपनी गणना long पर स्विच करने पर विचार करना चाहेंगे। परिणाम को ग्राहक को वापस करने से पहले इसे Decimal में परिवर्तित किया जाना चाहिए।
संबंधित मुद्दे