2015-08-06 9 views
5

मैं बहु प्रोग्रामिंग में शुरू हो रही हूँ तो मुझे क्षमा करें कृपया निम्नलिखित स्पष्ट लगता है। मैं एक छवि प्रसंस्करण कार्यक्रम में मल्टीथ्रेडिंग जोड़ रहा हूं और स्पीडअप बिल्कुल मेरी अपेक्षा नहीं है।प्रदर्शन (4x speedup बनाम 8x)

मुझे वर्तमान में 4 भौतिक प्रोसेसर सीपीयू पर हाइपरथ्रेडिंग (8) के साथ 4x बार की गति मिल रही है, इसलिए मैं जानना चाहता हूं कि इस प्रकार की गति की उम्मीद है या नहीं। एकमात्र चीज जिसे मैं सोच सकता हूं वह यह समझ सकता है कि एक भौतिक CPU के दोनों हाइपरथ्रेड को किसी प्रकार की मेमोरी बस साझा करना है।

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

मैं एक परीक्षण कार्यक्रम

वर्तमान कार्यक्रम के रूप में आप देख सकते हैं कोई वास्तविक को काले से एक माना जाता छवि स्थापित करने के अलावा अन्य कार्य किया है parallel_for धारावाहिक मामले और दो समानांतर मामलों QThreadPool और TBB :: का उपयोग करते हुए प्रदर्शन लिखा है सफेद और यह जानने के उद्देश्य से किया गया है कि छवि पर किसी वास्तविक ऑपरेशन को लागू करने से पहले बेसलाइन क्या है।

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

#include <iostream> 
#include <sys/time.h> 

#include <vector> 
#include <QThreadPool> 
#include "/usr/local/include/tbb/tbb.h" 

#define LOG(x) (std::cout << x << std::endl) 

struct col4 
{ 
    unsigned char r, g, b, a; 
}; 

class QTileTask : public QRunnable 
{ 
public: 
    void run() 
    { 
     for(uint32_t y = m_yStart; y < m_yEnd; y++) 
     { 
      int rowStart = y * m_width; 
      for(uint32_t x = m_xStart; x < m_xEnd; x++) 
      { 
       int index = rowStart + x; 
       m_pData[index].r = 255; 
       m_pData[index].g = 255; 
       m_pData[index].b = 255; 
       m_pData[index].a = 255; 
      } 
     } 
    } 

    col4*   m_pData; 
    uint32_t  m_xStart; 
    uint32_t  m_yStart; 
    uint32_t  m_xEnd; 
    uint32_t  m_yEnd; 
    uint32_t  m_width; 
}; 

struct TBBTileTask 
{ 
    void operator()() 
    { 
     for(uint32_t y = m_yStart; y < m_yEnd; y++) 
     { 
      int rowStart = y * m_width; 
      for(uint32_t x = m_xStart; x < m_xEnd; x++) 
      { 
       int index = rowStart + x; 
       m_pData[index].r = 255; 
       m_pData[index].g = 255; 
       m_pData[index].b = 255; 
       m_pData[index].a = 255; 
      } 
     } 
    } 

    col4*   m_pData; 
    uint32_t  m_xStart; 
    uint32_t  m_yStart; 
    uint32_t  m_xEnd; 
    uint32_t  m_yEnd; 
    uint32_t  m_width; 
}; 

struct TBBCaller 
{ 
    TBBCaller(std::vector<TBBTileTask>& t) 
     : m_tasks(t) 
    {} 

    TBBCaller(TBBCaller& e, tbb::split) 
     : m_tasks(e.m_tasks) 
    {} 

    void operator()(const tbb::blocked_range<size_t>& r) const 
    { 
     for (size_t i=r.begin();i!=r.end();++i) 
      m_tasks[i](); 
    } 

    std::vector<TBBTileTask>& m_tasks; 
}; 

inline double getcurrenttime(void) 
{ 
    timeval t; 
    gettimeofday(&t, NULL); 
    return static_cast<double>(t.tv_sec)+(static_cast<double>(t.tv_usec)/1000000.0); 
} 

char* getCmdOption(char ** begin, char ** end, const std::string & option) 
{ 
    char ** itr = std::find(begin, end, option); 
    if (itr != end && ++itr != end) 
    { 
     return *itr; 
    } 
    return 0; 
} 

bool cmdOptionExists(char** begin, char** end, const std::string& option) 
{ 
    return std::find(begin, end, option) != end; 
} 

void baselineSerial(col4* pData, int resolution) 
{ 
    double t = getcurrenttime(); 
    for(int y = 0; y < resolution; y++) 
    { 
     int rowStart = y * resolution; 
     for(int x = 0; x < resolution; x++) 
     { 
      int index = rowStart + x; 
      pData[index].r = 255; 
      pData[index].g = 255; 
      pData[index].b = 255; 
      pData[index].a = 255; 
     } 
    } 
    LOG((getcurrenttime() - t) * 1000 << " ms. (Serial)"); 
} 

