iaperftest - Comparação de performance entre funções v6

A função abaixo realiza teste de performance entre diferentes funções, executando para cada uma delas uma lista de testes.

Como entrada, esta função recebe uma lista (vetor) das funções a serem executadas e um outro vetor com uma lista dos valores a serem testados

Synopse

runTests(listF, listT, n, show_r)

  • listF: lista de funções a serem testadas.
  • listT: lista de testes a serem rodados para cada função.
  • n: número de repetições de cada teste (será retornado o menor tempo)
  • show_result: True/False indicando se deve mostrar coluna de resultados

Código-fonte da função

  1 from numpy import *
  2 #from iaOPF import *
  3 import os
  4 
  5 import time
  6 import pickle
  7 
  8 class PerfTest():
  9     vet_tests         = []
 10     vet_fncs          = []
 11     show_r            = True
 12     running_page      = None
 13     running_namespace = None
 14 
 15     def setup(self, namespace, page):
 16         #Configura o diretorio onde o script esta sendo executado
 17         self.running_namespace = namespace
 18         self.running_page = page
 19 
 20 
 21 
 22     def addTests(self, T, show_results = True):
 23         self.vet_tests = T
 24         self.show_r = show_results
 25 
 26 
 27     def runTests(self, listF, n=1):
 28         vet_Linhas = []
 29 
 30         for fnc in listF:
 31             linha = []
 32             #Nome do Autor
 33             linha.append(fnc[0])
 34             #Nome da funcao
 35             linha.append(fnc[1])
 36 
 37             #Roda com cada um dos parametros
 38             for entrada in self.vet_tests:
 39                 #Tenta executar os testes
 40                 n_results = 0
 41                 vet_posproc = []
 42                 try:
 43                     min_t = inf
 44                     for i in arange(n):
 45                         t0 = time.time()
 46                         result = fnc[2](entrada[1])
 47                         t1 = time.time()
 48                         min_t = min(min_t, t1 - t0)
 49                     #Converte para milisegundos e arredonda com precisao de microsegundos
 50                     min_t = round(min_t*1000, 3)
 51 
 52 
 53                     #Executa as checagens de resultado
 54                     if (self.show_r):
 55                         #Prepara um vetor para cada uma das funcoes de pos-processamento
 56                         if (len(fnc) > 3):
 57                             #Realiza um loop por todos os resultados da FUNCAO, mostrando os resultados
 58                             for posproc in fnc[3]:
 59                                 #Executa o pos_processamento
 60                                 result_pos = posproc(entrada[1],result)
 61                                 #Adiciona o pos processamento ao vetor
 62                                 vet_posproc.append(str(result_pos))
 63                                 #linha.append(str(result_pos))
 64                                 n_results += 1
 65 
 66                     #Adiciona o tempo
 67                     linha.append(str(min_t)+' ms')
 68                     #Adiciona o vetor de Pos-Processamento, mesmo que esteja vazio
 69                     linha.append(vet_posproc)
 70                 except Exception as erro:
 71                     #Indica que houve um erro
 72                     linha.append(erro.__class__.__name__)
 73                     print erro.args
 74                     #Adiciona o vetor de resultados, do jeito que estiver
 75                     linha.append(vet_posproc)
 76 
 77             vet_Linhas.append(linha)
 78 
 79         #Cria um vetor final apenas para mostrar o resultado para o usuario
 80         vet_showUser = self.vetCabecalho()
 81         vet_showUser.extend(vet_Linhas)
 82 
 83         #Tenta gravar uma arquivo pickle com os resultados da execucao
 84         #print "\nGravando: "
 85         #print vet_Linhas
 86         arq_attach = self.get_work_dir(self.running_namespace, self.running_page) + '/testperf.pkl'
 87         #arq_attach = self.get_work_dir(self.running_namespace, 'rod_testes_04') + '/testperf2.pkl'
 88 
 89         print "\ngravando arquivo: " + str(arq_attach) + "\n\n"
 90         if not os.path.isdir(os.path.dirname(arq_attach)):
 91             print "criando diretorio de attachments: " + str(os.path.dirname(arq_attach))
 92             os.makedirs(os.path.dirname(arq_attach))
 93         try:
 94             output = open(arq_attach, 'wb')
 95             pickle.dump(vet_Linhas, output)
 96             output.close()
 97         except:
 98             print "ERRO ao gravar o anexo: " + str(arq_attach)
 99             print "O RESULTADO DE SEUS TESTES NAO FORAM SALVOS"
