1

13秒しか使用しないシングルスレッドを処理する場合、コードのパフォーマンスについて混乱していますが、80秒を消費します。ベクトルに一度に 1 つのスレッドしかアクセスできないかどうかはわかりません。その場合、ベクトルの代わりに構造体配列を使用してデータを格納する必要がある可能性が高い場合、誰か親切に助けてもらえますか?

#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;
}
4

1 に答える 1

3

標準ライブラリのすべてのデータ構造と同様に、 のメソッドvectorは再入可能ですが、スレッドセーフではありません。つまり、異なるインスタンスには複数のスレッドが個別にアクセスできますが、各インスタンスには一度に 1 つのスレッドしかアクセスできないため、それを保証する必要があります。ただし、スレッドごとに個別のベクトルがあるため、それは問題ではありません。

おそらくあなたの問題はprintf. printfつまり、任意の数のスレッドから同時に呼び出すことができますが、内部で相互排除にラップされるという犠牲を払っています。

プログラムのスレッド部分での作業の大部分は、内部で行われますprintf。したがって、おそらく起こることは、すべてのスレッドが開始され、すぐに に到達しprintf、最初のスレッドを除くすべてが停止することです。printf が終了してミューテックスを解放すると、システムはそれを待っていたスレッドのスケジューリングを検討します。おそらくそうであるため、かなり遅いコンテキストスイッチが発生します。そして、すべての後に繰り返しますprintf

正確にどのように発生するかは、使用されている実際のロック プリミティブがオペレーティング システムと標準ライブラリのバージョンによって異なります。システムは毎回次​​のスリーパーのみをウェイクアップする必要がありますが、多くの実装では実際にはそれらすべてをウェイクアップします。そのため、printfs がほぼラウンドロビン方式で実行され、それぞれに対して 1 つのコンテキスト スイッチが発生することに加えて、スレッドがロックが保持されていることを検出してスリープ状態に戻る、かなりの数の追加の偽のウェイクアップが発生する可能性があります。

したがって、このことから得られる教訓は、スレッドによって物事が自動的に高速化されるわけではないということです。次の場合にのみ役立ちます。

  • スレッドは、ほとんどの時間をシステム コールのブロックに費やします。ネットワークサーバーのようなものでは、スレッドは、データからの応答がディスクから来て、最後にネットワークが応答を受け入れるのではなく、ソケットからのデータを待ちます。このような場合、スレッドがほとんど独立している限り、多くのスレッドが役立ちます。
  • CPUスレッドと同じくらい多くのスレッドがあります。現在、通常の数は 4 です (クアッドコアまたはハイパースレッディングを備えたデュアルコアのいずれか)。より多くのスレッドは物理的に並列に実行できないため、利益が得られず、多少のオーバーヘッドが発生します。したがって、16 スレッドはやり過ぎです。

そして、それらがすべて同じオブジェクトを操作するときは決して役に立たないため、とにかくロックを待つことにほとんどの時間を費やしてしまいます。ロックする独自のオブジェクトに加えて、入力および出力ファイル ハンドルも内部的にロックする必要があることに注意してください。

メモリの割り当てもスレッド間で内部的に同期する必要がありますが、最新のアロケーターには、スレッドの多くを回避するための個別のプールがあります。デフォルトのアロケーターが多くのスレッドで遅すぎることが判明した場合は、使用できる特殊なアロケーターがいくつかあります。

于 2012-12-14T07:47:11.737 に答える