void baselineParallelQt(col4* pData, int resolution, uint32_t tileSize) 
{ 
    double t = getcurrenttime(); 

    QThreadPool pool; 
    for(int y = 0; y < resolution; y+=tileSize) 
    { 
     for(int x = 0; x < resolution; x+=tileSize) 
     { 
      uint32_t xEnd = std::min<uint32_t>(x+tileSize, resolution); 
      uint32_t yEnd = std::min<uint32_t>(y+tileSize, resolution); 

      QTileTask* t = new QTileTask; 
      t->m_pData = pData; 
      t->m_xStart = x; 
      t->m_yStart = y; 
      t->m_xEnd = xEnd; 
      t->m_yEnd = yEnd; 
      t->m_width = resolution; 
      pool.start(t); 
     } 
    } 
    pool.waitForDone(); 
    LOG((getcurrenttime() - t) * 1000 << " ms. (QThreadPool)"); 
} 

void baselineParallelTBB(col4* pData, int resolution, uint32_t tileSize) 
{ 
    double t = getcurrenttime(); 

    std::vector<TBBTileTask> tasks; 
    for(int y = 0; y < resolution; y+=tileSize) 
    { 
     for(int x = 0; x < resolution; x+=tileSize) 
     { 
      uint32_t xEnd = std::min<uint32_t>(x+tileSize, resolution); 
      uint32_t yEnd = std::min<uint32_t>(y+tileSize, resolution); 

      TBBTileTask t; 
      t.m_pData = pData; 
      t.m_xStart = x; 
      t.m_yStart = y; 
      t.m_xEnd = xEnd; 
      t.m_yEnd = yEnd; 
      t.m_width = resolution; 
      tasks.push_back(t); 
     } 
    } 

    TBBCaller caller(tasks); 
    tbb::task_scheduler_init init; 
    tbb::parallel_for(tbb::blocked_range<size_t>(0, tasks.size()), caller); 

    LOG((getcurrenttime() - t) * 1000 << " ms. (TBB)"); 
} 

int main(int argc, char** argv) 
{ 
    int resolution = 1; 
    uint32_t tileSize = 64; 

    char * pResText = getCmdOption(argv, argv + argc, "-r"); 
    if (pResText) 
    { 
     resolution = atoi(pResText); 
    } 

    char * pTileSizeChr = getCmdOption(argv, argv + argc, "-b"); 
    if (pTileSizeChr) 
    { 
     tileSize = atoi(pTileSizeChr); 
    } 

    if(resolution > 16) 
     resolution = 16; 

    resolution = resolution << 10; 

    uint32_t tileCount = resolution/tileSize + 1; 
    tileCount *= tileCount; 

    LOG("Resolution: " << resolution << " Tile Size: "<< tileSize); 
    LOG("Tile Count: " << tileCount); 

    uint64_t pixelCount = resolution*resolution; 
    col4* pData = new col4[pixelCount]; 

    memset(pData, 0, sizeof(col4)*pixelCount); 
    baselineSerial(pData, resolution); 

    memset(pData, 0, sizeof(col4)*pixelCount); 
    baselineParallelQt(pData, resolution, tileSize); 

    memset(pData, 0, sizeof(col4)*pixelCount); 
    baselineParallelTBB(pData, resolution, tileSize); 

    delete[] pData; 

    return 0; 
} 
+3

Hyperthreading केवल मदद करता है जब अपने कोड शाखा mispredicts के बहुत सारे है और/या स्मृति स्टालों पर इंतजार कर कोड के बहुत सारे है। (यह थ्रेड बी को उसी कोर पर निष्पादित करने की अनुमति देता है जबकि थ्रेड ए डेटा की प्रतीक्षा कर रहा है या निष्पादन इकाइयों के हिस्सों का उपयोग नहीं कर रहा है) आपका कोड ऐसा लगता है कि यह बिटब्लॉस्टिंग है जो जल्दी ही मेमोरी बैंडविड्थ सीमित हो जाएगा। –

+1

क्वाड-कोर प्रोसेसर पर 4x स्पीडअप एचटी की परवाह किए बिना पहले ही प्रभावशाली है। मैं इस बात की गिनती नहीं कर सकता कि लोगों को कोई गति या नकारात्मक गति प्राप्त करने के बारे में कितने SO प्रश्न हैं। – Mysticial

+0

हे धन्यवाद दोस्तों, मैं पूरी तरह से समझ में नहीं आया @BillyONeal कैसे हाइपरथ्रेडिंग काम करता है जब तक मैं आपके उत्तर देखा था, लगता है जैसे मैं पकड़ने और हिंदुस्तान टाइम्स वास्तुकला और यह कैसे का सबसे अच्छा पाने के लिए के बारे में कुछ किताबें पढ़ने के लिए होगा! ;) –

उत्तर

6

हां, 4x गति की उम्मीद है। हाइपरट्रेडिंग हार्डवेयर में लागू समय-समय पर साझा करने का एक प्रकार है, इसलिए यदि आप एक थ्रेड कोर पर उपलब्ध सभी सुपरस्कार्कर पाइपलाइनों का उपयोग कर रहे हैं, तो आप इससे लाभ प्राप्त करने की उम्मीद नहीं कर सकते हैं, क्योंकि यह आपका मामला है। अन्य धागे को जरूरी इंतजार करना होगा।

आप को उससे भी नीचे speedup उम्मीद कर सकते हैं, तो अपनी स्मृति बस बैंडविड्थ उपलब्ध कोर की कुल संख्या से भी कम समय में चल धागे से संतृप्त है। आम तौर पर होता है अगर आप बहुत अधिक कोर है, इस सवाल में की तरह:

Why doesn't this code scale linearly?

+0

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

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