एक विधि के लिए मेमोरी बनाम प्रदर्शन की गति के लिए कब अनुकूलित करें?


107

मैंने हाल ही में अमेज़न पर साक्षात्कार किया है। एक कोडिंग सत्र के दौरान, साक्षात्कारकर्ता ने पूछा कि मैंने एक विधि में एक चर की घोषणा क्यों की। मैंने अपनी प्रक्रिया को समझाया और उन्होंने मुझे कम चर के साथ एक ही समस्या को हल करने के लिए चुनौती दी। उदाहरण के लिए (यह साक्षात्कार से नहीं था), मैंने विधि ए के साथ शुरुआत की और फिर इसे हटाकर मेथड बी में सुधार किया । वह प्रसन्न था और उसने कहा कि इस विधि से स्मृति उपयोग कम हो जाएगा।int s

मैं इसके पीछे के तर्क को समझता हूं, लेकिन मेरा सवाल यह है:

विधि ए बनाम विधि बी, और इसके विपरीत का उपयोग करना कब उचित है?

आप देख सकते हैं कि मेथड ए अधिक मेमोरी उपयोग करने वाला है, क्योंकि int sघोषित किया गया है, लेकिन इसे केवल एक गणना करना है, अर्थात a + b। दूसरी ओर, विधि बी में मेमोरी का उपयोग कम होता है, लेकिन दो गणनाएं करनी होती हैं, अर्थात a + bदो बार। मैं एक तकनीक का दूसरे पर इस्तेमाल कब करूं? या, एक तकनीक हमेशा दूसरे पर पसंद की जाती है? दो तरीकों का मूल्यांकन करते समय किन बातों पर ध्यान देना चाहिए?

विधि A:

private bool IsSumInRange(int a, int b)
{
    int s = a + b;

    if (s > 1000 || s < -1000) return false;
    else return true;
}

विधि B:

private bool IsSumInRange(int a, int b)
{
    if (a + b > 1000 || a + b < -1000) return false;
    else return true;
}

229
मैं शर्त लगाने को तैयार हूं कि एक आधुनिक संकलक उन दोनों मामलों के लिए एक ही विधानसभा उत्पन्न करेगा।
17 की 26

12
मैंने मूल स्थिति में प्रश्न को रोलबैक कर दिया है, क्योंकि आपके संपादन ने मेरे उत्तर को अमान्य कर दिया है - कृपया ऐसा न करें! यदि आप एक प्रश्न पूछते हैं कि अपने कोड को कैसे बेहतर बनाया जाए, तो प्रश्न को दिखाए गए तरीके से कोड में सुधार करके न बदलें - इससे उत्तर निरर्थक लगते हैं।
डॉक्टर ब्राउन

76
एक सेकंड रुको, उन्होंने int sऊपरी और निचले सीमा के लिए उन जादुई संख्याओं के साथ पूरी तरह से ठीक होने के दौरान छुटकारा पाने के लिए कहा ?
null

34
याद रखें: अनुकूलन करने से पहले प्रोफ़ाइल। आधुनिक संकलक के साथ, विधि ए और विधि बी को एक ही कोड (उच्च अनुकूलन स्तरों का उपयोग करके) के लिए अनुकूलित किया जा सकता है। इसके अलावा, आधुनिक प्रोसेसर के साथ, उनके पास निर्देश हो सकते हैं जो एकल ऑपरेशन में जोड़ से अधिक प्रदर्शन करते हैं।
थॉमस मैथ्यूज

142
न तो; पठनीयता के लिए अनुकूलन।
एंडी

जवाबों:


148

क्या हो सकता है या नहीं हो सकता है, इसके बारे में अटकलें लगाने के बजाय, आइए हम देखेंगे? मुझे C ++ का उपयोग करना होगा क्योंकि मेरे पास C # संकलक का काम नहीं है (हालाँकि VisualMelon से C # उदाहरण देखें ), लेकिन मुझे यकीन है कि समान सिद्धांत समान रूप से लागू होते हैं।

हम साक्षात्कार में आपके सामने आए दो विकल्पों को शामिल करेंगे। हम absकुछ उत्तरों द्वारा सुझाए गए संस्करण का भी उपयोग करेंगे।

#include <cstdlib>

bool IsSumInRangeWithVar(int a, int b)
{
    int s = a + b;

    if (s > 1000 || s < -1000) return false;
    else return true;
}

bool IsSumInRangeWithoutVar(int a, int b)
{
    if (a + b > 1000 || a + b < -1000) return false;
    else return true;
}

bool IsSumInRangeSuperOptimized(int a, int b) {
    return (abs(a + b) < 1000);
}

अब इसे बिना किसी अनुकूलन के संकलित करें: g++ -c -o test.o test.cpp

अब हम ठीक से देख सकते हैं कि यह क्या उत्पन्न करता है: objdump -d test.o

0000000000000000 <_Z19IsSumInRangeWithVarii>:
   0:   55                      push   %rbp              # begin a call frame
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d ec                mov    %edi,-0x14(%rbp)  # save first argument (a) on stack
   7:   89 75 e8                mov    %esi,-0x18(%rbp)  # save b on stack
   a:   8b 55 ec                mov    -0x14(%rbp),%edx  # load a and b into edx
   d:   8b 45 e8                mov    -0x18(%rbp),%eax  # load b into eax
  10:   01 d0                   add    %edx,%eax         # add a and b
  12:   89 45 fc                mov    %eax,-0x4(%rbp)   # save result as s on stack
  15:   81 7d fc e8 03 00 00    cmpl   $0x3e8,-0x4(%rbp) # compare s to 1000
  1c:   7f 09                   jg     27                # jump to 27 if it's greater
  1e:   81 7d fc 18 fc ff ff    cmpl   $0xfffffc18,-0x4(%rbp) # compare s to -1000
  25:   7d 07                   jge    2e                # jump to 2e if it's greater or equal
  27:   b8 00 00 00 00          mov    $0x0,%eax         # put 0 (false) in eax, which will be the return value
  2c:   eb 05                   jmp    33 <_Z19IsSumInRangeWithVarii+0x33>
  2e:   b8 01 00 00 00          mov    $0x1,%eax         # put 1 (true) in eax
  33:   5d                      pop    %rbp
  34:   c3                      retq

0000000000000035 <_Z22IsSumInRangeWithoutVarii>:
  35:   55                      push   %rbp
  36:   48 89 e5                mov    %rsp,%rbp
  39:   89 7d fc                mov    %edi,-0x4(%rbp)
  3c:   89 75 f8                mov    %esi,-0x8(%rbp)
  3f:   8b 55 fc                mov    -0x4(%rbp),%edx
  42:   8b 45 f8                mov    -0x8(%rbp),%eax  # same as before
  45:   01 d0                   add    %edx,%eax
  # note: unlike other implementation, result is not saved
  47:   3d e8 03 00 00          cmp    $0x3e8,%eax      # compare to 1000
  4c:   7f 0f                   jg     5d <_Z22IsSumInRangeWithoutVarii+0x28>
  4e:   8b 55 fc                mov    -0x4(%rbp),%edx  # since s wasn't saved, load a and b from the stack again
  51:   8b 45 f8                mov    -0x8(%rbp),%eax
  54:   01 d0                   add    %edx,%eax
  56:   3d 18 fc ff ff          cmp    $0xfffffc18,%eax # compare to -1000
  5b:   7d 07                   jge    64 <_Z22IsSumInRangeWithoutVarii+0x2f>
  5d:   b8 00 00 00 00          mov    $0x0,%eax
  62:   eb 05                   jmp    69 <_Z22IsSumInRangeWithoutVarii+0x34>
  64:   b8 01 00 00 00          mov    $0x1,%eax
  69:   5d                      pop    %rbp
  6a:   c3                      retq

000000000000006b <_Z26IsSumInRangeSuperOptimizedii>:
  6b:   55                      push   %rbp
  6c:   48 89 e5                mov    %rsp,%rbp
  6f:   89 7d fc                mov    %edi,-0x4(%rbp)
  72:   89 75 f8                mov    %esi,-0x8(%rbp)
  75:   8b 55 fc                mov    -0x4(%rbp),%edx
  78:   8b 45 f8                mov    -0x8(%rbp),%eax
  7b:   01 d0                   add    %edx,%eax
  7d:   3d 18 fc ff ff          cmp    $0xfffffc18,%eax
  82:   7c 16                   jl     9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
  84:   8b 55 fc                mov    -0x4(%rbp),%edx
  87:   8b 45 f8                mov    -0x8(%rbp),%eax
  8a:   01 d0                   add    %edx,%eax
  8c:   3d e8 03 00 00          cmp    $0x3e8,%eax
  91:   7f 07                   jg     9a <_Z26IsSumInRangeSuperOptimizedii+0x2f>
  93:   b8 01 00 00 00          mov    $0x1,%eax
  98:   eb 05                   jmp    9f <_Z26IsSumInRangeSuperOptimizedii+0x34>
  9a:   b8 00 00 00 00          mov    $0x0,%eax
  9f:   5d                      pop    %rbp
  a0:   c3                      retq

