簡體   English   中英

多線程基准

[英]Multi-threading benchmark

我有一個繁重的數學計算來計算一個范圍內的孿生素數的數量,我已經在線程之間划分了任務。

在這里,您可以看到執行時間與線程數的關系。

我的問題是關於:

  1. 為什么單線程和雙線程的性能非常相似?

  2. 為什么在5線程或7線程時執行時間會下降,而在使用6或8個線程時執行時間會增加? (我在幾次測試中都經歷過這種情況。)

  3. 我使用的是8核計算機。 根據經驗,我可以聲稱2× n (其中n是核心數)是一個很好的線程數嗎?

  4. 如果我使用具有高RAM使用率的代碼,我會期望在配置文件中有類似的趨勢,還是會隨着線程數量的增加而急劇變化?

多線程基准

這是代碼的主要部分,只是為了表明它沒有使用太多的RAM。

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

更新(在接受的答案之后)

新的配置文件:

多線程性能

改進的代碼如下。 現在,工作量得到公平分配。

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

考慮到當最后一個線程完成檢查其數字范圍時,您的程序將完成。 也許有些線程比其他線程更快?

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嗎? 一個人可以做得更好!

8個線程的工作速度不會超過7,因為你有8個CPU(顯然只處理一個線程 - 編輯:感謝@Algridas - 來自你的應用程序 ),你的main()需要一個線程來運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM