2016-12-29 86 views
3

我有一个沉重的数学计算来算的范围内twin prime数字的号码,我已经划分线程之间的任务。多线程基准

在这里您可以看到针对线程数量的执行时间配置文件。

我的问题是有关的理由:

  1. 为什么单线程和双线程有非常相似的表现吗?

  2. 为什么有在执行时间的下降当它被5-或7-螺纹,而当使用执行时间增加6个或8个线程? (我所经历,在若干测试。)

  3. 我已经使用一个8芯计算机。我可以声称2 × ñ(其中ñ是核心数量)是一个经验法则良好的线程数?

  4. 如果我使用一个代码RAM的高使用率,我希望在配置文件中类似的趋势,或将它显着地与越来越多的线程有变化吗?

multithreading benchmark

这是代码的主要部分,只显示它不使用多少内存。

bool is_prime(long a) 
{ 
    if(a<2l) 
     return false; 
    if(a==2l) 
     return true; 
    for(long i=2;i*i<=a;i++) 
     if(a%i==0) 
      return false; 
    return true; 
} 

uint twin_range(long l1,long l2,int processDiv) 
{ 
    uint count=0; 
    for(long l=l1;l<=l2;l+=long(processDiv)) 
     if(is_prime(l) && is_prime(l+2)) 
     { 
      count++; 
     } 
    return count; 
} 

规格:

$ lsb_release -a 

Distributor ID: Ubuntu 
Description: Ubuntu 16.04.1 LTS 
Release: 16.04 
Codename: xenial 

$ lscpu 

Architecture:   x86_64 
CPU op-mode(s):  32-bit, 64-bit 
Byte Order:   Little Endian 
CPU(s):    8 
On-line CPU(s) list: 0-7 
Thread(s) per core: 2 
Core(s) per socket: 4 
Socket(s):    1 
NUMA node(s):   1 
Vendor ID:    GenuineIntel 
CPU family:   6 
Model:     94 
Model name:   Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz 
Stepping:    3 
CPU MHz:    799.929 
CPU max MHz:   4000.0000 
CPU min MHz:   800.0000 
BogoMIPS:    6815.87 
Virtualisation:  VT-x 
L1d cache:    32K 
L1i cache:    32K 
L2 cache:    256K 
L3 cache:    8192K 
NUMA node0 CPU(s):  0-7 
Flags:     fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch intel_pt tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp 

更新(公认的答案后)

新轮廓:

Multi-threading performance

改进的代码如下。现在,工作量分配相当。

bool is_prime(long a) 
{ 
    if(a<2l) 
     return false; 
    if(a==2l) 
     return true; 
    for(long i=2;i*i<=a;i++) 
     if(a%i==0) 
      return false; 
    return true; 
} 


void twin_range(long n_start,long n_stop,int index,int processDiv) 
{ 
    // l1+(0,1,...,999)+0*1000 
    // l1+(0,1,...,999)+1*1000 
    // l1+(0,1,...,999)+2*1000 
    // ... 

    count=0; 
    const long chunks=1000; 
    long r_begin=0,k=0; 
    for(long i=0;r_begin<=n_stop;i++) 
    { 
     r_begin=n_start+(i*processDiv+index)*chunks; 
     for(k=r_begin;(k<r_begin+chunks) && (k<=n_stop);k++) 
     { 
      if(is_prime(k) && is_prime(k+2)) 
      { 
       count++; 
      } 
     } 
    } 

    std::cout 
     <<"Thread "<<index<<" finished." 
     <<std::endl<<std::flush; 
    return count; 
} 
+0

您有8个CPU,并且在8个线程之间几乎没有变化?不知道你是如何产卵,但可能指出你的问题之一。你是个人资料,是分期付款还是一次性付款? –

+0

@PaulEvans,我预计8个线程代码的工作速度比7个线程快。也许这是因为我们应该为另一个进程计算另一个线程。我想你是对的。但是5个线程代码有一个戏剧性的下降,我找不到任何理由。 – ar2015

+0

你在计算'main()'线程吗? –

回答

2

想想看,当最后线程完成检查其范围内的数字程序将结束。也许有些线程比其他线程更快?

is_prime()需要多长时间才能确定偶数是否为质数?它会在第一次迭代中找到它。如果a是素数,则查找奇数的素数将需要至少两次迭代并可能达到sqrt(a)迭代。 is_prime()当它被赋予大于偶数的数时,将会非常慢!

在你的两个线程的情况下,一个线程将检查100000000,100000002,100000004等的素数,而另一个线程将检查100000001,100000003,100000005等。一个线程检查所有偶数,而其他检查所有的奇数(包括所有那些缓慢的素数!)。

当你的线程完成时,你的线程会打印出("Thread at %ld done", l1),我想你会发现有些线程比其他线程快得多,这是由于你在线程之间划分域的方式。偶数个线程会将所有偶数值赋给相同的线程,导致分区特别差,这就是为什么您的偶数线程数比奇数更慢的原因。

这将是一个伟大的XKCD式的漫画。 “我们需要检查所有这些数字来寻找素数!手动!” “好的,我会检查一下,你做的可能性。”

这里你真正的问题是,像你这样的固定域分解要求每个分区都花费相同的时间来达到最优。

解决此问题的方法是动态执行分区。通常使用的模式涉及请求以块为单位工作的工作线程池。如果块相对于线程将执行的总工作量较小,则所有线程将在相似的时间内完成其工作。

对于你的问题,你可以有一个互斥体保护全局数据集start_number, stop_number, total_twins。每个线程在将其全局值增加chunk_size之前将保存start_number。然后它搜索范围[saved_start_number, saved_start_number+chunk_size),完成后将发现的双胞胎数量添加到全球total_twins。工作者线程继续这样做直到start_number >= stop_number。对全局变量的访问使用互斥体进行保护。必须调整块大小,以便从获得块和争用互斥体的成本中节约低效率,而空闲工作线程的低效率不会有更多块需要分配,而另一个线程仍在处理最后一块。如果使用原子增量来请求块,也许块大小可能与单个值一样小,但是如果在分布式计算系统中需要网络请求,则块大小需要更大。这是它如何工作的概述。

顺便说一句,你的is_prime()测试是天真的和极其缓慢。如果一个数不能被2整除,是否可以除以4?人们可以做得更好!

+0

非常感谢。我已经修复了代码和[新的配置文件](https://github.com/Arash-codedev/multi-threading-benchmark)对我来说仍然没有问题。 – ar2015

0

8个线程,因为你有8个CPU将不低于7提高工作效率(那些明明只有处理一个线程 - 编辑:感谢@Algridas - 从应用程序)每个和你main()需要一个线程运行。

+0

_和您的'main()'需要一个线程来运行._除了OP计算机上的所有其他应用程序。 :) –

+0

谢谢。你对他的其他问题有什么看法? – ar2015

+0

我问了类似的问题,他显然有一个关于其他应用程序与您同时运行的观点。 –