हम ढेर पतों से देख सकते हैं (उदाहरण के लिए, -0x4में mov %edi,-0x4(%rbp)बनाम -0x14में mov %edi,-0x14(%rbp)) है कि IsSumInRangeWithVar()ढेर पर 16 अतिरिक्त बाइट्स का उपयोग करता है।

क्योंकि IsSumInRangeWithoutVar()इंटरमीडिएट मान को संग्रहीत करने के लिए स्टैक पर कोई स्थान आवंटित नहीं sकिया गया है, जिसके परिणामस्वरूप इसे 2 निर्देश लंबा हो गया है।

मजेदार, IsSumInRangeSuperOptimized()बहुत कुछ दिखता है IsSumInRangeWithoutVar(), सिवाय इसके कि यह पहले -1000 और 1000 सेकंड की तुलना में है।

अब केवल सबसे बुनियादी अनुकूलन के साथ संकलित करें g++ -O1 -c -o test.o test.cpp:। परिणाम:

0000000000000000 <_Z19IsSumInRangeWithVarii>:
   0:   8d 84 37 e8 03 00 00    lea    0x3e8(%rdi,%rsi,1),%eax
   7:   3d d0 07 00 00          cmp    $0x7d0,%eax
   c:   0f 96 c0                setbe  %al
   f:   c3                      retq

0000000000000010 <_Z22IsSumInRangeWithoutVarii>:
  10:   8d 84 37 e8 03 00 00    lea    0x3e8(%rdi,%rsi,1),%eax
  17:   3d d0 07 00 00          cmp    $0x7d0,%eax
  1c:   0f 96 c0                setbe  %al
  1f:   c3                      retq

0000000000000020 <_Z26IsSumInRangeSuperOptimizedii>:
  20:   8d 84 37 e8 03 00 00    lea    0x3e8(%rdi,%rsi,1),%eax
  27:   3d d0 07 00 00          cmp    $0x7d0,%eax
  2c:   0f 96 c0                setbe  %al
  2f:   c3                      retq

क्या आप इसे देखेंगे: प्रत्येक संस्करण समान है । कंपाइलर कुछ काफी चालाक करने में सक्षम है: एक अहस्ताक्षरित तुलना abs(a + b) <= 1000करने a + b + 1000 <= 2000पर विचार करने के बराबर है setbe, इसलिए एक नकारात्मक संख्या एक बहुत बड़ी सकारात्मक संख्या बन जाती है। leaअनुदेश वास्तव में एक अनुदेश में इन सभी अतिरिक्त करते हैं, और सभी सशर्त शाखाओं समाप्त कर सकते हैं।

अपने प्रश्न का उत्तर देने के लिए, लगभग हमेशा ऑप्टिमाइज़ करने की चीज़ मेमोरी या गति नहीं है, बल्कि पठनीयता है । पठन कोड इसे लिखने की तुलना में बहुत कठिन है, और कोड को पढ़ने के लिए जिसे "अनुकूलित" करने के लिए तैयार किया गया है, यह उस कोड को पढ़ने की तुलना में बहुत कठिन है जिसे स्पष्ट लिखा गया है। अधिक बार नहीं, ये "अनुकूलन" नगण्य हैं, या जैसा कि इस मामले में प्रदर्शन पर शून्य वास्तविक प्रभाव है।


इस सवाल का पालन करें, जब यह कोड संकलित भाषा के बजाय एक संकलित भाषा में है तो क्या परिवर्तन होता है? फिर, क्या अनुकूलन मायने रखता है या इसका एक ही परिणाम है?

चलो उपाय करो! मैंने पायथन में उदाहरण दिए हैं:

def IsSumInRangeWithVar(a, b):
    s = a + b
    if s > 1000 or s < -1000:
        return False
    else:
        return True

def IsSumInRangeWithoutVar(a, b):
    if a + b > 1000 or a + b < -1000:
        return False
    else:
        return True

def IsSumInRangeSuperOptimized(a, b):
    return abs(a + b) <= 1000

from dis import dis
print('IsSumInRangeWithVar')
dis(IsSumInRangeWithVar)

print('\nIsSumInRangeWithoutVar')
dis(IsSumInRangeWithoutVar)

print('\nIsSumInRangeSuperOptimized')
dis(IsSumInRangeSuperOptimized)

print('\nBenchmarking')
import timeit
print('IsSumInRangeWithVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeWithoutVar: %fs' % (min(timeit.repeat(lambda: IsSumInRangeWithoutVar(42, 42), repeat=50, number=100000)),))
print('IsSumInRangeSuperOptimized: %fs' % (min(timeit.repeat(lambda: IsSumInRangeSuperOptimized(42, 42), repeat=50, number=100000)),))

पायथन 3.5.2 के साथ चलाएँ, इससे आउटपुट उत्पन्न होता है:

IsSumInRangeWithVar
  2           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 BINARY_ADD
              7 STORE_FAST               2 (s)

  3          10 LOAD_FAST                2 (s)
             13 LOAD_CONST               1 (1000)
             16 COMPARE_OP               4 (>)
             19 POP_JUMP_IF_TRUE        34
             22 LOAD_FAST                2 (s)
             25 LOAD_CONST               4 (-1000)
             28 COMPARE_OP               0 (<)
             31 POP_JUMP_IF_FALSE       38

  4     >>   34 LOAD_CONST               2 (False)
             37 RETURN_VALUE

  6     >>   38 LOAD_CONST               3 (True)
             41 RETURN_VALUE
             42 LOAD_CONST               0 (None)
             45 RETURN_VALUE

IsSumInRangeWithoutVar
  9           0 LOAD_FAST                0 (a)
              3 LOAD_FAST                1 (b)
              6 BINARY_ADD
              7 LOAD_CONST               1 (1000)
             10 COMPARE_OP               4 (>)
             13 POP_JUMP_IF_TRUE        32
             16 LOAD_FAST                0 (a)
             19 LOAD_FAST                1 (b)
             22 BINARY_ADD
             23 LOAD_CONST               4 (-1000)
             26 COMPARE_OP               0 (<)
             29 POP_JUMP_IF_FALSE       36

 10     >>   32 LOAD_CONST               2 (False)
             35 RETURN_VALUE

 12     >>   36 LOAD_CONST               3 (True)
             39 RETURN_VALUE
             40 LOAD_CONST               0 (None)
             43 RETURN_VALUE

IsSumInRangeSuperOptimized
 15           0 LOAD_GLOBAL              0 (abs)
              3 LOAD_FAST                0 (a)
              6 LOAD_FAST                1 (b)
              9 BINARY_ADD
             10 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             13 LOAD_CONST               1 (1000)
             16 COMPARE_OP               1 (<=)
             19 RETURN_VALUE

Benchmarking
IsSumInRangeWithVar: 0.019361s
IsSumInRangeWithoutVar: 0.020917s
IsSumInRangeSuperOptimized: 0.020171s

बाइटकोड "कंपाइलर" अनुकूलन के रास्ते में बहुत कुछ नहीं करता है, क्योंकि पायथन में बेचैनी बहुत दिलचस्प नहीं है।

तीनों कार्यों का प्रदर्शन लगभग समान है। IsSumInRangeWithVar()सीमांत गति प्राप्त होने के कारण हम इसके साथ जा सकते हैं । यद्यपि मैं जोड़ूंगा क्योंकि मैं विभिन्न मापदंडों की कोशिश कर रहा था timeit, कभी-कभी IsSumInRangeSuperOptimized()सबसे तेजी से निकलता था, इसलिए मुझे संदेह है कि यह अंतर के लिए जिम्मेदार बाहरी कारक हो सकते हैं, बजाय किसी भी कार्यान्वयन के आंतरिक लाभ के।

यदि यह वास्तव में प्रदर्शन महत्वपूर्ण कोड है, तो एक व्याख्या की गई भाषा बस एक बहुत ही खराब विकल्प है। Pypy के साथ एक ही कार्यक्रम चलाना, मुझे मिलता है:

IsSumInRangeWithVar: 0.000180s
IsSumInRangeWithoutVar: 0.001175s
IsSumInRangeSuperOptimized: 0.001306s

बस पेपी का उपयोग करते हुए, जो इंटरप्रेटर ओवरहेड के बहुत सारे को खत्म करने के लिए जेआईटी संकलन का उपयोग करता है, ने परिमाण के 1 या 2 आदेशों के प्रदर्शन में सुधार किया है। मुझे यह देखकर काफी धक्का लगा कि मैं IsSumInRangeWithVar()दूसरों की तुलना में तेज़ी से बड़ा हुआ हूं । इसलिए मैंने बेंचमार्क का क्रम बदल दिया और फिर से भाग गया:

IsSumInRangeSuperOptimized: 0.000191s
IsSumInRangeWithoutVar: 0.001174s
IsSumInRangeWithVar: 0.001265s

इसलिए ऐसा लगता है कि यह वास्तव में कार्यान्वयन के बारे में कुछ भी नहीं है जो इसे तेज करता है, बल्कि उस क्रम में जिसमें मैं बेंचमार्किंग करता हूं!

