... para pessoas que não sabem c++ Alexandre Suaide aula 2 O lego Organização das aulas • Aula 1 – Comandos (realmente) básicos do ROOT – Um pouco de c++ para usuários de ROOT – Criando objetos simples (histogramas, gráficos, etc) – Manuseando gráficos e histogramas. • A interface gráfica – Funções e ajustes de gráficos • Aula 2 – Macros – Inovando sem perder a classe. – Análise de dados no Pelletron • ScanRoot e PelTools – Debug, memory leaks e administrando objetos Macros no ROOT • O que é um macro? – Conjunto de comandos (como um programa) gravados em um arquivo. – Em geral é interpretado, mas pode-se compilar • O processo de compilação exige que o macro esteja consistente com o c++ standard • Como ler e executar – .L – carrega um macro na memória – .x – carrega e executa a função do macro cujo nome seja o mesmo do macro • Ex: .x teste.C – Carrega o macro teste.C e executa a função teste() Exemplos simples • Para executar – Método 1 root.exe [0] .L hello.C root.exe [1] hello() – Método 2 hello.C void hello() { cout <<"Hello world"<<endl; } root.exe [0] .x hello.C • Passando parâmetros – Método 1 root.exe [0] .L hist.C root.exe [1] hist(20,3) – Método 2 root.exe [0] .x hist.C(20,3) hist.C void hist(float mean, float RMS) { TH1F *h = new TH1F("h","teste",50,mean-4*RMS, mean+4*RMS); for(int i = 0;i<2000;i++) h->Fill(gRandom->Gaus(mean,RMS)); h->Draw(); } Compilando macros... Tornando a execução mais rápida • Compilar macros torna a execução 10-1000 vezes mais rápida • Para compilar um macro, digite, no prompt do linux compileMacro macro.C – Esse comando só está disponível no PelTools é uma variável • O macro compilado gera uma gSystem biblioteca de ambiente do ROOT compartilhada (.so) (classe TSystem) – Para carregar a biblioteca digite, no ROOT root.exe [0] gSystem->Load(“macro.so”) • Ex: macro hist.C compileMacro hist.C root root.exe [0] gSystem->Load(“hist.so”) root.exe [1] hist(20,3) Alguns cuidados na hora de compilar macros • O processo de compilação utiliza um compilador padrão c++ – Cuidado com a sintaxe. Em geral, o ROOT é muito tolerante com a sintaxe em c++. Macros interpretados rodam sem problemas mas na hora de compilar a estória é outra • O compilador não sabe sobre as definições do ROOT – Deve-se incluir explicitamente as definições de classes do ROOT (#include) • O macro hist.C ficaria assim: #include “TRandom.h” #include “TH1.h” void hist(float mean, float RMS) { TH1F *h = new TH1F("h","teste",50,mean-4*RMS, mean+4*RMS); for(int i = 0;i<2000;i++) h->Fill(gRandom->Gaus(mean,RMS)); h->Draw(); } Criando sem perder a classe • O ROOT oferece a possibilidade de criar as suas próprias classes – Utilize o mesmo padrão de programação em c++ • Porém o ROOT oferece algumas vantagens – Integração completa com o framework do ROOT • Criação de dicionários para utilizar o prompt de comando, incluindo a tecla TAB para completar comandos • Gerenciamento de IO. – Pode-se gravar objetos de classes criadas pelo usuário em arquivos root – Atualização de versões. » O ROOT gerencia automaticamente a evolução das classes que são criadas. – Para usar essas benfeitorias deve-se seguir algumas regras Regras para criação de classes (necessárias somente se você quiser integração total com o ROOT) • Classes devem ser derivadas do TObject ou TNamed (ou de outras classes derivadas delas) – Isso inclui automaticamente métodos de IO, como Write(), Get(), etc... • Utilizar os macros ClassDef e ClassImp na definição da classe – Esses macros são essenciais na geração do dicionário e também no gerenciamento de versões • O dicionário faz com que possa-se utilizar o prompt de comandos para manusear objetos definidos a partir de novas classes • O gerenciamento de versões faz com que possa-se ler objetos de arquivos root criados a partir de definições antigas de novas classes. – Ex: cria-se uma classe para cuidar de um telescópio E-DE. Faz-se algumas análises e grava-se alguns objetos em um arquivo. Após um tempo, muda-se a estrutura dessa classe para torná-la melhor. O gerenciamento de versões faz com que consiga-se ler os objetos definidos com a versão antiga da classe. Exemplo TTeste.h #include "TObject.h" class TTeste: public TObject { public: TTeste(); TTeste.cxx virtual ~TTeste(); ClassDef(TTeste,1) }; #include "TTeste.h" #include <iostream> using namespace std; ClassImp(TTeste) Versão da classe TTeste::TTeste() { cout <<"Esse é o construtor"<<endl; } TTeste::~TTeste() { cout <<"Esse é o destrutor"<<endl; } Compilando classes • Classes podem ser lidas do mesmo jeito que macros, porém compilar é muito mais eficiente • Compilar classes no ROOT é algo que exige uns 3-4 comandos no prompt do Linux – Compilar os arquivos propriamente ditos – Gerar o dicionário com o comando rootcint – Linkar o dicionário compilado com os outros arquivos compilados e gerar uma biblioteca compartilhada (.so) • Assim, para facilitar a vida, existe o comando compile (somente no PelTools) – Macro que compila todos os .cxx em um diretório, gera os dicionários, compila tudo e cria um arquivo .so Algumas regras para o comando compile • Todos os arquivos devem estar em um único diretório • Os headers devem ter extensão .h e os códigos, .cxx • Cada classe deve ser definida em um arquivo .h cujo nome deve ser o mesmo da classe (facilita geração do dicionário) – Ex: a classe TTeste deve ser definida no arquivo TTeste.h • Uso: – cd para o diretório onde estão as classes – Digite compile [nome do arquivo .so] ScanRoot e PelTools • ScanRoot – Versão modificada do ROOT que inclui bibliotecas e métodos para análise dos dados tomados no Pelletron • Agrupa as funções do SCAN + DAMM • Abre e lê arquivo de dados brutos (.FIL) • Preenche histogramas a partir dos dados, etc • PelTools – Classe definida com algumas funções básicas para análise de dados no Pelletron, como traçar bananas, projeções, ajustes de picos etc. • Setup – Inclua no seu arquivo de login • source /mnt/software/setup Idéia por trás do ScanRoot • Os dados são adquiridos no Pelletron e gravados em um formato especial (.FIL) – Bastante compacto – Informação de cada evento separadamente • Os dados devem ser processados para gerar os histogramas ou qualquer outra figura – Aqui entra o ScanRoot • ScanRoot em 4 etapas – Abrir um arquivo de dados (.FIL) – Abrir uma biblioteca com as funções que processarão os eventos – Processar os eventos – Gravar os resultados ScanRoot • Iniciando o programa. – Digite: scanroot – Para um help, digite: scanroot -h ******************************************* ** ** ** S c a n R o o t v 2.0 ** ** ** ** (c) 2003-2004 A. A. P. Suaide ** ** ** ******************************************* Running ScanRoot. type scanroot -help for options type menu() to open the ScanRoot Menu ScanRoot [0]> ******************************************* ** ** ** S c a n R o o t v 2.0 ** ** ** ** (c) 2003-2004 A. A. P. Suaide ** ** ** ******************************************* usage: spmroot [-h|help] [-d|debug] [-t|tools] [file1] [file2] ... -h or -help displays this message -d or -debug turns on debug mode and display event by event information -n or -nogui does not open the ScanRoot Menu file1, file2,... open root files and display the browser A interface gráfica • Como processar um arquivo .FIL – Clique em Open .FIL e selecione o arquivo – Clique em Load Histograms e selecione a biblioteca com as definições dos histogramas – Clique em GO – Clique em Save Histograms para salvar os histogramas gerados – Para abrir a janela de análise, clique em PelTools Menu Além da interface gráfica, há comandos para o prompt • Comandos básicos – hac(“filename”) • Carrega arquivo de definição de histogramas – openInput(“filename”) • Abre o .FIL – openOutput(“filename”,outNumber) • Abre um novo arquivo FIL para gravação – loadL2(“filename”) • Carrega definição de trigger de software – saveHist(“filename”) • Grava arquivo de histogramas – go(N) • Processa N eventos (N=0 processa o arquivo inteiro) – tools() • Abre a janela de PelTools – help() Analisando dados • Usando o prompt de comando (RootCint) – Alta flexibilidade • Interpretador c++/ROOT • Usando o PelTools – Pequena interface gráfica que auxilia, dentre outras coisas • Criação de bananas (TCutG) • Projeção de histogramas • Ajustes de picos, etc – Ajuste bastante rudimentar (precisa desenvolvimento) Como fazer histogramas • Pequena rotina em c++ – Todo o poder do c++ e do ROOT disponíveis • Não precisa compilar – O ScanRoot compila sozinho • Mesmo programa pode ser usado para aquisição de dados (SPMRoot) • Header – Incluir bibliotecas básicas – Definir variáveis globais • 4 funções (2 obrigatórias) – bookHistograms() – fillHistograms() – init() – finish() Mantendo a memória em ordem • Objetos criados no heap (comando new) só são deletados quando explicitamente requisitados – Isso gera um problema de gerenciamento de memória • Considere o seguinte exemplo void hist() { TH1F *h = new TH1F("h","teste",50,0,10); } root.exe [0] for(int i=0;i<10;i++) hist(); Vários objetos são criados com o mesmo nome, além disso, os ponteiros são perdidos. Perdeu-se o acesso àquele objeto mas a memória continua alocada MEMORY LEAK Algumas ferramentas no auxílio de gerenciamento • O ROOT possui alguma classes para ajudar no gerenciamento do sistema como um todo – TROOT • Ponto de entrada do ROOT. Permite acesso a cada objeto criado dentro do ROOT, além de outras informações do sistema (variável global gROOT) – TSystem • Define a interface básica com o sistema operacional (variável global gSystem) – TMemStat • Auxilia na monitoração do uso de memória – TBenchmark • Auxilia na medida de tempo (total e CPU) de processamento de um certo processo Procurando objetos na memória (gROOT) • O ROOT mantém uma lista de objetos criados na memória. Essa lista é indexada pelo nome do objeto. – TROOT::FindObject(char* name); • Retorna o ponteiro para o objeto cujo nome é “name” • Resolve somente casos onde o endereço (ponteiro) do objeto foi perdido. Objetos criados com o mesmo nome são perdidos, a menos que se tome o cuidado de guardar os ponteiros dos mesmos root.exe [0] TH1F *h = gROOT->FindObject(“h”); – TROOT::ls(); • Lista o conteúdo da memória do ROOT root.exe [0] gROOT->ls(); TROOT* OBJ: TH1F Rint h The ROOT of EVERYTHING teste : 0 at: 0x8d4ca20 TMemStat • TMemStat fornece informação sobre o uso de memória no ROOT – TMemStat::PrintMem(“”); • Fornece a quantidade de memória sendo usada e quanto essa memória cresceu/diminuiu desde a última solicitação root.exe [60] TMemStat m root.exe [61] m.PrintMem("") TMemStat:: total = 41.175781 heap = 15.332096 ( +0.102976) root.exe [62] for (int i=0;i<10000;i++) hist() Warning in <TH1::Build>: Replacing existing histogram: h (Potential memory leak). Warning in <TH1::Build>: Replacing existing histogram: h (Potential memory leak). ... Warning in <TH1::Build>: Replacing existing histogram: h (Potential memory leak). root.exe [64] m.PrintMem("") TMemStat:: total = 51.562500 heap = 26.420584 (+10.080040) Essa mensagem aparece porque tenta-se criar vários objetos com o mesmo TBenchmark • TBenchmark é um relógio para medir o desempenho de execução do código – Vários métodos • Start(“”) – Inicia relógio • Stop(“”) – Para relógio • GetRealTime(“”) – Fornece tempo real de execução • GetCpuTime(“”) – Fornece tempo de cpu root.exe [67] TBenchmark b root.exe [68] b.Start(""); for(int i=0;i<1000;i++) hist(); b.Stop(""); Warning in <TH1::Build>: Replacing existing histogram: h (Potential memory leak). ... Warning in <TH1::Build>: Replacing existing histogram: h (Potential memory leak). root.exe [69] b.GetRealTime("") (Float_t)1.09000003337860107e+00 root.exe [70] b.GetCpuTime("") (Float_t)4.69999998807907104e-01 Como resolver o nosso problema de memory leak • Duas situações diferentes – Eu só quero 1 histograma por vez na memória • Tenho que destruir o velho antes de criar o novo • Nesse caso, costuma-se dizer que o objeto pertence à função pois a função decide se o objeto continua vivendo ou não – Eu realmente necessito de vários histogramas na memória • Ou eu ponho nomes diferentes para cada histograma... • ...ou eu mantenho os ponteiros de cada histograma construído com o mesmo nome • Nesse caso, costuma-se dizer que o objeto não pertence à função pois ela não controla a vida do mesmo Caso 1: Eu só quero 1 histograma por vez • Usar o ROOT para verificar se o objeto já existe na memória e deletá-lo, caso necessário void hist() { TH1F *h = gROOT->FindObject("h"); if (h) delete h; h = new TH1F("h","teste",50,0,10); } Esse procedimento é lento pois, a cada chamada da função, a mesma precisa procurar pelo objeto. Porém, é seguro. root.exe [1] TMemStat m; root.exe [2] m.PrintMem("") TMemStat:: total = 35.445312 heap = 10.857408 (+10.857408) root.exe [3] for(int i =0;i<10000;i++) hist() root.exe [4] m.PrintMem("") TMemStat:: total = 35.445312 heap = 10.857464 ( +0.000056) Não foi alocada memória adicional Caso 2: o usuário controla o número de histogramas • Vamos fazer direito – Cada objeto possui um nome distinto – A função retorna um ponteiro do objeto criado TH1F* hist(int index) { TString nome = "hist"; nome+=index; // cria um histograma cujo nome é histxxxx TH1F *h = new TH1F(nome,nome,50,0,10); return h; // retorna o ponteiro do objeto recem criado } root.exe [1] TH1F *hist[10000] root.exe [2] TMemStat m; root.exe [3] m.PrintMem("") TMemStat:: total = 35.144531 heap = 10.863632 (+10.863632) root.exe [4] for(int i =0;i<10000;i++) hist[i] = hist(i) root.exe [5] m.PrintMem("") TMemStat:: total = 46.007812 heap = 22.093968 (+11.230336) root.exe [6] for(int i =0;i<10000;i++) delete hist[i] root.exe [7] m.PrintMem("") TMemStat:: total = 46.007812 heap = 10.920744 (-11.173224) Houve aumento da memória utilizada... ...mas o usuário tem controle sobre ela Quando gerenciamento de memória é importante • Do ponto de vista do programador – Sempre • Do ponto de vista do cientista – Quando não fazer gerenciamento causar problema Na prática, deve-se tomar cuidado com a memória. Quando um trabalho for feito de tal forma que cria-se algumas centenas ou milhares de objetos, dependendo do tamanho de cada um, pode-se, facilmente, alocar praticamente toda a memória disponível, o que pode acarretar no término do programa por falta de memória ou na completa degradação da performance devido ao fato do computador começar a fazer swap em disco. Programinhas onde somente são criados alguns objetos, apesar de não ser elegante, pode-se viver com um pequeno vazamento de memória. Como conseguir mais informação • Site do ROOT – http://root.cern.ch • Documentação das classes – http://root.cern.ch/root/Reference.html • Alguns documentos interessantes (root, c++) – Incluindo essas aulas – http://dfn.if.usp.br/~suaide/pelletron/links.htm • Download ROOT (+scanroot e PelTools) – http://dfn.if.usp.br/~suaide/pelletron/download.htm ... Para finalizar