C: ประสิทธิภาพของ pthread ต่ำกว่า single thrad

ฉันสับสนเกี่ยวกับประสิทธิภาพของโค้ดของฉัน เมื่อต้องจัดการกับเธรดเดี่ยวจะใช้เพียง 13 วินาที แต่จะใช้เวลา 80 วินาที ฉันไม่รู้ว่าเวกเตอร์สามารถเข้าถึงได้ทีละเธรดเท่านั้นหรือไม่ หากเป็นเช่นนั้น อาจเป็นไปได้ว่าฉันต้องใช้อาร์เรย์ struct เพื่อจัดเก็บข้อมูลแทนเวกเตอร์ ใครสามารถช่วยได้บ้าง

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iterator>
#include <string>
#include <ctime>
#include <bangdb/database.h>
#include "SEQ.h"

#define NUM_THREADS 16

using namespace std;


typedef struct _thread_data_t {
    std::vector<FDT> *Query;
    unsigned long start;
    unsigned long end;
    connection* conn;
    int thread;
} thread_data_t;



void *thr_func(void *arg) {

    thread_data_t *data = (thread_data_t *)arg;
    std::vector<FDT> *Query = data->Query;
    unsigned long start = data->start;
    unsigned long end = data->end;
    connection* conn = data->conn;

    printf("thread %d started %lu -> %lu\n", data->thread, start, end);

    for (unsigned long i=start;i<=end ;i++ )
    {
        FDT *fout = conn->get(&((*Query).at(i)));
        if (fout == NULL)
        {
            //printf("%s\tNULL\n", s);

        }
        else
        {
            printf("Thread:%d\t%s\n", data->thread, fout->data);
        }
    }

    pthread_exit(NULL);
}


int main(int argc, char *argv[])
{

    if (argc<2)
    {
        printf("USAGE: ./seq <.txt>\n");
        printf("/home/rd/SCRIPTs/12X18610_L5_I052.R1.clean.code.seq\n");

        exit(-1);
    }
    printf("%s\n", argv[1]);

    vector<FDT> Query;

    FILE* fpin;
    if((fpin=fopen(argv[1],"r"))==NULL)  {
        printf("Can't open Input file %s\n", argv[1]);
        return -1; 
    }

    char *key = (char *)malloc(36);

    while (fscanf(fpin, "%s", key) != EOF)
    {
        SEQ * sequence = new SEQ(key);

        FDT *fk = new FDT( (void*)sequence, sizeof(*sequence) );

        Query.push_back(*fk);
    }

    unsigned long Querysize = (unsigned long)(Query.size());
    std::cout << "myvector stores " << Querysize << " numbers.\n";



    //create database, table and connection
    database* db = new database((char*)"berrydb");

    //get a table, a new one or existing one, walog tells if log is on or off
    table* tbl = db->gettable((char*)"hg19", JUSTOPEN);

    if(tbl == NULL)
    {
        printf("ERROR:table NULL error");
        exit(-1);
    }

    //get a new connection
    connection* conn = tbl->getconnection();
    if(conn == NULL)
    {
        printf("ERROR:connection NULL error");
        exit(-1);
    }

    cerr<<"begin querying...\n";


    time_t begin, end;
    double duration;
    begin = clock();




    unsigned long ThreadDealSize = Querysize/NUM_THREADS;
    cerr<<"Querysize:"<<ThreadDealSize<<endl;



    pthread_t thr[NUM_THREADS];
    int rc;

    thread_data_t thr_data[NUM_THREADS];

    for (int i=0;i<NUM_THREADS ;i++ )
    {
        unsigned long ThreadDealStart = ThreadDealSize*i;
        unsigned long ThreadDealEnd   = ThreadDealSize*(i+1) - 1;

        if (i == (NUM_THREADS-1) )
        {
            ThreadDealEnd = Querysize-1;
        }

        thr_data[i].conn = conn;
        thr_data[i].Query = &Query;
        thr_data[i].start = ThreadDealStart;
        thr_data[i].end = ThreadDealEnd;
        thr_data[i].thread = i;
    }


    for (int i=0;i<NUM_THREADS ;i++ )
    {
        if (rc = pthread_create(&thr[i], NULL, thr_func, &thr_data[i]))
        {
          fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
          return EXIT_FAILURE;
        }
    }


    for (int i = 0; i < NUM_THREADS; ++i) {
        pthread_join(thr[i], NULL);
    }


    cerr<<"done\n"<<endl;
    end = clock();
    duration = double(end - begin) / CLOCKS_PER_SEC;
    cerr << "runtime:   " << duration << "\n" << endl;

    db->closedatabase(OPTIMISTIC);
    delete db;
    printf("Done\n");


  return EXIT_SUCCESS;
}

person user1744416    schedule 14.12.2012    source แหล่งที่มา
comment
อาจเกิดขึ้นในบางครั้งที่โค้ดเธรดเดี่ยวเร็วกว่าแบบมัลติเธรด เนื่องจากเวลาในการสลับบริบทในโปรแกรมเธรด   -  person Grijesh Chauhan    schedule 14.12.2012


คำตอบ (1)