मैं इसे और गहराई से खोदना पसंद करूंगा, क्योंकि ईमानदारी से मुझे नहीं पता कि ऐसा क्यों होता है। लेकिन मेरा मानना ​​है कि बिंदु बनाया गया है: सूक्ष्म-अनुकूलन जैसे कि एक मध्यवर्ती मान को एक चर के रूप में घोषित करना है या नहीं, शायद ही कभी प्रासंगिक है। एक व्याख्या की गई भाषा या अत्यधिक अनुकूलित संकलक के साथ, पहला उद्देश्य अभी भी स्पष्ट कोड लिखना है।

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

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


6
सी # में एक उदाहरण प्रदान करने के लिए: SharpLab दोनों विधियों के लिए समान ASM पैदा करता है (x86 पर डेस्कटॉप CLR v4.7.3130.00 (clr.dll))
VisualMelon

2
@VisualMelon अंतिम संस्कार पर्याप्त सकारात्मक जाँच: "वापसी ((a + b)> = -1000) और& ((a + b) <= 1000);" एक अलग परिणाम देता है। : sharplab.io/…
पीटर बी।

12
पठनीयता संभावित रूप से एक कार्यक्रम को अनुकूलित करने के लिए भी आसान बना सकती है। संकलक आसानी से समकक्ष तर्क का उपयोग करने के लिए फिर से लिख सकता है जैसे कि यह ऊपर है, केवल अगर यह वास्तव में यह पता लगा सकता है कि आप क्या करने की कोशिश कर रहे हैं। यदि आप बहुत सारे पुराने स्कूल के बिटकॉइन का उपयोग करते हैं , तो इन्ट्स और पॉइंटर्स के बीच आगे और पीछे कास्ट करें, म्यूटेबल स्टोरेज आदि का पुन: उपयोग करें। यह कंपाइलर के लिए बहुत कठिन हो सकता है कि वह यह साबित कर दे कि एक ट्रांसफॉर्मेशन बराबर है, और यह सिर्फ वही लिखा जाएगा जो आपने लिखा था। , जो हो सकता है।
लेउशेंको

1
@ अतिरिक्त देखें संपादित करें।
फिल फ्रॉस्ट

2
@ उत्तर: यह उत्तर वास्तव में आपको वही बता रहा है जो मैंने अपने उत्तर में लिखा था: जब आप एक सभ्य संकलक का उपयोग करते हैं तो कोई अंतर नहीं होता है, और इसके बजाय रीडिबिलीटी पर ध्यान केंद्रित करें। बेशक, यह बेहतर रूप से स्थापित दिखता है - शायद आप मुझ पर विश्वास करते हैं।
डॉक ब्राउन

67

उक्त प्रश्न का उत्तर देने के लिए:

एक विधि के लिए मेमोरी बनाम प्रदर्शन की गति के लिए कब अनुकूलित करें?

आपको दो चीजें स्थापित करनी होंगी:

  • आपके आवेदन को सीमित क्या है?
  • मैं उस संसाधन का सबसे अधिक लाभ कहां प्राप्त कर सकता हूं?

पहले प्रश्न का उत्तर देने के लिए, आपको यह जानना होगा कि आपके आवेदन की प्रदर्शन आवश्यकताएं क्या हैं। यदि कोई प्रदर्शन आवश्यकताएं नहीं हैं, तो एक तरह से या दूसरे को अनुकूलित करने का कोई कारण नहीं है। प्रदर्शन आवश्यकताओं से आपको "अच्छा पर्याप्त" के स्थान पर पहुंचने में मदद मिलती है।

आपके द्वारा प्रदान की गई विधि अपने आप ही किसी भी प्रदर्शन समस्या का कारण नहीं बनेगी, लेकिन शायद एक लूप के भीतर और बड़ी मात्रा में डेटा संसाधित करने के लिए, आपको इस बारे में थोड़ा अलग तरीके से सोचना शुरू करना होगा कि आप समस्या कैसे ला रहे हैं।

पता लगाना कि क्या आवेदन को सीमित कर रहा है

प्रदर्शन मॉनीटर के साथ अपने एप्लिकेशन के व्यवहार को देखना शुरू करें। सीपीयू, डिस्क, नेटवर्क और मेमोरी उपयोग पर नज़र रखें, जबकि यह चल रहा हो। एक या एक से अधिक वस्तुओं को अधिकतम किया जाएगा जबकि बाकी सब कुछ मामूली रूप से उपयोग किया जाता है - जब तक कि आप सही संतुलन नहीं मारते हैं, लेकिन यह लगभग कभी नहीं होता है)।

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

मान लें कि आप अपने CPU और डिस्क उपयोग को चरम पर देखते हैं। आप पहले "हॉट स्पॉट" या कोड की जांच करेंगे जिसे या तो बाकी की तुलना में अधिक बार कहा जाता है या प्रसंस्करण का काफी लंबा प्रतिशत लेता है।

यदि आपको कोई हॉट स्पॉट नहीं मिल रहा है, तो आप स्मृति को देखना शुरू करेंगे। शायद आप आवश्यकता से अधिक वस्तुओं का निर्माण कर रहे हैं और आपका कचरा संग्रह ओवरटाइम काम कर रहा है।

प्रदर्शन को पुनः प्राप्त करना

गुण - दोष की दृष्टि से सोचो। परिवर्तनों की निम्नलिखित सूची इस क्रम में है कि आपको निवेश पर कितना लाभ मिलेगा:

  • वास्तुकला: संचार चोक अंक के लिए देखें
  • एल्गोरिथम: जिस तरह से आप डेटा संसाधित करते हैं, उसे बदलने की आवश्यकता हो सकती है
  • हॉट स्पॉट: कम से कम कितनी बार आप हॉट स्पॉट कहते हैं, इससे बड़ा बोनस मिल सकता है
  • माइक्रो ऑप्टिमाइज़ेशन: यह आम नहीं है, लेकिन कभी-कभी आपको वास्तव में मामूली ट्विक्स (उदाहरण के लिए आपके द्वारा दिए गए उदाहरण) के बारे में सोचने की आवश्यकता होती है, खासकर अगर यह आपके कोड में एक गर्म स्थान है।

इस तरह की स्थितियों में, आपको वैज्ञानिक पद्धति को लागू करना होगा। एक परिकल्पना के साथ आओ, परिवर्तन करें, और इसका परीक्षण करें। यदि आप अपने प्रदर्शन लक्ष्यों को पूरा करते हैं, तो आप कर रहे हैं। यदि नहीं, तो सूची में अगली चीज़ पर जाएं।


बोल्ड में सवाल का जवाब:

विधि ए बनाम विधि बी, और इसके विपरीत का उपयोग करना कब उचित है?

ईमानदारी से, यह प्रदर्शन या स्मृति समस्याओं से निपटने की कोशिश में आखिरी कदम है। मेथड ए बनाम मेथड बी का प्रभाव भाषा और मंच (कुछ मामलों में) के आधार पर वास्तव में अलग होगा ।

किसी भी संकलित भाषा के साथ आधे रास्ते के लिए एक सभ्य आशावादी उन संरचनाओं में से किसी के साथ समान कोड उत्पन्न करेगा। हालांकि उन मान्यताओं को मालिकाना और खिलौना भाषाओं में सही नहीं रहना पड़ता है जिनके पास एक अनुकूलक नहीं है।

सटीक रूप से जिसका बेहतर प्रभाव पड़ेगा वह इस बात पर निर्भर करता है कि sumस्टैक वेरिएबल है या हीप वेरिएबल। यह एक भाषा कार्यान्वयन विकल्प है। उदाहरण के लिए C, C ++ और Java में, संख्या आदिम intडिफ़ॉल्ट रूप से स्टैक चर हैं। स्टैक वेरिएबल को असाइन करने से आपके कोड में अधिक मेमोरी इफेक्ट नहीं है, जितना कि आप पूरी तरह से इनबिल्ड कोड के साथ होगा।

अन्य आशाएँ जो आपको C पुस्तकालयों (विशेष रूप से पुराने) में मिल सकती हैं, जहाँ आपको पहले या नीचे एक 2 आयामी सरणी की प्रतिलिपि बनाने के बीच निर्णय लेना पड़ सकता है, वह है प्लेटफ़ॉर्म आश्रित अनुकूलन। यह कुछ ज्ञान की आवश्यकता है कि आप जिस चिपसेट को लक्षित कर रहे हैं वह मेमोरी एक्सेस को कैसे अनुकूलित करता है। आर्किटेक्चर के बीच सूक्ष्म अंतर हैं।

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


2
यह उत्तर मेरे प्रश्न पर सबसे अधिक ध्यान केंद्रित करता है और मेरे कोडिंग उदाहरणों पर पकड़ में नहीं आता है, अर्थात विधि ए और विधि बी
बेहतर बजट

18
मुझे ऐसा लगता है कि यह "आप प्रदर्शन अड़चनों को कैसे संबोधित करते हैं" का सामान्य उत्तर है, लेकिन आपको इस पद्धति का उपयोग करके 4 या 5 चर वाले किसी विशेष फ़ंक्शन से सापेक्ष मेमोरी उपयोग की पहचान करने के लिए कठिन दबाया जाएगा। मैं यह भी सवाल करता हूं कि अनुकूलन का यह स्तर कितना प्रासंगिक है जब कंपाइलर (या दुभाषिया) इस दूर का अनुकूलन कर सकता है या नहीं कर सकता है।
एरिक

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