100 
101 
102         #Prepara o vetor
103         vet_showUser = self.preparaShowResults(vet_showUser)
104         #print vet_
105         #Mostra a linha
106         iatab2rest(vet_showUser)
107 
108         #Retorna o vetor que sera escrito na pagina
109         return vet_Linhas
110 
111 
112     def preparaShowResults(self, vet_linhas):
113         #Pega o numero maximo de ramificacoes do vetor de linhas:
114         max_r = 0
115         for linha in vet_linhas:
116             #Verifica cada item
117             for col in linha:
118                 #Verifica se eh uma lista
119                 if (type(col)==list):
120                     #Atualiza o numero maximo de ramificacoes
121                     max_r = max(len(col), max_r)
122 
123         vLinhas = []
124         #Varre todo o vetor destrinchando as linhas
125         for linha in vet_linhas:
126             nova_linha = []
127             for col in linha:
128                 #Caso seja uma lista, destrincha ate o numero maximo
129                 if (type(col)==list):
130                     i=0
131                     for sub_col in col:
132                         nova_linha.append(sub_col)
133                         i+=1
134                     while (i < max_r):
135                         nova_linha.append('')
136                         i+=1
137                 else:
138                     #Apenas adiciona a linha
139                     nova_linha.append(col)
140             vLinhas.append(nova_linha)
141 
142         return vLinhas
143 
144 
145 
146     def vetCabecalho(self):
147         #Inicializa um vetor com as linhas de cabecalho
148         vet_Linhas = []
149 
150         #---CABECALHO--------------
151         linha = []
152         #--Nome do Autor
153         linha.append('Autor')
154         #--Nome da funcao
155         linha.append('Funcao')
156         #--Coluna para cada um dos parametros de entrada
157         for tst in self.vet_tests:
158             linha.append(tst[0])
159             #Cria um vetor vazio para abrigar os resultados dos testes
160             linha.append([])
161         vet_Linhas.append(linha)
162 
163         return vet_Linhas
164 
165 
166     def showResults(self, listPaginas):
167         #Pega o vetor de linhas ja inicializado com cabecalho
168         vet_Linhas = self.vetCabecalho()
169 
170         #Busca e adiciona as respostas de cada usuario
171         if (type(listPaginas)==list):
172             if (len(listPaginas)>0):
173                 #Tenta abrir o arquivo de resultados de cada pagina
174                 for pag in listPaginas:
175                     arq_path = self.get_work_dir(pag[0],pag[1])+'/testperf.pkl'
176                     try:
177                         input = open(arq_path, 'rb')
178                         vet_results = pickle.load(input)
179                         input.close()
180                         #Acrescenta um LINK no nome do autor apontando para a pagina onde o teste foi executado
181                         vet_results[0][0] = "`" + pag[0] + ':' + pag[1] + ' ' + vet_results[0][0] + "`"
182                         vet_Linhas.extend(vet_results)
183                         #print "\narq_path: " + str(arq_path)
184                     except:
185                         print "\narq-path: " + str(arq_path) + ": ERRO (arquivo nao encontrado ou em formato incorreto)"
186 
187 
188 
189         #Prepara o vetor para ser mostrado
190         vet_Linhas = self.preparaShowResults(vet_Linhas)
191 
192         #Mostra o resultado
193         iatab2rest(vet_Linhas)
194 
195         return vet_Linhas
196 
197 
198     def get_work_dir(self, namespace=None, page=None):
199         """
200         Returns the attachment directory (to be used as a work area)
201         for the 'page' contained in 'namespace'
202         """
203         import os, xs_config as config
204         if namespace is None:
205             namespace, page = os.path.split(os.path.splitext(__file__)[0])[-2:]
206             namespace = os.path.split(namespace)[-1]
207         return os.path.join(config.media_root, config.attachment_media_prefix, namespace, page)

