2017年計算機(jī)等級考試二級C++輔導(dǎo):怎樣提高調(diào)用數(shù)學(xué)函數(shù)的程序的性能

字號:


    MASS 高性能庫
    MASS 指的是數(shù)學(xué)加速子系統(tǒng)(Mathematical Acceleration Subsystem)。它由數(shù)學(xué)函數(shù)組成,這些數(shù)學(xué)函數(shù)是為在各種 IBM 計算平臺上優(yōu)化性能所設(shè)定的。MASS 最初是由 IBM 公司在 1995 年啟動的,并在隨后的發(fā)展中繼續(xù)得到改善,一直到現(xiàn)如今仍然在改進(jìn)。
    現(xiàn)在所有的 IBM® POWER™ 處理器都有相應(yīng)的各種版本的 MASS,運(yùn)行 AIX® 或者 Linux® 操作系統(tǒng)。還有其他版本的 IBM System BlueGene®/L 和 IBM System BlueGene®/P 超級電腦,以及 IBM Cell Broadband Engine™ (Cell/B.E.™)方案。庫包含有元素函數(shù)的加速實施方案,例如三角函數(shù)以及雙曲線函數(shù)以及它們的倒數(shù)、乘方、對數(shù)、指數(shù)、錯誤函數(shù)以及其他函數(shù)。包含函數(shù)的完整列表可以在 IBM Mathematical Acceleration Subsystem 頁面中找到。
    有標(biāo)量的庫也有向量的庫,而對于 Cell/B.E. 和 POWER7 來說,還有單個指示的多個數(shù)據(jù)(SIMD)庫。注意精確性與例外情況的處理在 MASS 函數(shù)與系統(tǒng)庫函數(shù)中可能是不一樣的。對于目標(biāo)硬件的其他匯編器(例如 gcc)的用戶來說,MASS 庫與 IBM XL C/C++ 還有 XL Fortran 匯編器封裝到一起,并且可以通過 MASS Web 網(wǎng)站來獲得。
    可以通過 C、 C++ 或者 Fortran 源程序來訪問庫。IBM XL C/C++ 與 IBM XL Fortran 匯編器可以識別機(jī)會以使用 MASS 來加速源程序,并自動激活它而不用更改源程序。本文向您介紹了怎樣實施一項技術(shù)幫助您的公司更好地使用這些強(qiáng)大的技術(shù)。
    什么程序可以獲益?
    任何包含有對數(shù)學(xué)庫函數(shù)(例如 exp、 log、sin、cos 等等)調(diào)用的 C、C++ 或者 Fortran 程序,潛在意義上都會從本文中所描述的技術(shù)中受益。
    什么是自動化向量?
    自動化向量技術(shù)是一種過程,在這個過程中 IBM XL C/C++ 或者 Fortran 匯編器會識別一個機(jī)會,去改善匯編過程中程序的性能,方法就是將對一次循環(huán)中一個標(biāo)準(zhǔn)庫(C/C++ 庫或者 Fortran 本質(zhì))的訪問替換為對相應(yīng) MASS 向量函數(shù)的訪問。因為 MASS 向量函數(shù)要比對一個標(biāo)準(zhǔn)庫函數(shù)的重復(fù)訪問快很多(倍數(shù)接近 30 倍),所以最后得到的性能改善效果將會是驚人的。
    一個簡單的例子就是為多個論斷計算特定函數(shù)的循環(huán),例如接下來的 Fortran 程序。
    subroutine sub (y, x, n)
    real*8 y(*), x(*)
    integer n, i
    intrinsic exp
    do i=1,n
    y(i) = exp(x(i)) ! standard intrinsic
    end do
    end subroutine
    有了適當(dāng)?shù)膮R編器選項,匯編器就會意識到機(jī)會去給程序加速,方法就是將對 exp() 的重復(fù)訪問替換為相應(yīng)的 MASS 向量函數(shù) vexp(),結(jié)果會產(chǎn)生一個程序,好像最開始是這樣寫成的這樣:
    include 'massv.include'
    subroutine sub (y, x, n)
    real*8 y(*), x(*)
    integer n
    call vexp (y, x, n) ! MASS vector function
    end subroutine
    這只是一個簡單的范例,演示了自動向量化背后的基本思想。XL 匯編器實際上能夠識別更加復(fù)雜的機(jī)會,并在需要的條件下重新安排源程序中的指南,以創(chuàng)建自動向量化的機(jī)會。
    在本文中的范例研究部分中,會檢查一個更加復(fù)雜和實際的范例。
    自動向量化的匯編器選項
    您可以使用以下的幾個選項來匯編程序:
    -qhot -qnostrict (for Fortran)
    -qhot -qnostrict –qignerrno (for C/C++)
    -qhot -O3
    -O4
    -O5
    當(dāng)您在使用這些選項集中的一個時,通過調(diào)用等價 MASS 向量函數(shù)(除了對以下函數(shù)的訪問除外:vatan2、vsatan2、 vdnint、 vdint、 vcosisin、vscosisin、vqdrt、vsqdrt、vrqdrt、vsrqdrt、vpopcnt4、vpopcnt8、vexp2、 vexp2m1、vsexp2、 vsexp2m1、vlog2、 vlog21p、 vslog2 和 vslog21p),匯編器會自動嘗試對系統(tǒng)數(shù)學(xué)函數(shù)的訪問向量化。如果匯編器不能對程序進(jìn)行向量化,它會自動試著調(diào)用等價 MASS 標(biāo)量函數(shù)。對于自動化的標(biāo)量或者向量,匯編器會使用匯編器庫 libxlopt.a 中包含的 MASS 函數(shù)的版本。您不需要向代碼中的 MASS 函數(shù)添加任何特意的調(diào)用,或者鏈接 xlopt 庫。
    除了一系列的選項之外,當(dāng) -qipa 選項處于可用狀態(tài)時,如果匯編器不能進(jìn)行向量化,那么它會試著在決定調(diào)用它們之前去內(nèi)聯(lián) MASS 標(biāo)量函數(shù)。
    如果您想要取消自動向量化的激活,那么您可以添加選項 –qhot=novector。
    用例研究
    接下來的部分是一個實際程序的范例 — 一個離散的 Fourier 轉(zhuǎn)變(DFT) — 顯示了在匯編不同匯編器選項時的改善結(jié)果。程序已經(jīng)足夠簡單以方便演示,然后又足夠的復(fù)雜以提供非瑣細(xì)的優(yōu)化機(jī)會。
    兩個程序的計時都是在附錄 3 中給出的驅(qū)動器程序完成的,運(yùn)行的環(huán)境是在 4.704 GHz 下運(yùn)行的 POWER6 電腦。
    附錄 1 顯示了 Fortran DFT 源程序。它包含了一個嵌套的循環(huán),該循環(huán)會調(diào)用 exp()、cos() 以及 sin(),接下來是一個調(diào)用 sin() 和 sqrt() 的循環(huán)。程序會使用 -O3(它并不能進(jìn)行自動向量化) 并使用 –O4 (它能使用自動向量化)。
    注意自動向量化帶來的好處會隨著問題規(guī)模的增加而增加,最終當(dāng)問題的規(guī)模達(dá)到 2000 時加速的程度會達(dá)到 8.94x 。
    附錄 2 顯示了附錄 1 中 Fortran DFT 程序的 C 版本(它包含了一個虛 consume() 路徑,這樣匯編器的內(nèi)部程序化分析[IPA]就不能看到,計算的結(jié)果實際上在演示范例中并沒有用得上,并因此可以改善整個的程序)。
    程序?qū)褂?-O3(它并不會提供自動向量化) ,使用 -O4 (它提供自動向量化),使用 –O5 (它提供自動向量化并提供 IPA)。
    正如在 Fortran 范例中演示的那樣,自動向量化帶來的好處隨著問題規(guī)模的增加而增加,最后當(dāng) n=2000 的時候達(dá)到了。另外,IPA 在 -O5 處提供的活化能夠提供一個額外的 1.22x 加速,因為它可以決定輸入與輸出沒有別名(這就是說,它沒有在內(nèi)存中重疊),允許它去向量化進(jìn)行極坐標(biāo)的轉(zhuǎn)變。-O5 在 –O3 的基礎(chǔ)上加速的程度是 7.33x 。
    結(jié)論
    本文向您提供了對 IBM MASS 庫以及 IBM XL C/C++ 和 XL Fortran 匯編器的自動向量化功能的描述。另外,本文演示了對范例程序(離散 Fourier 轉(zhuǎn)變)使用各種匯編器選項的操作,向您展示了通過使用 MASS 自動向量化的自動調(diào)用功能,使得與以前版本相比速度提高了 8.94 倍的效果。
    這種演示想要通過一種程序來鼓勵用戶,這種程序會訪問數(shù)學(xué)函數(shù)以驗證可用的匯編器選項,并從 IBM XL C/C++ 或者 XL Fortran 匯編器的自動向量化加速中獲益。
    附錄 1 – Fortran DFT 源程序
    subroutine dft (x, a, phi , n)
    real*8 x(n), a(n), phi(n)
    integer n
    ! Compute discrete Fourier transform of real inputs    ! x(i) and convert to polar form.
    real*8, parameter :: pi=3.1415926535897932384d0
    real*8 y_re(n), y_im(n), t, term_re, term_im
    intrinsic exp, cos, sin, sqrt, atan
    y_re(1:n) = 0.d0
    y_im(1:n) = 0.d0
    do k=1,n
    ! compute y(k), k-th DFT output
    do i=1,n
    ! compute i-th term of y(k):
    ! x(k)*exp(-2*pi*I*(k-1)*(i-1)/n)
    ! compute real and imaginary parts of i-th term
    ! using exp(I*t)=exp(t)*(cos(t)+I*sin(t))
    t = -2.d0*pi*(k-1)*(i-1)/n
    term_re = x(i) * cos(t) * exp(t)
    term_im = x(i) * sin(t) * exp(t)
    ! add term to sum
    y_re(k) = y_re(k) + term_re
    y_im(k) = y_im(k) + term_im
    end do
    end do
    ! transform y to polar coordinates
    do k=1,n
    ! compute amplitude of y(k)
    a(k) = sqrt (y_re(k)**2 + y_im(k)**2)
    ! compute phase of y(k)
    phi(k) = atan (y_im(k) / y_reim(k))
    end do
    end subroutine
    ! initialize input data
    subroutine init (a, n)
    real*8 a(n)
    integer n
    intrinsic sin,sqrt
    do j=1,n
    a(j)=sin(1.d0/sqrt(real(j,8)))
    end do
    end subroutine
    附錄 2 – C DFT 源程序
    #include 
    #define PI 3.1415926535897932384
    void dft(double x[],double a[],double phi[],int *m)
    {
    double y_re[NMAX], y_im[NMAX], t, s, term_re, term_im;
    int i,j,k,n=*m;
    for(i=0;i  y_re[i]=y_im[i]=0;
    }
    for(k=0;k  {
    // compute y(k), k-th DFT output
    for(i=0;i  {
    // compute i-th term of y(k):
    // x(k)*exp(-2*pi*I*(k-1)*(i-1)/n)
    // compute real and imaginary parts of i-th term
    // using exp(I*t)=exp(t)*(cos(t)+I*sin(t))
    t=-2.*PI*k*i/(double)n;
    term_re=x[i]*exp(t)*cos(t);
    term_im=x[i]*exp(t)*sin(t);
    // add term to sum
    y_re[k]+=term_re;
    y_im[k]+=term_im;
    }
    }
    // transform y to polar coordinates
    for(k=0;k  {
    // compute amplitude of y(k)
    a[k]=sqrt(y_re[k]*y_re[k]+y_im[k]*y_im[k]);
    // compute phase of y(k)
    phi[k]=atan2(y_im[k],y_re[k]);
    }
    }
    // initialize input data
    void init(double a[],int *m)
    {
    int j,n=*m;
    for(j=0;j  {
    a[j]=sin(1./sqrt((double)j+1.0));
    }
    }
    // Dummy function to use result, preventing compiler from
    // optimizing away the computation.
    void consume(double a[],double b[],double c[])
    {
    }