@BerinLoritsch फिर, सामान्य तौर पर मैं आपसे सहमत हूं, लेकिन इस विशिष्ट मामले में मैं नहीं। मैंने अपना उत्तर प्रदान कर दिया है, लेकिन मैंने व्यक्तिगत रूप से ऐसा कोई उपकरण नहीं देखा है, जो किसी फ़ंक्शन के स्टैक मेमोरी साइज़ से संबंधित प्रदर्शन समस्याओं की संभावित पहचान करने के लिए आपको ध्वजांकित करेगा या यहां तक ​​कि आपको रास्ता भी देगा।
एरिक

@DocBrown, मैंने इसे फिर से बनाया है। दूसरे प्रश्न के संबंध में, मैं आपसे बहुत सहमत हूं।
बेरिन लोरिट्श

45

"यह स्मृति को कम करेगा" - उन्हें, नहीं। यहां तक ​​कि अगर यह सच होगा (जो, किसी भी सभ्य संकलक के लिए नहीं है), तो अंतर शायद किसी भी वास्तविक दुनिया की स्थिति के लिए नगण्य होगा।

हालाँकि, मैं विधि A * (विधि A को थोड़े बदलाव के साथ) उपयोग करने की सलाह दूंगा:

private bool IsSumInRange(int a, int b)
{
    int sum = a + b;

    if (sum > 1000 || sum < -1000) return false;
    else return true;
    // (yes, the former statement could be cleaned up to
    // return abs(sum)<=1000;
    // but let's ignore this for a moment)
}

लेकिन दो पूरी तरह से अलग कारणों के लिए:

  • चर को sएक नाम देने से , कोड स्पष्ट हो जाता है

  • यह कोड में दो बार समान योग तर्क देने से बचता है, इसलिए कोड अधिक DRY हो जाता है, जिसका अर्थ है कि परिवर्तन के लिए कम त्रुटि की संभावना है।


36
मैं इसे और भी साफ करूंगा और "रिटर्न योग> -1000 && योग <1000" के साथ जाऊंगा।
17 की 26

36
@ किसी भी सभ्य आशावादी sumचर के लिए एक सीपीयू रजिस्टर का उपयोग करेगा , इस प्रकार शून्य मेमोरी उपयोग के लिए अग्रणी। और भले ही यह "पत्ती" विधि में स्मृति का केवल एक शब्द नहीं है। अविश्वसनीय रूप से स्मृति-बेकार जावा या सी # को ध्यान में रखते हुए उनके जीसी और ऑब्जेक्ट मॉडल के कारण हो सकता है, स्थानीय intचर वास्तव में किसी भी ध्यान देने योग्य स्मृति का उपयोग नहीं करता है। यह व्यर्थ सूक्ष्म अनुकूलन है।
अमोन

10
@ कोरी: यदि यह " थोड़ा अधिक जटिल" है, तो यह संभवतः "ध्यान देने योग्य स्मृति उपयोग" नहीं बन जाएगा। हो सकता है कि यदि आप एक और अधिक जटिल उदाहरण का निर्माण करते हैं, लेकिन यह एक अलग सवाल है। ध्यान दें, सिर्फ इसलिए कि आप एक अभिव्यक्ति के लिए एक विशिष्ट चर नहीं बनाते हैं, जटिल मध्यवर्ती परिणामों के लिए, रन टाइम वातावरण अभी भी आंतरिक रूप से अस्थायी ऑब्जेक्ट बना सकता है, इसलिए यह पूरी तरह से भाषा, पर्यावरण, अनुकूलन स्तर, और के विवरण पर निर्भर करता है जिसे आप "ध्यान देने योग्य" कहते हैं।
डॉक्टर ब्राउन

8
उपरोक्त बिंदुओं के अलावा, मुझे पूरा यकीन है कि स्टोर करने के लिए C # / Java कैसे चुनता sumहै एक कार्यान्वयन विवरण होगा और मुझे संदेह है कि कोई भी एक ठोस मामला बना सकता है या नहीं कि एक मूर्खतापूर्ण चाल की तरह एक स्थानीय से बचने के intलिए ऐसा होगा या लंबी अवधि में स्मृति उपयोग की राशि। IMO पठनीयता अधिक महत्वपूर्ण है। पठनीयता व्यक्तिपरक हो सकती है, लेकिन एफडब्ल्यूआईडब्ल्यू, व्यक्तिगत रूप से मैं आपको एक ही गणना दो बार नहीं करना चाहूंगा, न कि सीपीयू उपयोग के लिए, बल्कि इसलिए कि जब मुझे बग की तलाश है तो मुझे केवल एक बार आपके अतिरिक्त निरीक्षण करना होगा।
jrh

