我注意到,为一个代码运行一个线程要比拥有一个线程慢得多,而且我一直在努力地想知道为什么,有人可以帮忙吗?

代码说明:
我有时有一个非常大的数组,我需要以并行方式来处理其中的部分以进行优化,每一行的“一部分”都会在特定线程中循环并进行处理,现在我注意到,如果我只是有一个“部分”,即整个数组和贯穿其中的单个工作线程要比将数组划分并将其作为具有不同线程的单独子数组处理时要快得多。

    bool m_generate_row_worker(ull t_row_start,ull t_row_end)
    {
        for(;t_row_start<t_row_end;t_row_start++)
            {
                m_current_row[t_row_start]=m_singularity_checker(m_previous_row[t_row_start],m_shared_random_row[t_row_start]);

            }
        return true;
    }

    ...
    //code
    ...
    for(unsigned short thread_indx=0;thread_indx<noThreads-1;thread_indx++)
    {
        m_threads_array[thread_indx]=std::thread(
                m_generate_row_worker,this,
                thread_indx*(m_parts_per_thread),(thread_indx+1)*(m_parts_per_thread));
    }
    m_threads_array[noThreads-1]=std::thread(m_generate_row_worker,this,
            (noThreads-1)*(m_parts_per_thread),std::max((noThreads)*(m_parts_per_thread),m_blocks_per_row));
    //join
    for(unsigned short thread_indx=0;thread_indx<noThreads;thread_indx++)
    {
        m_threads_array[thread_indx].join();
    }
//EDIT
    inline ull m_singularity_checker(ull t_to_be_ckecked_with,ull
    t_to_be_ckecked)
    {
            return (t_to_be_ckecked & (t_to_be_ckecked_with<<1)
             & (t_to_be_ckecked_with>>1) ) | (t_to_be_ckecked_with &
    t_to_be_ckecked);
    }

最佳答案


  • 因为创建线程有开销。如果要执行的任务仅具有很小的计算成本,那么创建多个线程的成本将超过并行处理所节省的时间。当创建的线程远远多于CPU内核时,尤其是这种情况。
  • 因为许多算法不容易分成独立的子任务。对其他线程的依赖关系需要同步,这种同步的开销在某些情况下可能会比并行处理节省的时间更多。
  • 因为在设计不良的程序中,同步可能导致顺序执行所有任务,即使它们在单独的线程中也是如此。
  • 因为(取决于CPU体系结构)有时可以正确地实现,并且看似独立的任务具有有效的依赖性,因为它们在相同的内存区域上运行。更具体地说,当一个线程写入一块内存时,在同一高速缓存行上运行的所有线程都必须同步(CPU会自动为您执行此操作)以保持一致。高速缓存未命中的成本通常比并行处理节省的时间高得多。此问题称为“虚假共享”。
  • 因为有时引入多线程会使程序更加复杂,这使得编译器/优化器更难以利用指令级并行性。
  • ...

  • 结论:线程不是自动提高程序性能的 Elixir 。

    关于您的程序,鉴于您所摘录的内容,我们无法排除上述任何潜在问题。

    避免或发现上述问题的一些技巧:
  • 创建的线程数不要超过内核数,这会减少预期阻塞的线程数(等待输入,磁盘等)。
  • 仅在计算量大的问题上使用多线程(或在线程阻塞时进行工作,但是使用异步I/O和协程可以更有效地解决此问题)。
  • 除非经过特别设计以处理一个线程,否则不要从一个以上的线程对单个设备(磁盘,NIC,虚拟终端等)执行I/O(或尽可能少地执行I/O)。
  • 最小化线程之间的依赖关系数量。考虑对可能导致同步的全局事物的所有访问,并避免使用它们。例如,避免内存分配。请记住,诸如对标准容器的操作之类的事情都会进行内存分配。
  • 保持内存彼此分开(彼此不相邻)。如果处理数组,请将其分成连续的块,而不是在第(线程数)个元素中剥离一个元素。在某些极端情况下,将额外的拷贝复制到特定于线程的数据结构中,然后再进行最终加入可能是有效的。
  • 如果您已尽力而为,并且多线程处理的速度变慢,请考虑这也许不是解决您问题的好方法。
  • 10-06 09:12
    查看更多