2013-04-08 6 views
6

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

विभिन्न हिस्से:

// version 1 
count_words(&file1); 
pthread_create(&new_thread, NULL, count_words, &file2); 
pthread_join(new_thread, NULL); 

// version 2 
pthread_create(&new_thread, NULL, count_words, &file2); 
count_words(&file1); 
pthread_join(new_thread, NULL); 

// version 3 
pthread_create(&new_thread, NULL, count_words, &file2); 
pthread_join(new_thread, NULL); 
count_words(&file1); 

// version 4 
count_words(&file1); 
count_words(&file2); 

आम हिस्सा: (एक पूर्ण संस्करण बनाने के लिए इस आम भाग में विभिन्न हिस्से डालें)

#include <stdio.h> 
#include <pthread.h> 
#include <ctype.h> 
#include <stdlib.h> 
#include <time.h> 

#define N 2000 

typedef struct file_t { 
    char *name; 
    int words; 
} file_t; 

double time_diff(struct timespec *, struct timespec *); 
void *count_words(void *); 

// Usage: progname file1 file2 
int main(int argc, char *argv[]) { 
    pthread_t new_thread; 
    file_t file1, file2; 

    file1.name = argv[1]; 
    file1.words = 0; 
    file2.name= argv[2]; 
    file2.words = 0; 

    // Insert different part here 

    printf("Total words: %d\n", file1.words+file2.words); 
    return 0; 
} 

void *count_words(void *arg) { 
    FILE *fp; 
    file_t *file = (file_t *)arg; 
    int i, c, prevc = '\0'; 
    struct timespec process_beg, process_end; 
    struct timespec thread_beg, thread_end; 
    double process_diff, thread_diff; 

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_beg); 
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thread_beg); 

    fp = fopen(file->name, "r"); 
    for (i = 0; i < N; i++) { 
     while ((c = getc(fp)) != EOF) { 
      if (!isalnum(c) && isalnum(prevc)) 
       file->words++; 
      prevc = c; 
     } 
     fseek(fp, 0, SEEK_SET); 
    } 
    fclose(fp); 

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &process_end); 
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &thread_end); 

    process_diff = time_diff(&process_beg, &process_end); 
    thread_diff = time_diff(&thread_beg, &thread_end); 
    printf("count_words() in %s takes %.3fs process time and" 
      "%.3fs thread time\n", file->name, process_diff, thread_diff); 
    return NULL; 
} 

double time_diff(struct timespec *beg, struct timespec *end) { 
    return ((double)end->tv_sec + (double)end->tv_nsec*1.0e-9) 
     - ((double)beg->tv_sec + (double)beg->tv_nsec*1.0e-9); 
} 

नोट

  1. file1 है एक शब्द "शब्द" के 10000 शब्दों के साथ। file2 cp कमांड द्वारा बनाई गई फ़ाइल 1 की एक प्रति है।
  2. निष्पादन समय काफी लंबा बनाने के लिए, प्रोग्राम दोहराव शब्दों की गणना करता है। एन लूप की संख्या है। तो परिणाम कुल शब्दों की सटीक संख्या नहीं है, लेकिन यह एन
  3. से गुणा करता है कृपया गिनती एल्गोरिदम पर बहुत अधिक जोर न दें। मैं इस उदाहरण में निष्पादन समय के बारे में चिंतित हूं।
  4. एक महत्वपूर्ण जानकारी: मशीन इंटेल® सेलेरॉन (आर) सीपीयू 420 @ 1.60GHz है। एक कोर ओएस लिनक्स 3.2.0 है। शायद एक कोर इस अजीब घटना का कारण है जैसे दूसरों ने कहा। लेकिन मैं अभी भी इसे समझना चाहता हूं।

प्रोग्राम शब्दों की गणना करता है और प्रक्रिया cpu समय और नियमित count_words() के थ्रेड सीपीयू समय की गणना करने के लिए clock_gettime() का उपयोग करता है और फिर समय और शब्द संख्या आउटपुट करता है। नीचे आउटपुट और प्रश्नों के साथ मेरी टिप्पणी है। अगर कोई कारण बताएगा कि अतिरिक्त समय क्या लिया जाता है तो मुझे बहुत सराहना की जाएगी।

// version 1 
count_words() in file1 takes 2.563s process time and 2.563s thread time 
count_words() in file2 takes 8.374s process time and 8.374s thread time 
Total words: 40000000 

टिप्पणी: मूल धागा count_words() को समाप्त करता है और नए धागे को मरने की प्रतीक्षा करता है। जब count_words() चल रहा है नया थ्रेड में, कोई संदर्भ स्विच होता है (क्योंकि प्रक्रिया में समय == धागा समय)। इतना समय क्यों लगता है? नए धागे में count_words() में क्या होता है?

// version 2 
count_words() in file1 takes 16.755s process time and 8.377s thread time 
count_words() in file2 takes 16.753s process time and 8.380s thread time 
Total words: 40000000 

टिप्पणी: दो धागे समानांतर यहाँ चलाता है। संदर्भ स्विच होता है, तो प्रक्रिया में समय> धागा समय।

// version 3 
count_words() in file2 takes 8.374s process time and 8.374s thread time 
count_words() in file1 takes 8.365s process time and 8.365s thread time 
Total words: 40000000 

