125 lines
3.8 KiB
Python
Executable File
125 lines
3.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Benchmark GoAWK against other AWK versions
|
|
|
|
from __future__ import print_function
|
|
|
|
import glob
|
|
import os.path
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
AWKS = [
|
|
'./goawk',
|
|
'./orig', # GoAWK without perf improvements
|
|
'original-awk',
|
|
'gawk',
|
|
'mawk',
|
|
]
|
|
NORM_INDEX = AWKS.index('original-awk')
|
|
TESTS_TO_MEAN = None # By default, calculate the mean of all tests
|
|
if False:
|
|
# Only get the mean of these tests because these are the only ones
|
|
# we show in the GoAWK article.
|
|
TESTS_TO_MEAN = [
|
|
'tt.01_print',
|
|
'tt.02_print_NR_NF',
|
|
'tt.02a_print_length',
|
|
'tt.03_sum_length',
|
|
'tt.03a_sum_field',
|
|
'tt.04_printf_fields',
|
|
'tt.05_concat_fields',
|
|
'tt.06_count_lengths',
|
|
'tt.07_even_fields',
|
|
'tt.big_complex_program',
|
|
'tt.x1_mandelbrot',
|
|
'tt.x2_sum_loop',
|
|
]
|
|
NUM_RUNS = 6
|
|
MIN_TIME = 0.5
|
|
PROGRAM_GLOB = 'testdata/tt.*'
|
|
|
|
if len(sys.argv) > 1:
|
|
PROGRAM_GLOB = 'testdata/' + sys.argv[1]
|
|
|
|
|
|
def repeat_file(input_file, repeated_file, n):
|
|
with open(input_file, 'rb') as fin, open(repeated_file, 'wb') as fout:
|
|
for i in range(n):
|
|
fin.seek(0)
|
|
shutil.copyfileobj(fin, fout)
|
|
|
|
|
|
print('Test ', end='')
|
|
for awk in AWKS:
|
|
display_awk = os.path.basename(awk)
|
|
display_awk = display_awk.replace('original-awk', 'awk')
|
|
print('| {:>8} '.format(display_awk), end='')
|
|
print()
|
|
print('-'*28 + ' | --------'*len(AWKS))
|
|
|
|
repeats_created = []
|
|
products = [1] * len(AWKS)
|
|
num_products = 0
|
|
programs = sorted(glob.glob(PROGRAM_GLOB))
|
|
for program in programs:
|
|
# First do a test run with GoAWK to see roughly how long it takes
|
|
cmdline = '{} -f {} testdata/foo.td >tt.out'.format(AWKS[0], program)
|
|
start = time.time()
|
|
status = subprocess.call(cmdline, shell=True)
|
|
elapsed = time.time() - start
|
|
|
|
# If test run took less than MIN_TIME seconds, scale/repeat input
|
|
# file accordingly
|
|
input_file = 'testdata/foo.td'
|
|
if elapsed < MIN_TIME:
|
|
multiplier = int(round(MIN_TIME / elapsed))
|
|
repeated_file = '{}.{}'.format(input_file, multiplier)
|
|
if not os.path.exists(repeated_file):
|
|
repeat_file(input_file, repeated_file, multiplier)
|
|
repeats_created.append(repeated_file)
|
|
input_file = repeated_file
|
|
|
|
# Record time taken to run this test, running each NUM_RUMS times
|
|
# and taking the minimum elapsed time
|
|
awk_times = []
|
|
for awk in AWKS:
|
|
cmdline = '{} -f {} {} >tt.out'.format(awk, program, input_file)
|
|
times = []
|
|
for i in range(NUM_RUNS):
|
|
start = time.time()
|
|
status = subprocess.call(cmdline, shell=True)
|
|
elapsed = time.time() - start
|
|
times.append(elapsed)
|
|
if status != 0:
|
|
print('ERROR status {} from cmd: {}'.format(status, cmdline), file=sys.stderr)
|
|
min_time = min(sorted(times)[1:])
|
|
awk_times.append(min_time)
|
|
|
|
# Normalize to One True AWK time = 1.0
|
|
norm_time = awk_times[NORM_INDEX]
|
|
speeds = [norm_time/t for t in awk_times]
|
|
test_name = program.split('/')[1]
|
|
if TESTS_TO_MEAN is None or test_name in TESTS_TO_MEAN:
|
|
num_products += 1
|
|
for i in range(len(AWKS)):
|
|
products[i] *= speeds[i]
|
|
|
|
display_name = test_name.split('_')[0] + ' (' + ' '.join(test_name.split('_')[1:]) + ')'
|
|
print('{:28}'.format(display_name), end='')
|
|
for i, awk in enumerate(AWKS):
|
|
print(' | {:8.2f}'.format(speeds[i]), end='')
|
|
print()
|
|
|
|
print('-'*28 + ' | --------'*len(AWKS))
|
|
print('**Geo mean** ', end='')
|
|
for i, awk in enumerate(AWKS):
|
|
print(' | **{:.2f}**'.format(products[i] ** (1.0/num_products)), end='')
|
|
print()
|
|
|
|
# Delete temporary files created
|
|
os.remove('tt.out')
|
|
for repeated_file in repeats_created:
|
|
os.remove(repeated_file)
|