sexta-feira, 22 de fevereiro de 2013

Se eu soubesse antes...

Quando eu peguei meu projeto de pesquisa, imaginei que seria tranquilo. O maior trabalho seria criar a ferramenta para as análises, que é a modificação de um programa já existente em Fortran 90.

Se eu tivesse me mantido nas ferramentas que eu conhecia na época, talvez tivesse sido mesmo tranquilo. Até entrar no mestrado eu usava o editor de VBA pra Excel, depois eu comecei a usar o Visual Studio. Daí no meio do mestrado eu troquei o sistema operacional pra GNU/Linux e não me preocupei com IDE. E o que eu tinha feito antes era tão simples que eu também não me preocupei com debuggers.

Até lá, eu vivia no mundo mágico onde tudo funcionava, mesmo que eu não soubesse exatamente como. Não que eu não ficasse intrigado, mas eu não tive maiores problemas por não conhecer profundamente o que eu estava fazendo.

Então começaram os trabalhos. Enquanto eu ficava com o notepad++ (windows) ou o gedit (ubuntu) mais uma tela de terminal aberta, meus colegas rodavam o Visual Compaq em máquinas virtuais rodando Windows XP. Enquanto eu usava gFortran, eles usavam o iFort (é da Intel, não da Apple).

E daí eu tive umas dificuldades a mais, que aos poucos fui entendendo como solucionava.

1) A primeira coisa que eu deveria saber, na verdade eu já sabia, só não apliquei direito. Comecei a fazer as alterações criando módulos novos e testando. Só que chegou uma hora que eu parei de testar os módulos porque não conseguia mais entender como modularizar os testes das funções. Esse foi o primeiro erro. Com um pouco de conversa com os mais experientes eu provavelmente teria conseguido fazer os testes, e não programar várias funções diferentes sendo chamadas umas nas outras sem saber onde estavam os erros.

2) Flags de compilação. Isso é básico, muito básico. O que foi mais útil pra mim foram as flags de avisos, que me ajudaram a rastrear não só os erros, como o que estava mal escrito. Sem esses avisos, o compilador apontava pra linhas que eu não conseguia entender por que havia um erro. Depois de ativar os avisos veio a resposta: porque o erro não estava ali. Então, comecei compilando com:
$ gfortran programa.f90 -o programa

E agora eu faço usando scripts como esse:
$ cat c.sh
str=$1
len=${#str}
gfortran -g -fbounds-check -fmax-errors=3 -Wuninitialized -ffpe-trap=overflow -fbacktrace -ffree-line-length-0 -Wall -Wtabs $1 -o ${str:0:(len-4)}
$ ./c.sh programa.f03
Para o .bat, eu lembro que a referência é dada por %1 e não $1, mas nunca lembro como manipula strings.

Nota que tem um ponto importante que foi a fonte dos meus pepinos dos últimos meses, que o -fbounds-check evita. O seguinte código não vai dar erro sem o -fbounds-check. Por sinal, por algum motivo não consegui fazer dar erro agora pelo vetor a não ter sido inicializado, tenho que dar uma olhada nisso com mais calma.
program errado
implicit none
integer,dimension(:),allocatable :: a
integer :: b
allocate(a(10))
b = a(11)
endprogram errado


3) Debugger. Fiz o debug por mais de um ano colocando todos os write(*,*) possíveis e imagináveis no código, até perceber que tinha variáveis demais e eu não estava entendo mais o que acontecia. Meu trabalho teria sido mais rápido e fácil se, 1 ano atrás, eu tivesse aprendido a usar algum debugger. Então, era hora de procurá-lo, antes tarde do que nunca, dizem por aí. Resolvi usar o GDB. O detalhe é que ele, a princípio, tá redondinho pro Fortran 77, mas não necessariamente pros mais recentes. Não sei se o pepino é do GDB ou do gFortran, mas depois de 2 dias aprendendo a usar o GDB eu descobri que não conseguia ver as variáveis dos arrays alocados dinamicamente. Resolvi fazer um programa de teste pra encontrar o problema, e mudei pra encontrar a "solução" (também conhecida como gambiarra). O programa ficou o seguinte:
program kindreal
implicit none

integer,parameter :: kr4=4,kr8=8

real(kr4),dimension(2)   :: a4
real(kr8),dimension(2)    :: a8
real(kr4),dimension(:),allocatable :: b4
real(kr8),dimension(:),allocatable :: b8

real(kr8) :: arg1,arg2

arg1 = 10.d0
arg2 = 9.d200

a8 = (/ arg1,arg2 /)
b8 = (/ arg1,arg2 /)

a4(1) = real(arg1,kr4)
b4(1) = real(arg1,kr4)
a4(2) = huge(a4(2))
b4(2) = huge(b4(2))

write(*,*)'a4',a4(:)
write(*,*)'b4',b4(:)
write(*,*)'a8',a8(:)
write(*,*)'b8',b8(:)

endprogram kindreal

E a saída ficou:
 a4   10.000000      3.40282347E+38
 b4   10.000000      3.40282347E+38
 a8   10.000000000000000       8.99999999999999939E+200
 b8   10.000000000000000       8.99999999999999939E+200

Vendo no GDB, fica da seguinte forma:
(gdb) p a4
$1 = (10, 3.40282347e+38)
(gdb) p b4
$2 = (0)
(gdb) p a8
$3 = (10, 8.9999999999999994e+200)
(gdb) p b8
$4 = (0)
(gdb) p *((real *)b4)
$5 = 10
(gdb) p *((real *)b4)@2
$6 = (10, 3.40282347e+38)
(gdb) p *((real *)b8)@2
$7 = (0, 2.5625)
O que mostra que o problema é como mostrar os arranjos alocáveis em real(8), que eu não descobri como fazer.

Bom, na verdade o erro é não saber o que se está fazendo, mas aos poucos eu vou descobrindo.

PS: O texto "O Retorno (2/2)" está quase escrito. Só não sei se vai ser (2/2) ou (1,5/2), hehe.
PS2: Como eu não sabia colocar código na página (não manjo nada de html), usei isso aqui: http://formatmysourcecode.blogspot.com.br/
PS3: Não podia faltar uma música pra encerrar o texto:

2 comentários:

  1. Dependendo da escala, talvez tu possa usar um makefile (http://mrbook.org/tutorials/make/) ao invés de um script. Mas só faz isso se for te facilitar em vez de complicar.

    Se tu muda o código e acaba quebrando outras coisas que não é o que tu tá testando, uma boa é usar um framework de teste unitário (http://sourceforge.net/projects/fortranxunit/). É fácil de usar, principalmente quando o assunto são funções de matemática.

    vivendo e aprendendo, mestre!

    []'s

    ResponderExcluir
  2. Esse framework de teste eu nunca tinha ouvido falar, mas o Makefile já me falaram várias vezes. Só não coloquei aqui porque ainda não aprendi a usar, hehe. Até o final do ano eu aprendo a usar o Makefile.

    ResponderExcluir