1

我对我的代码的性能感到困惑,在处理单线程时它只使用 13 秒,但它会消耗 80 秒。我不知道向量是否一次只能由一个线程访问,如果是这样,我可能必须使用结构数组而不是向量来存储数据,有人可以帮忙吗?

#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是可重入的,但不是线程安全的。这意味着不同的实例可以被多个线程独立访问,但每个实例一次只能被一个线程访问,你必须确保它。但是由于每个线程都有单独的向量,所以这不是你的问题。

您的问题可能是printf. printf是线程安全的,这意味着您可以同时从任意数量的线程调用它,但代价是在内部被互斥。

程序线程部分的大部分工作都是在内部完成的printf。所以可能发生的情况是所有线程都已启动并迅速到达 ,printf除第一个之外的所有线程都将停止。当 printf 完成并释放互斥锁时,系统会考虑调度正在等待它的线程。它可能确实如此,因此会发生相当慢的上下文切换。并在每个 之后重复printf

具体如何发生取决于正在使用的实际锁定原语,这取决于您的操作系统和标准库版本。系统每次应该只唤醒下一个睡眠者,但许多实现实际上唤醒了所有睡眠者。因此,除了printfs 主要以循环方式执行之外,每个都会导致一个上下文切换,还可能有相当多的额外虚假唤醒,其中线程刚刚发现锁被持有并重新进入睡眠状态。

因此,从中得到的教训是线程不会自动使事情变得更快。他们只在以下情况下提供帮助:

  • 线程大部分时间都花在阻塞系统调用上。在诸如网络服务器之类的事物中,线程等待来自套接字的数据,而不是等待来自磁盘的响应数据,最后等待网络接受响应。在这种情况下,拥有许多线程会有所帮助,只要它们大多是独立的。
  • 线程数与 CPU 线程数一样多。目前通常的数字是 4(四核或超线程双核)。更多的线程不能在物理上并行运行,因此它们不会提供任何收益并且会产生一些开销。因此16个线程是多余的。

当他们都操纵相同的对象时,他们从来没有帮助过,所以他们最终还是把大部分时间都花在了等待锁上。除了您锁定的任何您自己的对象之外,请记住输入和输出文件句柄也必须在内部锁定。

内存分配也需要在线程之间进行内部同步,但是现代分配器有单独的线程池来避免其中的大部分;如果默认分配器被证明对于许多线程来说太慢了,那么您可以使用一些专门的分配器。

于 2012-12-14T07:47:11.737 回答