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[])
{
}