【问题标题】:memcpy slow when called in thread在线程中调用时 memcpy 很慢
【发布时间】:2017-10-05 04:43:14
【问题描述】:

我对 memcpy 有一个问题,我似乎不明白。我在线程上使用 memcpy,与从主线程运行它时获得的时间相比,它慢了 3-4 倍。在这两种情况下,我都有 2 个线程在运行,一个正在等待,一个正在调用 memcpy。你能给我任何可能的解释吗?我使用具有超线程的 4 核 Intel 机器。

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
#include <pthread.h>
#include <algorithm>
#define MILLION 1000000

#define START_TIMER(timer) {          \
  gettimeofday(&(timer), NULL);           \
}

#define STOP_TIMER(timer) {         \
  gettimeofday(&(timer), NULL);         \
}

#define TIME_DIFF(timer1, timer2, total) {      \
  long long sec_diff = 0;         \
  long long usec_diff = 0;          \
  sec_diff = (timer2).tv_sec - (timer1).tv_sec;     \
  usec_diff = (timer2).tv_usec - (timer1).tv_usec;    \
  (total)+= (sec_diff * MILLION) + usec_diff;      \
}
void copy(){
    struct timeval start, stop;
    long long total=0;
    char buff[1024*1024];
    for(int i =0;i<100;i++){

    char* temp = new char[1024*1024];
    START_TIMER(start);
    std::copy(buff,buff+1024*1024,temp);
    STOP_TIMER(stop);
    TIME_DIFF(start,stop,total);
    delete temp;
    }
    printf("%lld\n",total/100 );
}
void* mem(void* args){
    copy();
    pthread_exit(NULL);
}

void * nothing(void *args){
    pthread_exit(NULL);

}
pthread_t thread;
int main(int argc,char* argv[]){
    if(atoi(argv[1])==0){
        pthread_create(&thread,NULL,nothing,NULL);
        pthread_join(thread,NULL);
        copy();
    }
    else{

        pthread_create(&thread,NULL,mem,NULL);
        pthread_join(thread,NULL);
    }
}

感谢您的宝贵时间。我希望这不是太愚蠢。

【问题讨论】:

  • 如果在 i 循环之前初始化 buff 会发生什么?
  • 请分享您从 main 和线程调用复制函数的代码。这样我们就可以尝试重现问题。
  • 如果将 main() 中的 copy() 移到 thread create 之上,即先运行主线程 copy,会发生什么?
  • Sush 复制功能就在 main 的上方。 @ThingyWotsit 只有一个线程会执行副本。您选择了哪一个作为执行参数。 0为主线程,其他为创建线程

标签: c++ c multithreading pthreads memcpy


【解决方案1】:

调用“复制”函数时,进程的线程数似乎有所不同。当从主线程调用它时,'nothing'线程已经退出,因此实际上只有一个线程用于该进程。但是,虽然它是从“mem”线程调用的,但系统中实际上有两个线程。如果系统已加载或进程的线程之间存在争用,这可能会有所不同。如果在另一台机器或负载较小的机器上运行,则可能不存在此时间差。可以通过更改代码来验证此理论,例如您在空线程中等待并仅在调用复制函数后才取消它。

【讨论】:

  • 你是对的,副本应该在join之前。我解决了这个问题,但我仍然有相同的结果。感谢您的更正。
【解决方案2】:

起初我无法让你的代码运行,当它调用 copy() 时发生线程(传递 1 作为参数)。 pthread_create 分配了一个小的堆栈大小,并且声明 1MB 数组 buff 导致了分段错误。

我修改了代码,为线程分配了更大的堆栈:

int main(int argc,char* argv[]){
    if(atoi(argv[1])==0){
        pthread_create(&thread,NULL,nothing,NULL);
        pthread_join(thread,NULL);
        copy();
    }
    else{
        pthread_attr_t thread_attr;
        pthread_attr_init(&thread_attr);
        pthread_attr_setstacksize(&thread_attr , 20*1024*1024);
        pthread_create(&thread, &thread_attr,mem,NULL);
        pthread_join(thread,NULL);
    }
}

这行得通,并且在我机器上的任一线程上复制之间的运行时间没有区别。

但是,您的操作系统可能会导致堆争用。每次它需要分配内存时,它都需要获得对“互斥锁”(某种,可能是自旋锁)的控制权,以确保没有其他线程正在从堆中分配/释放。这会导致您遇到的延迟。

【讨论】:

  • 该代码照原样对我有用,我得到大约 100 微秒与 300 多微秒。我也试过你的代码,但我仍然有同样的问题。谢谢你的时间,我很感激。我刚刚看到您对堆上的操作系统和互斥锁的编辑。这不应该为这两种情况造成问题吗?为什么只有其中一个如此明显
  • 您是否阅读了我关于堆争用的更新?我认为我们的架构处理堆的方式不同。
  • (我在具有 4 核和超线程的 I7 上运行,操作系统是 Mac OS 10.11 btw)
  • 在这种情况下没有堆争用 - 有一个线程进行分配。 @Pol 尝试在循环之前用一些数据填充buffbuff 被分配到物理内存可能会导致额外的延迟。
  • @AndreyTurkin 你能复制这个吗?如果时间差异是由物理分配引起的,那么线程之间会有什么不同?
猜你喜欢
  • 1970-01-01
  • 2012-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多