เช่นเดียวกับโครงสร้างข้อมูลทั้งหมดในไลบรารีมาตรฐาน วิธีการ vector จะกลับเข้าใหม่ แต่ไม่ปลอดภัยต่อเธรด นั่นหมายความว่าสามารถเข้าถึงอินสแตนซ์ที่แตกต่างกันได้โดยหลายเธรดโดยแยกจากกัน แต่แต่ละอินสแตนซ์สามารถเข้าถึงได้เพียงครั้งละหนึ่งเธรดเท่านั้น และคุณต้องตรวจสอบให้แน่ใจ แต่เนื่องจากคุณมีเวกเตอร์แยกกันสำหรับแต่ละเธรด นั่นไม่ใช่ปัญหาของคุณ

ปัญหาของคุณน่าจะเป็น printf printf ปลอดภัยสำหรับเธรด ซึ่งหมายความว่าคุณสามารถเรียกได้จากเธรดจำนวนเท่าใดก็ได้ในเวลาเดียวกัน แต่ต้องเสียค่าใช้จ่ายในการรวมการแยกกันภายใน

งานส่วนใหญ่ในส่วนเธรดของโปรแกรมของคุณเสร็จสิ้นภายใน printf ดังนั้นสิ่งที่น่าจะเกิดขึ้นก็คือ เธรดทั้งหมดเริ่มต้นขึ้นและไปที่ printf อย่างรวดเร็ว โดยที่เธรดทั้งหมดจะหยุดทำงาน ยกเว้นเธรดแรก เมื่อ printf เสร็จสิ้นและรีลีส mutex ระบบจะพิจารณาการจัดตารางเวลาเธรดที่กำลังรออยู่ มันอาจจะเป็นเช่นนั้น ดังนั้นการสลับบริบทค่อนข้างช้าจึงเกิดขึ้น และทำซ้ำทุกๆ printf

มันจะเกิดขึ้นได้อย่างไรนั้นขึ้นอยู่กับว่ามีการใช้การล็อกดั้งเดิมแบบใด ซึ่งขึ้นอยู่กับระบบปฏิบัติการและเวอร์ชันไลบรารีมาตรฐานของคุณ ในแต่ละครั้งระบบควรปลุกเฉพาะผู้นอนหลับรายถัดไปเท่านั้น แต่การใช้งานจำนวนมากจะปลุกการทำงานทั้งหมดจริงๆ ดังนั้นนอกเหนือจาก printfs ที่ถูกดำเนินการในรูปแบบ Round-robin เกือบทั้งหมด ซึ่งจะมีการสลับบริบทหนึ่งครั้งสำหรับแต่ละอัน ยังอาจมีการปลุกปลอมเพิ่มเติมอีกสองสามรายการ โดยที่เธรดเพิ่งพบว่ามีการล็อคค้างไว้และกลับไปเข้าสู่โหมดสลีป

ดังนั้นบทเรียนจากเรื่องนี้ก็คือ เธรดไม่ได้ทำให้สิ่งต่าง ๆ เร็วขึ้นโดยอัตโนมัติ ช่วยได้ก็ต่อเมื่อ:

  • เธรดใช้เวลาส่วนใหญ่ในการบล็อกการโทรของระบบ ในสิ่งต่างๆ เช่น เซิร์ฟเวอร์เครือข่าย เธรดจะรอข้อมูลจากซ็อกเก็ต มากกว่าจากข้อมูลเพื่อการตอบสนองที่มาจากดิสก์ และสุดท้ายคือเครือข่ายจะยอมรับการตอบสนอง ในกรณีเช่นนี้ การมีเธรดจำนวนมากจะช่วยได้ตราบใดที่เธรดเหล่านั้นส่วนใหญ่แยกจากกัน
  • มีเธรดจำนวนมากเนื่องจากมีเธรด CPU ปัจจุบันตัวเลขปกติคือ 4 (อาจเป็นแบบ quad-core หรือ dual-core ที่มีไฮเปอร์เธรด) เธรดจำนวนมากไม่สามารถทำงานแบบขนานได้จริง ดังนั้นจึงไม่ได้รับผลกำไรและมีค่าใช้จ่ายเล็กน้อย 16 เธรดจึงเกินความจำเป็น

และพวกเขาไม่เคยช่วยอะไรเลยเมื่อพวกเขาจัดการกับวัตถุเดียวกัน ดังนั้นพวกเขาจึงใช้เวลาส่วนใหญ่ในการรอล็อคอยู่ดี นอกเหนือจากออบเจ็กต์ของคุณเองที่คุณล็อคแล้ว โปรดทราบว่าตัวจัดการไฟล์อินพุตและเอาท์พุตจะต้องถูกล็อคภายในเช่นกัน

การจัดสรรหน่วยความจำยังจำเป็นต้องซิงโครไนซ์ภายในระหว่างเธรดภายใน แต่ตัวจัดสรรสมัยใหม่มีพูลแยกต่างหากสำหรับเธรดเพื่อหลีกเลี่ยงปัญหาส่วนใหญ่ หากตัวจัดสรรเริ่มต้นช้าเกินไปสำหรับเธรดจำนวนมาก คุณสามารถใช้เธรดพิเศษบางอย่างได้

person Jan Hudec    schedule 14.12.2012