Lista de Funções

A lista de funções é um vetor onde cada item contém um sub-vetor com a função a ser executada e também duas strings informativas indicando o autor e o nome da função (tal qual o autor nomeou).

Este sub-vetor tem a seguinte estrutura:
  • string nome do autor
  • string nome da função
  • lambda PARAMETRO: function(PARAMETRO)
  • vetor de pos-processamento: este vetor contém as diferentes funções que serão executadas em cima do resultado da função sendo testada. O objetivo é possibilitar a extração de diversos parametros que permitam avaliar se o resultado retornado está OK. Por exemplo, caso esteja sendo testada uma função que gera uma matriz é possível montar algumas funções de pos-processamento que retornem SOMA, MÉDIA, Num. de ZEROS, etc.
Na definição de cada função de pos-processamento, duas variáveis estão disponíveis:
  • x: é a variável que serviu de entrada na função testada
  • y: é o valor de retorno da função testada

Por exemplo, para incluir na lista de funções a serem testadas a função "zeros" e a função "ones" do numpy gerando uma matriz quadrada cuja dimensão é entrada que será testada com diferentes valores:

 1 #Inicializa o vetor de funcoes
 2 listF = []
 3 
 4 #Monta as funcoes a serem testadas e algumas funcoes de Pos-Processamento (PP)
 5 posproc_soma = lambda x,y: y.sum()
 6 posproc_max  = lambda x,y: y.max()
 7 posproc_min  = lambda x,y: y.min()
 8 listPP = [posproc_soma, posproc_min, posproc_max]
 9 
10 fnc_1 = ['Ciclano', 'zeros', lambda x: zeros((x,x)), listPP ]
11 fnc_2 = ['Beltrano', 'ones', lambda x: ones((x,x)),  listPP ]
12 fnc_3 = ['Fulano', 'ones_2', lambda x: ones(x,x),    listPP ]
13 
14 #Adiciona a lista de funcoes
15 listF.append(fnc_1)
16 listF.append(fnc_2)
17 listF.append(fnc_3)

Lista de Testes

O vetor de valores de teste possui em cada item um sub-vetor contendo o valor para teste e uma string de descrição:

  • string descritiva do teste (Ex: "matriz 5x5")
  • valor de teste, que será entrada da função sendo testada

Por exemplo, para incluir nos testes a geração de matrizes de tamanho 10, 100 e 1000:

 1 #Inicializa o vetor de testes
 2 listT = []
 3 
 4 #Monta as entradas de teste
 5 teste_1 = [ 'matriz 10x10', 10 ]
 6 teste_2 = [ 'matriz 100x100', 50 ]
 7 teste_3 = [ 'matriz 1000x1000', 1000 ]
 8 
 9 #Adiciona ao vetor
10 listT.append(teste_1)
11 listT.append(teste_2)
12 listT.append(teste_3)

Executando o exemplo

Como saída, a função tem como saída uma tabela no formato ReST.

Para isso, na hora de executá-la é importante configurar a saída de código com a diretiva:

  • output_format: rest

Outro cuidado é aumentar o tempo de timeout (em segundos) da execução dos testes:

  • timeout: 900
1 #Importa o modulo
2 import iaperftest
3 
4 #Roda os testes
5 #aux = iaperftest.runTests(listF, listT, 10, True)
6 objTestes = iaperftest.PerfTest()
7 objTestes.setup(xsPackage, xsModule)
8 objTestes.addTests(listT)
9 vet_result = objTestes.runTests(listF)

('data type not understood',) ('data type not understood',) ('data type not understood',)

gravando arquivo: /home/rubens/www/media/Attachments/iaOPF/iaperftest/testperf.pkl

Autor Funcao matriz 10x10       matriz 100x100       matriz 1000x1000      
Ciclano zeros 0.015 ms 0.0 0.0 0.0 0.006 ms 0.0 0.0 0.0 6.767 ms 0.0 0.0 0.0
Beltrano ones 1.162 ms 100.0 1.0 1.0 0.016 ms 2500.0 1.0 1.0 4.751 ms 1000000.0 1.0 1.0
Fulano ones_2 TypeError       TypeError       TypeError