300 likes | 401 Views
... 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
E N D
... para pessoas que não sabem c++ Alexandre Suaide aula 2
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 hello.C void hello() { cout <<"Hello world"<<endl; } root.exe [0] .L hello.C root.exe [1] hello() • Para executar • Método 1 • Método 2 root.exe [0] .x hello.C • Passando parâmetros • Método 1 • Método 2 root.exe [0] .L hist.C root.exe [1] hist(20,3) 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 • O macro compilado gera uma biblioteca compartilhada (.so) • Para carregar a biblioteca digite, no ROOT gSystem é uma variável de ambiente do ROOT (classe TSystem) 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(); virtual ~TTeste(); ClassDef(TTeste,1) }; TTeste.cxx #include "TTeste.h" #include <iostream> using namespace std; ClassImp(TTeste) TTeste::TTeste() { cout <<"Esse é o construtor"<<endl; } TTeste::~TTeste() { cout <<"Esse é o destrutor"<<endl; } Versão da classe
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 ******************************************* ** ** ** 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]> • Iniciando o programa. • Digite: • Para um help, digite: scanroot scanroot -h ******************************************* ** ** ** 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* Rint The ROOT of EVERYTHING OBJ: TH1F h 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 nome.
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 } Houve aumento da memória utilizada... ...mas o usuário tem controle sobre ela 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)
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