टिप्पणी: नया थ्रेड पहले और मूल धागा इसके लिए इंतजार कर रहा है। नए धागे में शामिल होने के बाद, मूल धागा गिनने लगता है। उनमें से न तो क्यों इतना समय लिया, विशेष रूप से गिनती के बाद नया थ्रेड में शामिल हो गए संदर्भ स्विचिंग है,?

// version 4 
count_words() in file1 takes 2.555s process time and 2.555s thread time 
count_words() in file2 takes 2.556s process time and 2.556s thread time 
Total words: 40000000 

टिप्पणी: सबसे तेजी से संस्करण। कोई नया धागा बनाया गया। दोनों count_words() एक थ्रेड में चलते हैं।

+0

संस्करण 1 + 2 का जिक्र करना: झूठी साझाकरण? शायद यह मदद करता है: http://stackoverflow.com/q/8331255/694576 – alk

+0

@alk: मेरी मशीन में एक कोर है। चूंकि आपके उद्धृत लिंक में स्वीकृत उत्तर में कहा गया है कि झूठी साझाकरण एकाधिक कोर का परिणाम है। क्या एकल कोर पर होना संभव है? –

उत्तर

7

यह शायद इसलिए है क्योंकि किसी भी धागा बलों के निर्माण getc में तुल्यकालन का उपयोग करने के libc है। यह इस समारोह को काफी धीमा बनाता है।

void *skip(void *p){ return NULL; }; 
pthread_create(&new_thread, NULL, skip, NULL); 
count_words(&file1); 
count_words(&file2); 

इस समस्या को दूर करने के लिए आप एक बफर का उपयोग कर सकते हैं:: निम्न उदाहरण मेरे लिए 3 संस्करण के रूप में के रूप में धीमी है

for (i = 0; i < N; i++) { 
    char buffer[BUFSIZ]; 
    int read; 
    do { 
     read = fread(buffer, 1, BUFSIZ, fp); 
     int j; 
     for(j = 0; j < read; j++) { 
      if (!isalnum(buffer[j]) && isalnum(prevc)) 
       file->words++; 
      prevc = buffer[j]; 
     } 
    } while(read == BUFSIZ); 
    fseek(fp, 0, SEEK_SET); 
} 

इस समाधान में, आईओ कार्यों तुल्यकालन भूमि के ऊपर तुच्छ बनाने के लिए शायद ही कभी पर्याप्त कहा जाता है । यह न केवल अजीब समय की समस्या हल करता है, बल्कि यह कई गुना तेजी से बनाता है। मेरे लिए यह 0.54s (धागे के बिना) या 0.85s (धागे के साथ) 0.15s (दोनों मामलों में) से घटा है।

+0

मैंने प्रत्येक विधि में अपनी विधि लागू की है, और सभी थ्रेड सीपीयू और प्रक्रिया सीपीयू समय कम हो गए हैं। अब, count_words() में प्रत्येक स्थिति में 1.20 और 1.30 के बीच एक थ्रेड सीपीयू समय होता है। क्या थ्रेड सुरक्षित सुनिश्चित करने के लिए getc() में उपयोग किए गए सिंक्रनाइज़ेशन का उपयोग प्रत्येक I/O लाइब्रेरी फ़ंक्शन में लॉकिंग का उपयोग करना है? मैंने getc_unlocked() के साथ getc() को बदलने की कोशिश की, जिसने समय को लगभग 1.50 तक घटा दिया। लेकिन अभी भी आपकी विधि से थोड़ा धीमा है और थ्रेड असुरक्षित है। मैंने यह भी पाया कि मेरे उदाहरण में, संदर्भ स्विच समय लेने वाली नौकरी नहीं है। धन्यवाद। –

+0

मैं अभी भी गहरा जाना चाहता हूं। फाइल लॉकिंग हर बार getc() रन होता है। लेकिन एक थ्रेड संदर्भ (8.xxx बनाम 2.xxx) के मुकाबले दो-थ्रेड संदर्भ के तहत इसे और अधिक समय क्यों लगता है? हमें यह भी ध्यान रखना चाहिए कि संस्करण 1, 2, 3 में लॉक की आवश्यकता है। संस्करण 3 में, नए थ्रेड के बाद count_words() चलाता है, जब केवल एक थ्रेड शेष होता है। Getc() को यह नहीं मिल सकता है और जल्दी लॉकिंग (या कुछ और) जैसे संस्करण 4 में करता है? –

+0

@WenhaoYang, हां, इस मामले में सिंक्रनाइज़ेशन लॉक हो रहा है, इसे जल्द ही 'मैन फ्लॉकफाइल' में वर्णित किया गया है। '_unlocked' फ़ंक्शंस यहां सुरक्षित होना चाहिए, क्योंकि प्रत्येक 'FILE' संरचना का उपयोग एक थ्रेड द्वारा किया जाता है। किसी भी थ्रेड बनाने से पहले इसे वास्तव में बहुत तेजी से चलाने का कारण बनता है, मुझे नहीं पता। शायद 'अगर (pthread_create कहा जाता है) सचमुच() 'कुछ स्तर पर? – zch

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