As bibliotecas de alto desempenho MASS
MASS significa Mathematical Acceleration Subsystem (subsistema de aceleração matemática). É composto por bibliotecas de funções matemáticas especificamente ajustadas para desempenho ideal em várias plataformas de computação IBM. MASS foi originalmente lançado pela IBM em 1995 e tem sido aprimorado e expandido continuamente desde então.
Há versões atuais de MASS para todos os processadores IBM® POWER™, executando sistemas operacionais AIX® ou Linux®. Há também versões para os supercomputadores IBM System BlueGene®/L e IBM System BlueGene®/P, bem como para a solução IBM Cell Broadband Engine™ (Cell/B.E.™). As bibliotecas contêm implementações aceleradas de funções elementares, como trigonométricas e hiperbólicas e seus inversos, potência, logaritmos, exponenciais, função de erro e outras. Listas completas das funções incluídas estão disponíveis na página IBM Mathematical Acceleration Subsystem.
Há bibliotecas escalares e de vetor e, para Cell/B.E. e POWER7, há bibliotecas de dados múltiplos com instrução única (SIMD). Observe que a precisão e o tratamento de exceções não podem ser idênticos nas funções de MASS e nas funções da biblioteca de sistema. As bibliotecas MASS vêm com os compiladores IBM XL C/C++ e XL Fortran e estão também disponíveis no site de MASS para usuários de outros compiladores (como gcc) para o hardware de destino.
As bibliotecas podem ser chamadas a partir de programas de origem C, C++ ou Fortran. Os compiladores IBM XL C/C++ e IBM XL Fortran são capazes de reconhecer oportunidades de usar MASS para acelerar o programa de origem e invocá-lo automaticamente sem a necessidade de alterações no programa de origem. Este artigo mostra como implementar uma técnica que pode ajudar a sua organização a aproveitar esse poderoso recurso.
Quais programas podem se beneficiar?
Qualquer programa C, C++ ou Fortran que tenha chamadas a funções de bibliotecas matemáticas (como exp, log, sin, cos, etc.) em um loop pode se beneficiar das técnicas descritas neste artigo.
A autovetorização é um processo pelo qual os compiladores IBM XL C/C++ ou Fortran reconhecem uma oportunidade de aprimorar o desempenho do programa sendo compilado substituindo uma chamada a uma função de biblioteca padrão (C/C++ libm ou Fortran intrinsic) em um loop com uma chamada para a função de vetor MASS correspondente. Como as funções de vetor MASS são muito mais rápidas (por um fator de até aproximadamente 30) do que uma chamada repetida a uma função de biblioteca padrão, o aumento no desempenho resultante pode ser substancial.
Um exemplo simples é um loop que calcula uma determinada função para vários argumentos, como o seguinte programa 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
|
Com as opções de compilador apropriadas, o compilador reconhecerá a oportunidade de acelerar o programa substituindo a chamada repetida a exp() por uma única chamada à função de vetor de MASS correspondente vexp(), resultando em um programa que se comporta como se fosse originalmente escrito assim:
include 'massv.include'
subroutine sub (y, x, n)
real*8 y(*), x(*)
integer n
call vexp (y, x, n) ! MASS vector function
end subroutine
|
Esse é apenas um exemplo simples, mostrando a ideia básica que está por trás da autovetorização. Os compiladores XL são na verdade capazes de reconhecer oportunidades muito mais complicadas e também tentarão reorganizar as instruções no programa de origem se necessário para que sejam criadas oportunidades de autovetorização.
Um exemplo mais complicado e realista é examinado na seção de estudo de caso deste artigo.
Opções de compilador para autovetorização
É possível compilar programas com qualquer um dos seguintes conjuntos de opções:
- -qhot -qnostrict (para Fortran)
- -qhot -qnostrict -qignerrno (para C/C++)
- -qhot -O3
- -O4
- -O5
Quando se compila um programa usando um desses conjuntos de opções, o compilador automaticamente tenta vetorizar as chamadas às funções matemáticas do sistema chamando as funções de vetor de MASS equivalentes (exceto para chamadas às seguintes funções, que são exceções: vatan2, vsatan2, vdnint, vdint, vcosisin, vscosisin, vqdrt, vsqdrt, vrqdrt, vsrqdrt, vpopcnt4, vpopcnt8, vexp2, vexp2m1, vsexp2, vsexp2m1, vlog2, vlog21p, vslog2 e vslog21p). Se o compilador não conseguir vetorizar o programa, ele automaticamente tentará chamar as funções escalares MASS equivalentes. Para escalação ou vetorização automática, o compilador usa versões das funções de MASS contidas na biblioteca do compilador libxlopt.a. Não é necessário adicionar nenhuma chamada especial às funções de MASS no seu código ou criar um link para a biblioteca xplot.
Além de qualquer um dos conjuntos de opções anteriores, quando a opção -qipa estiver ativa, se o compilador não puder vetorizar, ele tentará processar sequencialmente as funções escalares de MASS antes de decidir chamá-las.
Se quiser desativar a autovetorização, adicione a opção -qhot=novector.
A seguir, temos um estudo de caso de um aplicativo real — uma transformação Fourier discreta (DFT) — mostrando as melhorias no desempenho resultantes da compilação com diversas opções de compilador. O aplicativo é simples o suficiente para ser claramente ilustrativo, e ainda assim complexo o bastante para oferecer oportunidades de otimização não triviais.
A sincronização para ambos os programas foi feita com o programa de driver fornecido no Apêndice 3, em um computador POWER6 em execução a 4,704 GHz.
O Apêndice 1 mostra o programa de origem Fortran DFT. Ele contém um loop aninhado que chama exp(), cos() e sin(), seguido por um loop que chama sin() e sqrt(). O programa foi compilado com -O3 (que não faz a autovetorização) e também com -O4 (que faz a autovetorização). Os resultados são mostrados na Figura 1.
Observe que o benefício da autovetorização cresce à medida que o tamanho do problema aumenta, alcançando um fator de melhoria de 8,94x em um tamanho de problema igual a 2000.
Figura 1: Comparação do desempenho de DFT Fortran com as opções de compilação -O3 e -O4, para vários tamanhos de problema.
O Apêndice 2 mostra uma versão C do programa de Fortran DFT no Apêndice 1 (ele contém uma rotina consume() simulada, de forma que a análise interprocessual do compilador [IPA] não consegue ver que o resultado do cálculo não é realmente usado nesse exemplo ilustrativo e, dessa forma, ele otimizará todo o programa).
O programa foi compilado com -O3 (que não oferece a autovetorização), com -O4 (que oferece a autovetorização) e com -O5 (que oferece a autovetorização e também IPA). Os resultados são mostrados na Figura 2.
Como ilustrado no exemplo de Fortran, o benefício da autovetorização cresce à medida que o tamanho do problema cresce, alcançando um fator de 6,00x a n=2000. Além disso, a ativação de IPA a -O5 oferece um aumento de velocidade adicional de 1,22x, visto que pode determinar que as entradas e as saídas não tenham alias (ou seja, não se sobreponham na memória), permitindo a vetorização da chamada para atan2 na conversão para coordenadas polares. O aumento da velocidade de -O5 em relação a -O3 é 7,33x a n=2000.
Figura 2: Comparação do desempenho de DFT C com as opções de compilação -O3, -O4 e O5, para vários tamanhos de problema.
Este artigo ofereceu uma descrição das bibliotecas IBM MASS e dos recursos de autovetorização dos compiladores IBM XL C/C++ e XL Fortran. Além disso, o artigo mostrou o uso de várias opções de compilador em um programa de exemplo (transformação de Fourier discreta), mostrando como acelerações de até 8,94 vezes em relação às velocidades anteriores podem ser obtidas com o uso dos compiladores por meio de chamada automática de MASS pela autovetorização.
Essa demonstração tem o objetivo de incentivar os usuários com programas que chamam funções matemáticas a fazer experiências com as opções de compilador disponíveis, beneficiando-se assim com os aumentos de velocidade resultantes da autovetorização do compilador IBM XL C/C++ ou XL Fortran.
Apêndice 1 – Programa de origem 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_re(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
|
Apêndice 2 – Programa de origem C DFT
#include <math.h>
#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<n;++i) {
y_re[i]=y_im[i]=0;
}
for(k=0;k<n;++k)
{
// compute y(k), k-th DFT output
for(i=0;i<n;++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<n;++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<n;++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[])
{
}
|
Apêndice 3 – Programa de driver
Aqui, apresentamos o programa de driver Fortran que foi usado para sincronizar os códigos DFT.
program main
interface
subroutine dft(x,a,phi,n)
real*8 x(n),a(n),phi(n)
integer n
end subroutine
subroutine init(a,n)
real*8 a(n)
integer n
end subroutine
subroutine consume(a,b,c)
real*8 a(*),b(*),c(*)
end subroutine
end interface
! Parameters:
! nmax is the problem size.
! nrep is the number of repetitions of the
! problem. This should be chosen so that
! the elapsed time is long enough to give
! sufficient timing resolution.
! cyc is the clock frequency in Hz for the
! processor that the program is to be run on.
! (Can be found from AIX command pmcycles.)
integer, parameter :: nmax=1000
integer, parameter :: nrep=100
real*8, parameter :: cyc=4704000000.d0
real*8 x(nmax), a(nmax), phi(nmax)
real*8 tx, ty, accum, del(4)
intrinsic sin, sqrt
real*8 rtc
del(4)=0.d0
acc = 0.d0
do k=1,nrep
tx=rtc()
call init(x,nmax)
call dft(x,a,phi,nmax)
ty=rtc()
call consume(x,a,phi)
do j=1,nmax
acc = acc + a(j) + phi(j)
end do
del(4) = del(4) + (ty-tx)
end do
del(1) = del(4)/real(nmax,8)
del(2) = del(1)/real(nrep,8)
del(3) = cyc*del(2)
print *,'acc=',acc/real(nrep,8),' n=',nmax,
& ' r=',nrep, ' a=',del(1),' b=',del(2),
& ' c=',del(3),' w=',del(4)
end program
|
O desempenho exato obtido pode variar dependendo do modelo de processador usado e de sua configuração, bem como da versão dos compiladores usados. Por isso, é possível que você tenha uma experiência de desempenho diferente da obtida nos experimentos descritos aqui.
Aprender
- IBM
Mathematical Acceleration Subsystem: Fornece informações sobre conteúdo, uso,
desempenho e precisão para as bibliotecas MASS.
- Guia do usuário do IBM XL C/C++ para AIX: Fornece mais detalhes sobre sinalizadores
de compilador.
- Guia de otimização e programação do IBM XL C/C++ para AIX: Fornece mais detalhes
sobre as bibliotecas de alto desempenho.
- Guia do usuário do IBM XL Fortran para AIX: Fornece mais detalhes sobre sinalizadores
de compilador.
- Guia de otimização e programação do IBM XL Fortran para AIX: Fornece mais detalhes
sobre as bibliotecas de alto desempenho.
- Guia de introdução e ajuste
do processador POWER4: Este Redbook tem mais detalhes sobre sinalizadores de
otimização de compilador.
- AIX e
UNIX: Visite a seção AIX e UNIX do developerWorks para expandir suas qualificações de
UNIX.
- Novo no AIX e UNIX: Visite a página Novo no AIX e UNIX para saber mais sobre AIX e
UNIX.
- Eventos técnicos e webcasts
do developerWorks: Mantenha-se atualizado com os webcasts e eventos técnicos do
developerWorks.
- Wiki AIX 5L:
Um ambiente de colaboração para informações técnicas relacionadas a AIX.
- Podcasts: Sintonize e conheça as novidades de especialistas técnicos da
IBM.
Obter produtos e tecnologias
- Software de teste IBM: Elabore seu
próximo projeto de desenvolvimento com o software disponível para download diretamente do
developerWorks.
Discutir
- Participe dos fóruns sobre AIX e UNIX:
- AIX 5L — técnico
- Fórum de AIX para Desenvolvedores
- Gerenciamento de Sistemas de Cluster
- Assistente de Suporte IBM
- Ferramentas de Desempenho — técnico
- Virtualização — técnico
- Mais fóruns de AIX e UNIX
- Participe dos blogs developerWorks e envolva-se na
comunidade do developerWorks.
- Participe do C/C++ Café, que
inclui o Blog
de Computação Científica com C/C++
Robert Enenkel trabalha no Optimizing Compiler Group no IBM Toronto Laboratory; antes, foi pesquisador no IBM Centre for Advanced Studies (CAS). Enenkel é bacharel, mestre e doutor pela University of Toronto, com tese na área de métodos numéricos paralelos para equações diferenciais. Ele atualmente realiza pesquisas e desenvolvimentos em computação numérica, no que ela está relacionada a compiladores e sistemas operacionais, incluindo aritmética de ponto flutuante, bibliotecas de funções matemáticas e o ajuste de desempenho de algoritmos. Dr. Enenkel recebeu da IBM vários prêmios por inventos e de reconhecimento como autor. Mais informações podem ser encontradas nesta página: https://www-927.ibm.com/ibm/cas/toronto/people/members/renekel.shtml.
Daniel Zabawa trabalha no Optimizing Compiler Group no IBM Toronto Laboratory. Ele é mestre e doutor em ciência da computação pela University of Toronto. Zabawa atualmente realiza pesquisas e desenvolvimento em planejamento e otimização de loop, algoritmos de ponto flutuante para arquiteturas superescalares e ajuste de desempenho de funções matemáticas de ponto flutuante.