2016-09-23 9 views
5

मैं समझने की कोशिश करता हूं कि सी ++ कंपाइलर लूप संलयन करने में सक्षम नहीं है और कब नहीं।सी ++ में लूप संलयन (कंपाइलर की मदद कैसे करें?)

निम्नलिखित कोड वेक्टर में सभी मानों के वर्ग डबल्स (f(x) = (2*x)^2) की गणना करने के दो अलग-अलग तरीकों के प्रदर्शन को मापता है।

#include <chrono> 
#include <iostream> 
#include <numeric> 
#include <vector> 

constexpr int square(int x) 
{ 
    return x * x; 
} 

constexpr int times_two(int x) 
{ 
    return 2 * x; 
} 

// map ((^2) . (^2)) $ [1,2,3] 
int manual_fusion(const std::vector<int>& xs) 
{ 
    std::vector<int> zs; 
    zs.reserve(xs.size()); 
    for (int x : xs) 
    { 
     zs.push_back(square(times_two(x))); 
    } 
    return zs[0]; 
} 

// map (^2) . map (^2) $ [1,2,3] 
int two_loops(const std::vector<int>& xs) 
{ 
    std::vector<int> ys; 
    ys.reserve(xs.size()); 
    for (int x : xs) 
    { 
     ys.push_back(times_two(x)); 
    } 

    std::vector<int> zs; 
    zs.reserve(ys.size()); 
    for (int y : ys) 
    { 
     zs.push_back(square(y)); 
    } 
    return zs[0]; 
} 

template <typename F> 
void test(F f) 
{ 
    const std::vector<int> xs(100000000, 42); 

    const auto start_time = std::chrono::high_resolution_clock::now(); 
    const auto result = f(xs); 
    const auto end_time = std::chrono::high_resolution_clock::now(); 

    const auto elapsed = end_time - start_time; 
    const auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); 
    std::cout << elapsed_us/1000 << " ms - " << result << std::endl; 
} 

int main() 
{ 
    test(manual_fusion); 
    test(two_loops); 
} 

दो छोरों takes about twice as much time एक पाश के साथ संस्करण के रूप में, जीसीसी और बजना के लिए -O3 भी साथ साथ संस्करण।

क्या कंप्रेसर को two_loops को दूसरे लूप में इन-प्लेस के बिना manual_fusion के रूप में तेज़ी से अनुकूलित करने की अनुमति देने का कोई तरीका है? कारण मैं पूछ रहा हूं कि मैं अपनी लाइब्रेरी FunctionalPlus पर fplus::enumerate(fplus::transform(f, xs)); की तरह जंजीर कॉल करना चाहता हूं।

+1

को push_back है आप दूसरे संस्करण में दो आवंटन किया है। चलो इसे चालू करें (संशोधित करें) 'ys' सीधे –

+0

आपको बहुत धन्यवाद, [यह काम करता है] (http://ideone.com/I17kdT)! क्या आपको लगता है कि संकलक आवंटन को अनुकूलित करने की संभावना है? –

+1

मुझे ऐसा नहीं लगता है। ऐसा करने के लिए बहुत अधिक अनुमान लगाया जाएगा (एक अनुमान पहले से ही अनुकूलन को मना करता है) –

उत्तर

1

आप इस प्रकार अपने two_loops समारोह को संशोधित करने की कोशिश कर सकते हैं:

int two_loops(const std::vector<int>& xs) 
{ 
    std::vector<int> zs; 
    zs.reserve(xs.size()); 
    for (int x : xs) 
    { 
     zs.push_back(times_two(x)); 
    } 

    for (int i=0 : i<zs.size(); i++) 
    { 
     zs[i] = (square(zs[i])); 
    } 
    return zs[0]; 
} 

बिंदु स्मृति में दो बार के आवंटन से बचने और एक अन्य वेक्टर

+0

धन्यवाद। यह काम। लेकिन मैं आवंटन को हटाए बिना संभावना की उम्मीद कर रहा था। कारण यह है कि, मैं अपनी लाइब्रेरी [FunctionalPlus] (https://github.com/Dobiasd/FunctionalPlus) जैसे 'fplus :: enumerate (fplus :: transform (f, xs)) की तुलना में जंजीर कॉल करना चाहता हूं;' तेज़। मैंने तदनुसार अपना प्रश्न बढ़ाया। –

+0

यदि आप जंजीर कॉल चाहते हैं तो आपको उस कीमत का भुगतान करना होगा जो लंबे समय तक निष्पादन के समय है। कम से कम आप आवंटन और push_backs से बच सकते हैं। पहला कॉल वेक्टर बना देगा और बाद की कॉल वेक्टर सामग्री को बदल देगा। – Trifon

+0

दुर्भाग्यवश सामग्री को बदलने से फ़ंक्शनलप्लस में उपयोग किए जाने वाले दृष्टिकोण में फिट नहीं होता है। –

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