2
... यह भी ध्यान दें कि सामान्य रूप से एकत्र की गई कचरा एक अप्रत्याशित, "स्मृति का मंथन" है कि (सी # के लिए) केवल जरूरत पड़ने पर साफ किया जा सकता है , मुझे याद है कि एक ऐसा कार्यक्रम बनाया गया था जिसमें रैम के गीगाबाइट आवंटित किए गए थे और यह केवल शुरू हुआ था " सफाई "खुद के बाद जब स्मृति दुर्लभ हो गई। यदि GC को चलाने की आवश्यकता नहीं है, तो यह अपना मधुर समय ले सकता है और अधिक दबाव वाले मामलों के लिए अपने CPU को बचा सकता है।
jrh

35

आप उन दोनों से बेहतर कर सकते हैं

return (abs(a + b) > 1000);

अधिकांश प्रोसेसर (और इसलिए संकलक) एक ही ऑपरेशन में एब्स () कर सकते हैं। आपके पास न केवल कम रकम है, बल्कि कम तुलनाएं भी हैं, जो आम तौर पर अधिक कम्प्यूटेशनल रूप से महंगी हैं। यह ब्रांचिंग को भी हटा देता है, जो अधिकांश प्रोसेसर पर बहुत खराब होता है क्योंकि यह पाइपलाइनिंग को रोकना संभव बनाता है।

साक्षात्कारकर्ता, जैसा कि अन्य उत्तरों ने कहा है, संयंत्र जीवन है और इसका कोई व्यवसाय नहीं है जो एक तकनीकी साक्षात्कार का आयोजन कर रहा है।

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

संपादित करें FabioTurati सही ढंग से इंगित करता है कि यह मूल, (मेरी गलती!) के विपरीत तर्क भावना है, और यह कि नथ के उद्धरण से एक और प्रभाव दिखाता है जहां हम कोड को तोड़ने का जोखिम उठाते हैं, जबकि हम इसे अनुकूलित करने की कोशिश कर रहे हैं।


2
@ निश्चित रूप से, मुझे पूरा यकीन है कि ग्राहम ने अनुरोध किया था "उन्होंने मुझे कम चर के साथ एक ही समस्या को हल करने के लिए चुनौती दी" । अगर मैं साक्षात्कारकर्ता होगा, मुझे लगता है कि इस सवाल का जवाब उम्मीद थी, नहीं चलती a+bमें ifऔर उसे दो बार कर रही है। आप इसे गलत समझते हैं "वह प्रसन्न था और उसने कहा कि यह इस विधि द्वारा स्मृति उपयोग को कम कर देगा" - वह आपके लिए अच्छा था, स्मृति के बारे में इस अर्थहीन स्पष्टीकरण से अपनी निराशा छिपा रहा था। यहां सवाल पूछने के लिए आपको इसे गंभीरता से नहीं लेना चाहिए। क्या आपको नौकरी मिली? मेरा अनुमान है कि आपने नहीं किया :-(
सिनट्रार

1
आप एक ही समय में 2 रूपांतरण लागू कर रहे हैं: आपने 2 स्थितियों को 1 में बदल दिया है, का उपयोग कर रहे हैं abs(), और आपके पास एक भी है return, एक होने के बजाय जब स्थिति सही है ("यदि शाखा") और दूसरा तब जब यह गलत है ( "और शाखा")। जब आप इस तरह से कोड बदलते हैं, तो सावधान रहें: अनजाने में एक फ़ंक्शन लिखने का जोखिम है जो झूठे और वापसी के समय सही होना चाहिए। जो वास्तव में यहाँ हुआ है। मुझे पता है कि आप एक और बात पर ध्यान केंद्रित कर रहे थे, और आपने इस पर एक अच्छा काम किया है। फिर भी, यह आसानी से आपको काम दे सकता था ...
Fabio Turati

2
@FabioTurati अच्छी तरह से देखा - धन्यवाद! मैं जवाब अपडेट कर दूंगा। और यह रिफैक्टिंग और अनुकूलन के बारे में एक अच्छा बिंदु है, जो नथ के उद्धरण को और भी अधिक प्रासंगिक बनाता है। जोखिम लेने से पहले हमें यह साबित करना चाहिए कि हमें अनुकूलन की आवश्यकता है।
ग्राहम

2
अधिकांश प्रोसेसर (और इसलिए संकलक) एक ही ऑपरेशन में एब्स () कर सकते हैं। दुर्भाग्य से पूर्णांक के लिए मामला नहीं है। ARM64 में एक सशर्त नकारात्मक है यदि यह उपयोग कर सकता है यदि झंडे पहले से सेट हैं adds, और ARM ने रिवर्स-सब ( rsblt= रिवर्स-उप यदि कम-थाह) की भविष्यवाणी की है, लेकिन बाकी सब को लागू करने abs(a+b)या करने के लिए कई अतिरिक्त निर्देशों की आवश्यकता होती है abs(a)godbolt.org/z/Ok_Con x86, ARM, AArch64, PowerPC, MIPS और RISC-V asm आउटपुट दिखाता है। यह केवल तुलना को एक सीमा-जाँच में बदलकर है (unsigned)(a+b+999) <= 1998Uकि gcc इसे फिल के उत्तर की तरह अनुकूलित कर सकता है।
पीटर कॉर्ड्स

2
इस उत्तर में "बेहतर" कोड अभी भी गलत है, क्योंकि यह एक अलग उत्तर के लिए उत्पादन करता है IsSumInRange(INT_MIN, 0)। मूल कोड लौटता है falseक्योंकि INT_MIN+0 > 1000 || INT_MIN+0 < -1000; लेकिन "नए और बेहतर" कोड लौटाता है trueक्योंकि abs(INT_MIN+0) < 1000। (या, कुछ भाषाओं में, यह एक अपवाद फेंक देगा या अपरिभाषित व्यवहार कर सकता है। अपनी स्थानीय लिस्टिंग की जाँच करें।)
Quuxplusone

16

विधि ए बनाम विधि बी, और इसके विपरीत का उपयोग करना कब उचित है?

हार्डवेयर सस्ता है; प्रोग्रामर महंगे हैं । तो इस सवाल पर आप दो समय बर्बाद करने की लागत शायद या तो जवाब से भी बदतर है।

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

दूसरी ओर, यदि यह विशुद्ध रूप से एक अकादमिक अभ्यास है, तो आप विधि C के साथ दोनों दुनियाओं में सर्वश्रेष्ठ हो सकते हैं:

private bool IsSumInRange(int a, int b)
{
    a += b;
    return (a >= -1000 && a <= 1000);
}

17
a+=bएक साफ-सुथरी चाल है, लेकिन मुझे अपने अनुभव के तरीकों से (सिर्फ मामले में इसे बाकी के जवाब से निहित नहीं किया गया है) उल्लेख करना है, मापदंडों के साथ खिलवाड़ करना बहुत मुश्किल हो सकता है और बनाए रख सकता है।
jrh

1
मैं @jrh से सहमत हूं। मैं आरओसी के लिए एक मजबूत वकील हूं, और इस तरह की चीज कुछ भी है लेकिन
जॉन वू

3
"हार्डवेयर सस्ता है, प्रोग्रामर महंगे हैं।" उपभोक्ता इलेक्ट्रॉनिक्स की दुनिया में, यह कथन गलत है। यदि आप लाखों इकाइयां बेचते हैं, तो प्रति यूनिट हार्डवेयर लागत पर $ 0,10 को बचाने के लिए अतिरिक्त विकास लागत में $ 500.000 खर्च करना बहुत अच्छा निवेश है।
बार्ट वैन इनगेन शानौ

2
@ जॉनोवु: आपने ifचेक को सरल बनाया , लेकिन तुलना के परिणाम को उल्टा करना भूल गए; अपने कार्य अब लौटा रहा है trueजब a + bहै नहीं रेंज में। या तो !स्थिति के बाहर ( return !(a > 1000 || a < -1000)) में जोड़ें, या !inverting परीक्षणों को वितरित करें , पाने के लिए return a <= 1000 && a >= -1000;या सीमा की जांच अच्छी तरह से करने के लिए,return -1000 <= a && a <= 1000;
ShadowRanger

1
@ जॉनोवु: फिर भी किनारे के मामलों में थोड़ा दूर, वितरित तर्क की आवश्यकता होती है <=/ >=, नहीं </ >( </ >, 1000 और -1000 को सीमा से बाहर होने के रूप में माना जाता है, मूल कोड ने उन्हें सीमा के रूप में माना है)।
19 अप्रैल को शैडो रेंजर

11

मैं पठनीयता के लिए अनुकूलन करूँगा। विधि X:

private bool IsSumInRange(int number1, int number2)
{
    return IsValueInRange(number1+number2, -1000, 1000);
}

private bool IsValueInRange(int Value, int Lowerbound, int Upperbound)
{
    return  (Value >= Lowerbound && Value <= Upperbound);
}

छोटे तरीके जो सिर्फ 1 काम करते हैं लेकिन इसके बारे में तर्क करना आसान है।

(यह व्यक्तिगत पसंद है, मुझे नकारात्मक के बजाय सकारात्मक परीक्षण पसंद है, आपका मूल कोड वास्तव में परीक्षण कर रहा है कि क्या मूल्य सीमा के बाहर नहीं है।)


5
इस। (ऊपर टिप्पणी की गई है कि फिर से समान थे: पठनीयता)। 30 साल पहले, जब हम उन मशीनों के साथ काम कर रहे थे जिनकी रैम 1mb से कम थी, तो परफॉरमेंस को निचोड़ना आवश्यक था - ठीक y2k समस्या की तरह, कुछ सौ हजार रिकॉर्ड प्राप्त करें कि प्रत्येक के पास कुछ बाइट्स हैं जो अप्रयुक्त संस्करण के कारण बर्बाद हो रहे हैं संदर्भ, आदि और यह जल्दी से जोड़ता है जब आपके पास केवल 256k RAM है। अब जब हम उन मशीनों के साथ काम कर रहे हैं, जिनमें कई गीगाबाइट रैम हैं, तो कुछ एमबी रैम का उपयोग करके बनाम पठनीयता और कोड की स्थिरता बनाए रखना एक अच्छा व्यापार नहीं है।
ivanivan

@ivanivan: मुझे नहीं लगता कि "y2k समस्या" वास्तव में स्मृति के बारे में थी। डेटा-एंट्री दृष्टिकोण से, चार में प्रवेश करने की तुलना में दो अंकों में प्रवेश करना अधिक कुशल है, और चीजों को दर्ज करना उन्हें किसी अन्य रूप में परिवर्तित करने की तुलना में आसान है।
सुपरकाट

10
अब आपको 2 कार्यों के माध्यम से पता लगाना है कि क्या हो रहा है। आप इसे अंकित मूल्य पर नहीं ले सकते, क्योंकि आप इस नाम से नहीं बता सकते कि ये समावेशी हैं या अनन्य सीमाएं हैं। और यदि आप उस जानकारी को जोड़ते हैं, तो फ़ंक्शन का नाम इसे व्यक्त करने के लिए कोड से अधिक लंबा है।
पीटर

1
पठनीयता का अनुकूलन करें और छोटे, आसान करने के लिए कार्य करें - सुनिश्चित करें, सहमत हैं। लेकिन मैं दृढ़ता से कि नाम से असहमत aऔर bकरने के लिए number1और number2किसी भी तरह से एड्स पठनीयता। इसके अलावा आपके कार्यों का नामकरण असंगत है: IsSumInRangeयदि IsValueInRangeतर्क के रूप में इसे स्वीकार किया जाता है तो हार्ड-कोड क्यों नहीं है?
19

1 फ़ंक्शन अतिप्रवाह कर सकता है। (अन्य उत्तरों के कोड की तरह।) हालांकि अतिप्रवाह-सुरक्षित कोड की जटिलता एक फ़ंक्शन में डालने के लिए एक तर्क है।
दार्शनिक

6

संक्षेप में, मुझे नहीं लगता कि प्रश्न की वर्तमान कंप्यूटिंग में बहुत अधिक प्रासंगिकता है, लेकिन एक ऐतिहासिक दृष्टिकोण से यह एक दिलचस्प विचार है।

आपका साक्षात्कारकर्ता संभावित रूप से पौराणिक पुरुष महीने का प्रशंसक है। पुस्तक में, फ्रेड ब्रूक्स यह मामला बनाता है कि प्रोग्रामर को आम तौर पर अपने टूलबॉक्स में मुख्य कार्यों के दो संस्करणों की आवश्यकता होगी: एक मेमोरी-अनुकूलित संस्करण और एक सीपीयू-अनुकूलित संस्करण। फ्रेड ने अपने अनुभव के आधार पर आईबीएम सिस्टम / 360 ऑपरेटिंग सिस्टम के विकास का नेतृत्व किया, जहां मशीनों में 8 किलोबाइट रैम हो सकती है। ऐसी मशीनों में, कार्यों में स्थानीय चर के लिए आवश्यक मेमोरी संभवतः महत्वपूर्ण हो सकती है, खासकर यदि कंपाइलर ने उन्हें प्रभावी रूप से दूर नहीं किया (या यदि कोड सीधे विधानसभा भाषा में लिखा गया था)।

वर्तमान युग में, मुझे लगता है कि आप एक ऐसी प्रणाली को खोजने के लिए सख्त हो जाएंगे, जहां एक विधि में स्थानीय चर की उपस्थिति या अनुपस्थिति ध्यान देने योग्य अंतर होगी। परिवर्तनशील पदार्थ के लिए, विधि को पुनरावर्ती के साथ अपेक्षित पुनरावृत्ति की आवश्यकता होगी। फिर भी, यह संभावना है कि स्टैक की गहराई अधिक हो जाएगी, जिससे स्टैक ओवरफ्लो अपवादों से पहले चर स्वयं एक समस्या का कारण बने। एकमात्र वास्तविक परिदृश्य जहां यह एक समस्या हो सकती है, एक पुनरावर्ती विधि में ढेर पर आवंटित सरणियों का बहुत बड़ा है। लेकिन यह भी संभावना नहीं है क्योंकि मुझे लगता है कि अधिकांश डेवलपर्स बड़े सरणियों की अनावश्यक प्रतियों के बारे में दो बार सोचेंगे।


4

असाइनमेंट के बाद = a + b; चर और बी का उपयोग नहीं किया जाता है। इसलिए, यदि आप पूरी तरह से मस्तिष्क-क्षतिग्रस्त संकलक का उपयोग नहीं कर रहे हैं, तो एस के लिए किसी भी मेमोरी का उपयोग नहीं किया जाता है; स्मृति जो किसी भी तरह से a और b के लिए उपयोग की गई थी, उसका पुनः उपयोग किया जाता है।

लेकिन इस समारोह का अनुकूलन पूरी तरह से बकवास है। यदि आप अंतरिक्ष को बचा सकते हैं, तो यह हो सकता है कि 8 बाइट्स हों जब फ़ंक्शन चल रहा हो (जो कि फ़ंक्शन वापस आने पर पुनर्प्राप्त किया जाता है), तो बिल्कुल व्यर्थ। यदि आप समय बचा सकते हैं, तो यह एकल संख्या में नैनोसेकंड होगा। इसका अनुकूलन करना समय की कुल बर्बादी है।


3

स्थानीय मान प्रकार चर को स्टैक पर आवंटित किया जाता है या (कोड के ऐसे छोटे टुकड़ों के लिए अधिक संभावना) प्रोसेसर में रजिस्टरों का उपयोग करते हैं और कभी भी किसी भी रैम को देखने के लिए नहीं मिलते हैं। किसी भी तरह से वे कम रहते हैं और चिंता की कोई बात नहीं है। आप मेमोरी उपयोग पर विचार करना शुरू करते हैं जब आपको संग्रह में डेटा तत्वों को बफर या कतार करने की आवश्यकता होती है जो संभावित रूप से बड़े और लंबे समय तक रहते हैं।

फिर यह निर्भर करता है कि आप अपने आवेदन के लिए सबसे ज्यादा क्या परवाह करते हैं। संसाधन गति? जवाब देने का समय? मेमोरी पदचिह्न? रख-रखाव की? डिजाइन में संगति? सब तुम्हारे ऊपर है।


4
Nitpicking: .NET कम से कम (पोस्ट की भाषा अनिर्दिष्ट है) "स्टैक पर" आवंटित किए जाने वाले स्थानीय चर के बारे में कोई गारंटी नहीं देता है। एरिक लिपर्ट द्वारा "स्टैक इम्प्लीमेंटेशन डिटेल" देखें ।
जूनियर

1
@jrh स्टैक या हीप पर स्थानीय चर एक कार्यान्वयन विवरण हो सकता है, लेकिन अगर कोई वास्तव में स्टैक पर एक चर चाहता था stackallocऔर अब Span<T>। संभवतः एक गर्म स्थान में उपयोगी है, प्रोफाइलिंग के बाद। इसके अलावा, संरचना के आसपास के कुछ डॉक्स का मतलब है कि मूल्य प्रकार स्टैक पर हो सकते हैं जबकि संदर्भ प्रकार नहीं होंगे। वैसे भी, सबसे अच्छा आप जीसी से थोड़ा बच सकते हैं।
बॉब

2

जैसा कि अन्य उत्तरों ने कहा है, आपको यह सोचने की ज़रूरत है कि आप किस चीज के लिए अनुकूलन कर रहे हैं।

इस उदाहरण में, मुझे संदेह है कि कोई भी सभ्य संकलक दोनों विधियों के लिए समान कोड उत्पन्न करेगा, इसलिए निर्णय का रन टाइम या मेमोरी पर कोई प्रभाव नहीं पड़ेगा !

यह क्या प्रभावित करता है यह कोड की पठनीयता है। (कोड मनुष्यों को पढ़ने के लिए है, न केवल कंप्यूटरों के लिए।) दो उदाहरणों के बीच बहुत अंतर नहीं है; जब अन्य सभी चीजें समान होती हैं, तो मैं संक्षिप्तता को एक गुण मानता हूं, इसलिए मैं शायद बी पद्धति चुनूंगा। लेकिन अन्य सभी चीजें शायद ही कभी समान होती हैं, और अधिक जटिल वास्तविक दुनिया के मामले में, इसका बड़ा प्रभाव हो सकता है।

विचार करने के लिए बातें:

  • क्या मध्यवर्ती अभिव्यक्ति का कोई दुष्प्रभाव है? यदि यह किसी भी अशुद्ध कार्यों को कॉल करता है या किसी भी चर को अपडेट करता है, तो निश्चित रूप से इसे डुप्लिकेट करना केवल शैली का नहीं, बल्कि शुद्धता की बात होगी।
  • मध्यवर्ती अभिव्यक्ति कितनी जटिल है? यदि यह बहुत सारी गणना और / या कॉल फ़ंक्शन करता है, तो कंपाइलर इसे अनुकूलित करने में सक्षम नहीं हो सकता है, और इसलिए यह प्रदर्शन को प्रभावित करेगा। (हालांकि, जैसा कि नुथ ने कहा , "हमें छोटी क्षमताओं के बारे में भूलना चाहिए, 97% समय के बारे में कहना चाहिए")।
  • क्या मध्यवर्ती चर का कोई अर्थ है ? क्या इसे ऐसा नाम दिया जा सकता है जो यह समझाने में मदद करे कि क्या हो रहा है? एक संक्षिप्त लेकिन जानकारीपूर्ण नाम कोड को बेहतर तरीके से समझा सकता है, जबकि एक अर्थहीन दृश्य दृश्य शोर है।
  • मध्यवर्ती अभिव्यक्ति कब तक है? यदि लंबे समय तक, तो इसे डुप्लिकेट करने से कोड लंबे समय तक और पढ़ने में कठिन हो सकता है (खासकर अगर यह एक लाइन ब्रेक को मजबूर करता है); यदि नहीं, तो नकल बिल्कुल कम हो सकती है।

1

जैसा कि कई उत्तरों ने बताया है, इस समारोह को आधुनिक संकलक के साथ ट्यून करने का प्रयास करने से कोई फर्क नहीं पड़ेगा। एक ऑप्टिमाइज़र सबसे अच्छा समाधान का पता लगा सकता है (उत्तर के लिए वोट दें जो कोडर कोड को साबित करने के लिए दिखाया गया है!)। आपने कहा कि साक्षात्कार में कोड की तुलना करने के लिए कहा गया कोड बिल्कुल नहीं था, इसलिए शायद वास्तविक उदाहरण थोड़ा अधिक समझ में आता है।

लेकिन इस प्रश्न पर एक और नज़र डालते हैं: यह एक साक्षात्कार प्रश्न है। तो असल मुद्दा यह है कि आपको यह मानकर कैसे जवाब देना चाहिए कि आप नौकरी करना चाहते हैं?

चलिए यह भी मान लेते हैं कि साक्षात्कारकर्ता को पता है कि वे किस बारे में बात कर रहे हैं और वे केवल यह जानने की कोशिश कर रहे हैं कि आप क्या जानते हैं।

मैं यह उल्लेख करूंगा कि, ऑप्टिमाइज़र को अनदेखा करते हुए, पहला स्टैक पर एक अस्थायी चर बना सकता है जबकि दूसरा नहीं होगा, लेकिन गणना दो बार करेगा। इसलिए, पहले अधिक मेमोरी का उपयोग करता है लेकिन तेज है।

आप यह उल्लेख कर सकते हैं कि वैसे भी, गणना के लिए परिणाम को संग्रहीत करने के लिए एक अस्थायी चर की आवश्यकता हो सकती है (ताकि इसकी तुलना की जा सके), ताकि आप उस चर का नाम दें या नहीं, इससे कोई अंतर नहीं पड़ सकता है।

मैं तब उल्लेख करूंगा कि वास्तव में कोड को अनुकूलित किया जाएगा और सभी चर स्थानीय होने के बाद सबसे अधिक संभावना समतुल्य मशीन कोड उत्पन्न होगा। हालाँकि, यह इस बात पर निर्भर करता है कि आप किस संकलक का उपयोग कर रहे हैं (यह बहुत पहले नहीं था कि मैं जावा में स्थानीय चर को "अंतिम" घोषित करके एक उपयोगी प्रदर्शन सुधार प्राप्त कर सकता था)।

आप उल्लेख कर सकते हैं कि किसी भी मामले में स्टैक अपने स्वयं के मेमोरी पेज में रहता है, इसलिए जब तक कि आपके अतिरिक्त चर ने स्टैक को पृष्ठ को ओवरफ्लो करने का कारण नहीं बनाया, तब तक यह वास्तव में किसी भी अधिक मेमोरी को आवंटित नहीं करेगा। यदि यह अतिप्रवाह करता है तो यह एक नया पृष्ठ चाहेगा।

मैं यह उल्लेख करूंगा कि एक अधिक यथार्थवादी उदाहरण इस बात का विकल्प हो सकता है कि कैश का उपयोग कई संगणनाओं के परिणामों को रखने के लिए किया जाए या नहीं और यह सीपीयू बनाम मेमोरी का प्रश्न खड़ा करेगा।

यह सब दर्शाता है कि आप जानते हैं कि आप किस बारे में बात कर रहे हैं।

मैं यह कहने के लिए अंत तक छोड़ दूंगा कि इसके बजाय पठनीयता पर ध्यान देना बेहतर होगा। हालांकि इस मामले में सच है, साक्षात्कार के संदर्भ में यह व्याख्या की जा सकती है कि "मुझे प्रदर्शन के बारे में पता नहीं है लेकिन मेरा कोड एक जेनेट और जॉन कहानी की तरह पढ़ता है "।

आपको जो नहीं करना चाहिए, वह सामान्य ब्लैंड स्टेटमेंट्स के बारे में बताता है कि कोड ऑप्टिमाइज़ेशन कैसे आवश्यक नहीं है, तब तक ऑप्टिमाइज़ न करें जब तक कि आपने कोड को प्रोफाइल न किया हो (यह सिर्फ इंगित करता है कि आप अपने लिए बुरा कोड नहीं देख सकते हैं), प्रोग्रामर की तुलना में हार्डवेयर की लागत कम होती है , और कृपया, कृपया, "शीघ्रपतन ब्लाह ..." नथ को उद्धृत न करें।

कोड प्रदर्शन एक महान कई संगठनों में एक वास्तविक मुद्दा है और कई संगठनों को प्रोग्रामर की आवश्यकता होती है जो इसे समझते हैं।

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

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

मैं व्यक्तिगत रूप से कई दशकों से काम कर रहा हूं, कई परियोजनाओं पर काम किया है जहां सिस्टम प्रदर्शन के मुद्दों के कारण विफल या अनुपयोगी हो गए हैं और मुझे उन प्रणालियों के अनुकूलन में बुलाया गया है और सभी मामलों में यह प्रोग्रामर द्वारा लिखे गए खराब कोड के कारण हुआ है जो समझ में नहीं आया था वे जो लिख रहे थे उसका प्रभाव। फर्थमोर, यह कोड का एक टुकड़ा नहीं है, यह हमेशा हर जगह होता है। जब मैं मुड़ता हूं, तो प्रदर्शन के बारे में सोचना शुरू करने के लिए देर हो जाती है: नुकसान हो चुका है।

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

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


0

आपको पहले शुद्धता के लिए अनुकूलन करना चाहिए।

आपका फ़ंक्शन उन इनपुट मानों के लिए विफल रहता है जो Int.MaxValue के करीब हैं:

int a = int.MaxValue - 200;
int b = int.MaxValue - 200;
bool inRange = test.IsSumInRangeA(a, b);

यह सच है क्योंकि यह राशि -400 से अधिक हो जाती है। फ़ंक्शन भी = int.inValue + 200 के लिए काम नहीं करता है। (गलत तरीके से "400" तक जुड़ जाता है)

हम यह नहीं जान पाएंगे कि साक्षात्कारकर्ता तब तक क्या देख रहा था जब तक कि वह झंकार नहीं करता, लेकिन "अतिप्रवाह वास्तविक है"

साक्षात्कार की स्थिति में, समस्या के दायरे को स्पष्ट करने के लिए प्रश्न पूछें: अनुमत अधिकतम और न्यूनतम इनपुट मान क्या हैं? एक बार जब आपके पास वे होते हैं, तो आप एक अपवाद फेंक सकते हैं यदि कॉलर रेंज के बाहर मूल्यों को प्रस्तुत करता है। या (सी # में), आप एक चेक किए गए {} अनुभाग का उपयोग कर सकते हैं, जो अतिप्रवाह पर एक अपवाद फेंक देगा। हां, यह अधिक काम और जटिल है, लेकिन कभी-कभी ऐसा होता है।


विधियाँ केवल उदाहरण थीं। वे सही नहीं लिखे गए थे, लेकिन वास्तविक प्रश्न को स्पष्ट करने के लिए। वैसे, इनपुट के लिए धन्यवाद!
बेहतर बजट

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

1
@Corey 1 के प्रश्नों के रूप में अच्छे साक्षात्कारकर्ता) इस मुद्दे से संबंधित उम्मीदवार की क्षमता का आकलन करते हैं, जैसा कि यहाँ rghome द्वारा सुझाया गया है, अभी भी 2) बड़े मुद्दों (जैसे अनपेक्षित कार्यात्मक शुद्धता) और संबंधित ज्ञान की गहराई में एक उद्घाटन के रूप में - यह अधिक है बाद के कैरियर के साक्षात्कार में - सौभाग्य।
chux

0

आपका प्रश्न यह होना चाहिए था: "क्या मुझे इसे अनुकूलित करने की आवश्यकता है?"।

संस्करण ए और बी एक महत्वपूर्ण विवरण में भिन्न होते हैं जो ए अधिमान्य बनाता है, लेकिन यह अनुकूलन के लिए असंबंधित है: आप कोड को दोहराते नहीं हैं।

वास्तविक "ऑप्टिमाइज़ेशन" को सामान्य सबप्रेसेशन एलिमिनेशन कहा जाता है, जो कि हर कंपाइलर करता है। कुछ यह बुनियादी अनुकूलन तब भी करते हैं जब अनुकूलन बंद हो जाते हैं। तो यह वास्तव में एक अनुकूलन नहीं है (उत्पन्न कोड लगभग निश्चित रूप से हर मामले में बिल्कुल समान होगा)।

लेकिन अगर यह एक अनुकूलन नहीं है, तो यह बेहतर क्यों है? ठीक है, आप कोड नहीं दोहराते हैं, कौन परवाह करता है!

वैसे सबसे पहले, आपको गलती से सशर्त खंड का आधा गलत होने का जोखिम नहीं है। लेकिन इससे भी महत्वपूर्ण बात यह है कि इस कोड को पढ़ने वाला कोई व्यक्ति अनुभव के बजाय तुरंत वही कर सकता है जो आप करने की कोशिश कर रहे हैं if((((wtf||is||this||longexpression))))। पाठक को if(one || theother)जो देखने को मिलता है , वह अच्छी बात है। शायद ही कभी, मैं ऐसा होता हूं कि आप अन्य व्यक्ति तीन साल बाद अपना कोड पढ़ रहे हैं और सोच रहे हैं कि "डब्ल्यूटीएफ का यह मतलब है?"। उस स्थिति में यह हमेशा मददगार होता है यदि आपका कोड तुरंत यह बताता है कि इरादा क्या था। एक सामान्य सबडिप्रेशन को ठीक से नाम दिए जाने के साथ, यह मामला है।
इसके अलावा, अगर भविष्य में किसी भी समय, आप तय करते हैं कि बदलने के लिए उदाहरण के लिए आप की जरूरत a+bकरने के लिए a-b, आप बदलना होगा एकस्थान, दो नहीं। और दुर्घटना से दूसरा (गलत) होने का कोई खतरा नहीं है।

अपने वास्तविक प्रश्न के बारे में, आपको जो कुछ भी अनुकूलन करना चाहिए, उसके लिए सबसे पहले आपका कोड सही होना चाहिए । यह सबसे महत्वपूर्ण बात है। जो कोड सही नहीं है वह खराब कोड है, यहां तक ​​कि अगर गलत होने के बावजूद यह "ठीक काम करता है", या कम से कम ऐसा लगता है कि यह ठीक काम करता है। उसके बाद, कोड पठनीय होना चाहिए (किसी अपरिचित द्वारा पठनीय)।
जैसा कि अनुकूलन करने के लिए ... एक निश्चित रूप से जानबूझकर विरोधी-अनुकूलित कोड नहीं लिखना चाहिए, और निश्चित रूप से मैं यह नहीं कह रहा हूं कि आपको शुरू करने से पहले डिजाइन पर एक विचार खर्च नहीं करना चाहिए (जैसे कि समस्या के लिए सही एल्गोरिदम चुनना। कम से कम कुशल नहीं है)।

लेकिन अधिकांश अनुप्रयोगों के लिए, अधिकांश समय, प्रदर्शन जो आपको सही चलने के बाद मिलता है, एक अनुकूलन कंपाइलर के माध्यम से एक उचित एल्गोरिथ्म का उपयोग करके पठनीय कोड बस ठीक है, चिंता करने की कोई वास्तविक आवश्यकता नहीं है।

यदि ऐसा नहीं है, तो यदि आवेदन का प्रदर्शन वास्तव में आवश्यकताओं को पूरा नहीं करता है, और केवल तब , आपको इस तरह के स्थानीय अनुकूलन करने की चिंता करनी चाहिए, जैसा आपने प्रयास किया था। हालाँकि, आप शीर्ष स्तर के एल्गोरिथ्म पर पुनर्विचार करेंगे। यदि आप एक बेहतर एल्गोरिथम के कारण 50,000 बार के बजाय 500 बार किसी फ़ंक्शन को कॉल करते हैं, तो माइक्रो-ऑप्टिमाइज़ेशन पर तीन घड़ी चक्रों को बचाने की तुलना में इसका बड़ा प्रभाव पड़ता है। यदि आप हर समय एक यादृच्छिक मेमोरी एक्सेस पर कई सौ चक्रों के लिए स्टाल नहीं करते हैं , तो यह कुछ सस्ते गणनाओं को अतिरिक्त करने आदि की तुलना में बड़ा प्रभाव डालता है।

अनुकूलन एक कठिन मामला है (आप इसके बारे में पूरी किताबें लिख सकते हैं और इसका कोई अंत नहीं हो सकता है), और कुछ विशेष स्थान पर आँख बंद करके समय व्यतीत करना (बिना यह जाने भी कि क्या यह अड़चन है!) आमतौर पर समय बर्बाद होता है। प्रोफाइलिंग के बिना, अनुकूलन सही होना बहुत कठिन है।

लेकिन अंगूठे के एक नियम के रूप में, जब आप अंधे उड़ान भर रहे होते हैं और बस कुछ करना चाहते हैं , या एक सामान्य डिफ़ॉल्ट रणनीति के रूप में चाहते हैं, तो मैं "मेमोरी" के लिए अनुकूलन करने का सुझाव दूंगा।
"मेमोरी" (विशेष रूप से स्थानिक इलाके और एक्सेस पैटर्न में) के लिए अनुकूलन करने से आमतौर पर लाभ मिलता है क्योंकि एक बार के विपरीत जब सब कुछ "थोथा समान" होता था, आजकल रैम तक पहुंच सबसे महंगी चीजों (डिस्क से पढ़ने की कमी) में से है! आप सिद्धांत रूप में कर सकते हैं। दूसरी ओर, ALU सस्ता है और हर हफ्ते तेज हो रहा है। मेमोरी बैंडविड्थ और लेटेंसी तेजी से लगभग नहीं सुधरती है। डेटा-हेवी एप्लिकेशन में खराब एक्सेस पैटर्न की तुलना में अच्छे इलाके और अच्छे एक्सेस पैटर्न आसानी से रनटाइम में 5x अंतर (चरम में, 20x उदाहरणों में) बना सकते हैं। अपने कैश के लिए अच्छा हो, और आप एक खुश व्यक्ति होंगे।

पिछले पैराग्राफ को परिप्रेक्ष्य में रखने के लिए, उन विभिन्न चीजों पर विचार करें जो आप कर सकते हैं। a+bएक या दो चक्रों ( जैसे अगर अनुकूलित नहीं किया गया है) को निष्पादित करना , लेकिन सीपीयू आमतौर पर प्रति चक्र कई निर्देश शुरू कर सकता है, और गैर-निर्भर निर्देशों को पाइपलाइन कर सकता है इसलिए अधिक वास्तविक रूप से यह केवल आपको लगभग आधा चक्र या उससे कम लागत देता है। आदर्श रूप से, यदि संकलक शेड्यूलिंग में अच्छा है, और स्थिति के आधार पर, इसकी लागत शून्य हो सकती है।
यदि आप भाग्यशाली हैं और यह L1 में है, और यदि आप इतने भाग्यशाली नहीं हैं (L2 हिट), तो आपको ("मेमोरी") डेटा प्राप्त करने में 4-5 चक्रों का खर्च आता है। यदि डेटा बिल्कुल कैश में नहीं है, तो यह कई सौ चक्र लेता है। यदि आपका बेतरतीब एक्सेस पैटर्न TLB की क्षमताओं से अधिक है (केवल ~ 50 प्रविष्टियों के साथ करना आसान है), तो कुछ और सौ चक्र जोड़ें। यदि आपका बेतरतीब एक्सेस पैटर्न वास्तव में एक पेज फॉल्ट का कारण बनता है, तो इससे आपको सबसे अच्छे मामले में कुछ दस हजार चक्रों और सबसे खराब में कई मिलियन खर्च होते हैं।
अब इसके बारे में सोचें, वह कौन सी चीज है जिससे आप सबसे ज्यादा बचना चाहते हैं?


0

एक विधि के लिए मेमोरी बनाम प्रदर्शन की गति के लिए कब अनुकूलित करें?

प्राप्त करने के बाद कार्यक्षमता सही पहले । फिर माइक्रो ऑप्टिमाइज़ेशन के साथ चयनात्मकता चिंता का विषय है।


अनुकूलन के संबंध में एक साक्षात्कार प्रश्न के रूप में, कोड सामान्य चर्चा को उकसाता है फिर भी क्या कोड के उच्च स्तर का लक्ष्य कार्यात्मक रूप से सही है?

C ++ और C और अन्य, दोनों intसे समस्या के रूप में अतिप्रवाह मानते हैं a + b। यह अच्छी तरह से परिभाषित नहीं है और C इसे अपरिभाषित व्यवहार कहता है । यह "रैप" करने के लिए निर्दिष्ट नहीं है - भले ही यह सामान्य व्यवहार है।

bool IsSumInRange(int a, int b) {
    int s = a + b;  // Overflow possible
    if (s > 1000 || s < -1000) return false;
    else return true;
}

इस तरह के एक समारोह IsSumInRange()को अच्छी तरह से परिभाषित करने और सभी intमूल्यों के लिए सही प्रदर्शन करने की उम्मीद की जाएगी a,b। कच्चा a + bनहीं है। एसी समाधान का उपयोग कर सकते हैं:

#define N 1000
bool IsSumInRange_FullRange(int a, int b) {
  if (a >= 0) {
    if (b > INT_MAX - a) return false;
  } else {
    if (b < INT_MIN - a) return false;
  }
  int sum = a + b;
  if (sum > N || sum < -N) return false;
  else return true;
}

ऊपर दिए गए कोड की तुलना में एक व्यापक पूर्णांक प्रकार का उपयोग करके अनुकूलित किया जा सकता है intनीचे के रूप में, यदि उपलब्ध हो तो या वितरण sum > N, sum < -Nभीतर परीक्षण if (a >= 0)तर्क। फिर भी इस तरह के अनुकूलन एक स्मार्ट कंपाइलर दिए गए "तेज" उत्सर्जित कोड को सही मायने में नहीं ले सकते हैं और न ही चतुर होने के अतिरिक्त रखरखाव के लायक हैं।

  long long sum a;
  sum += b;

यहां तक ​​कि का उपयोग करते abs(sum)समय समस्याओं का खतरा है sum == INT_MIN


0

हम किस प्रकार के संकलक के बारे में बात कर रहे हैं, और किस प्रकार की "मेमोरी" है? क्योंकि आपके उदाहरण में, एक उचित आशावादी मानते हुए, अभिव्यक्ति a+bको आम तौर पर इस तरह के अंकगणित करने से पहले एक रजिस्टर (स्मृति का एक रूप) में संग्रहीत किया जाना चाहिए।

इसलिए यदि हम एक ऐसे डंबल कंपाइलर के बारे में बात कर रहे हैं जो a+bदो बार सामना करता है , तो यह आपके दूसरे उदाहरण में अधिक रजिस्टरों (मेमोरी) को आवंटित करने जा रहा है , क्योंकि आपका पहला उदाहरण सिर्फ एक ही रजिस्टर को एक बार स्थानीय वेरिएबल में मैप करने पर उस अभिव्यक्ति को स्टोर कर सकता है, लेकिन हम इस बिंदु पर बहुत मूर्खतापूर्ण संकलक के बारे में बात कर रहे हैं ... जब तक कि आप एक अन्य प्रकार के मूर्ख संकलक के साथ काम नहीं कर रहे हैं जो स्टैक हर जगह हर एक चर को फैलाता है, इस स्थिति में हो सकता है कि पहले वाला इसे तुलना में अनुकूलन करने के लिए अधिक दु: ख का कारण बने द्वितीय*।

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

यह मूर्खतापूर्ण तरीके से अनुपस्थित मापों / अव्यवस्थाओं और यहां तक ​​कि सबसे खराब स्थिति में भी सभी बेहद अटकलें हैं, यह "मेमोरी बनाम प्रदर्शन" मामला नहीं है (क्योंकि सबसे बुरे आशावादियों के बीच भी, जिनके बारे में हम सोच सकते हैं, हम बात नहीं कर रहे हैं। स्टैक / रजिस्टर की तरह कुछ भी लेकिन अस्थायी मेमोरी के बारे में), यह विशुद्ध रूप से एक "प्रदर्शन" मामला है, और किसी भी उचित अनुकूलक के बीच दोनों समान हैं, और यदि कोई उचित ऑप्टिमाइज़र का उपयोग नहीं कर रहा है, तो अनुकूलन के बारे में जुनूनी प्रकृति में सूक्ष्मता क्यों है विशेष रूप से अनुपस्थित माप? यह निर्देश चयन / रजिस्टर आवंटन असेंबली-स्तरीय फ़ोकस की तरह है, जिसका उपयोग करते समय मैं किसी को भी उत्पादक बने रहने की उम्मीद नहीं करता, कहते हैं, एक दुभाषिया जो स्टैक सब कुछ बिखेरता है।

एक विधि के लिए मेमोरी बनाम प्रदर्शन की गति के लिए कब अनुकूलित करें?

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

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.