C: kinerja pthread, lebih rendah dari single thrad

Saya bingung dengan kinerja kode saya, ketika berhadapan dengan satu thread hanya menggunakan 13 detik, tetapi akan memakan waktu 80 detik. Saya tidak tahu apakah vektor hanya dapat diakses oleh satu utas pada satu waktu, jika demikian kemungkinan besar saya harus menggunakan array struct untuk menyimpan data alih-alih vektor, adakah yang bisa membantu?

#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 sumber
comment
Ini mungkin terjadi suatu saat kode berulir tunggal lebih cepat daripada multi-utas karena waktu peralihan konteks dalam program threading   -  person Grijesh Chauhan    schedule 14.12.2012


Jawaban (1)


Seperti semua struktur data di perpustakaan standar, metode vector masuk kembali, tetapi tidak aman untuk thread. Itu berarti instance yang berbeda dapat diakses oleh beberapa thread secara independen, namun setiap instance hanya dapat diakses oleh satu thread pada satu waktu dan Anda harus memastikannya. Namun karena Anda memiliki vektor terpisah untuk setiap thread, itu bukan masalah Anda.

Yang mungkin masalah Anda adalah printf. printf aman untuk thread, artinya Anda dapat memanggilnya dari sejumlah thread mana pun secara bersamaan, tetapi dengan mengorbankan pengecualian timbal balik secara internal.

Mayoritas pekerjaan di bagian thread dari program Anda dilakukan di dalam printf. Jadi apa yang mungkin terjadi adalah semua thread dimulai dan dengan cepat sampai ke printf, di mana semua thread kecuali yang pertama akan berhenti. Ketika printf selesai dan melepaskan mutex, sistem mempertimbangkan penjadwalan thread yang menunggunya. Mungkin memang demikian, sehingga terjadi peralihan konteks yang agak lambat. Dan ulangi setelah setiap printf.

Bagaimana tepatnya hal ini terjadi bergantung pada primitif penguncian sebenarnya yang digunakan, yang bergantung pada sistem operasi dan versi perpustakaan standar Anda. Sistem seharusnya hanya membangunkan sleeper berikutnya setiap kali, namun banyak implementasi yang benar-benar membangunkan semuanya. Jadi selain printfs yang sebagian besar dieksekusi dengan cara round-robin, menimbulkan satu saklar konteks untuk masing-masing, mungkin ada beberapa bangun palsu tambahan di mana thread hanya menemukan kunci ditahan dan kembali ke mode tidur.

Jadi pelajaran dari hal ini adalah bahwa thread tidak membuat segalanya menjadi lebih cepat secara otomatis. Mereka hanya membantu ketika:

  • Thread menghabiskan sebagian besar waktunya untuk memblokir panggilan sistem. Dalam hal-hal seperti server jaringan, thread menunggu data dari soket, kemudian dari data hingga respons datang dari disk dan akhirnya jaringan menerima respons. Dalam kasus seperti itu, memiliki banyak thread akan membantu selama sebagian besar thread tersebut independen.
  • Jumlah thread sama banyaknya dengan jumlah thread CPU. Saat ini angka yang biasa adalah 4 (baik quad-core atau dual-core dengan hyper-threading). Lebih banyak thread tidak dapat berjalan secara fisik secara paralel, sehingga tidak memberikan keuntungan dan menimbulkan sedikit overhead. 16 utas dengan demikian berlebihan.

Dan mereka tidak pernah membantu ketika mereka memanipulasi objek yang sama, sehingga mereka akhirnya menghabiskan sebagian besar waktu menunggu kunci. Selain objek Anda sendiri yang Anda kunci, perlu diingat bahwa pegangan file input dan output juga harus dikunci secara internal.

Alokasi memori juga perlu melakukan sinkronisasi internal antar thread, namun pengalokasi modern memiliki kumpulan thread terpisah untuk menghindari sebagian besar hal tersebut; jika pengalokasi default terbukti terlalu lambat dengan banyak thread, ada beberapa thread khusus yang dapat Anda gunakan.

person Jan Hudec    schedule 14.12.2012