Can you please help me with the following script?
At the moment the script is taking up to 20 mins to execute, depending on the amount of data being processed (each time the script is executed, it processes several thousand lines of data). I would like to make the script more responsive. Can you tell me how I can update the script for better performance?
See comments in each of the main parts of the script:
#!/opt/SP/mdp/home/SCRIPTS/tools/Python-2.6.2/bin/python
import glob
import re as regex
from datetime import datetime
# Here I am getting two set of files which I am going to use to create two separate dictionaries
Cnfiles = glob.glob('/logs/split_logs/Cn*_generic_activity.log')
Prfiles = glob.glob('/logs/split_logs/Pr*_generic_activity.log')
# Output file
log = file('/logs/split_logs/processed_data.log', 'w')
# First dictionary, holds received records
Cn = {}
for logfile in Cnfiles:
with open(logfile) as logfile:
filecontent = logfile.xreadlines()
for line in filecontent:
if 'SERV1' in line and 'RECV' in line or 'SERV2' in line and 'RECV' in line or 'SERV3' in line and 'RECV' in line:
line = regex.sub('<._', '', line)
line = line.replace('<', '')
line = line.replace('>', '')
line = line.replace('.', ' ')
line = line.replace(r'|', ' ')
line = line.strip()
field = line.split(' ')
opco = field[4]
service = field[5]
status = field[6]
jarid = field[10]
Cn.setdefault(opco, {}).setdefault(service, {}).setdefault(status, {})[jarid] = jarid
# Second dictionary, holds the various stages the records go through
Pr = {}
for logfile in Prfiles:
with open(logfile) as logfile:
filecontent = logfile.xreadlines()
for line in filecontent:
if 'status 7 to 13' in line or 'status 9 to 13' in line or 'status 7 to 14' in line or 'status 9 to 14' in line or 'status 5 to 504' in line or 'status 7 to 505' in line:
line = line.replace('<', '')
line = line.replace('>', '')
line = line.replace('.', ' ')
line = line.strip()
field = line.split(' ')
jarid = field[8]
status = field[13]
Pr.setdefault(status, {})[jarid] = jarid
# Up to this point, the script performs quite well, even with big files.
# However, the next step is comparing the two dictionaries and if it finds a record that is in both dicts,
# it is creating new sub-dictionaries to hold the various stages of the records. This is the step
# that is taking the most time! Do you know of a better way I can do this?
for opco in Cn.keys():
for service in Cn[opco].keys():
for status in Cn[opco][service].keys():
for jarid in Cn[opco][service][status].keys():
if jarid in Pr['13'].keys():
Cn[opco][service].setdefault('ACK', {})[jarid] = jarid
elif jarid in Pr['14'].keys():
Cn[opco][service].setdefault('NACK', {})[jarid] = jarid
else:
if jarid in Pr['504'].keys() or jarid in Pr['505'].keys():
Cn[opco][service].setdefault('RETRY', {})[jarid] = jarid
# Once the new sub-dictionaries are created, I am just counting the number of records in
# each one and writing the output to the log.
timestamp = (datetime.now()).strftime('%y%m%d %H:%M')
for opco in sorted(Cn.keys()):
for service in sorted(Cn[opco].keys()):
if 'RECV' in Cn[opco][service].keys():
recvcount = len(Cn[opco][service]['RECV'])
else:
recvcount = ''
if 'NACK' in Cn[opco][service].keys():
nackcount = len(Cn[opco][service]['NACK'])
else:
nackcount = ''
if 'ACK' in Cn[opco][service].keys():
ackcount = len(Cn[opco][service]['ACK'])
else:
ackcount = ''
if 'RETRY' in Cn[opco][service].keys():
retrycount = len(Cn[opco][service]['RETRY'])
else:
retrycount = ''
log.write('%s\t%s\t%s\t%s\t%s\t%s\t%s\n' % (timestamp, opco, service, recvcount, ackcount, nackcount, retrycount))
log.close()
UPDATE
I have updated my script using most of the suggestions posted here. I am now using regexes. I am not using dict.keys()
to iterate through the dicts anymore and I have shortened my statements, etc. However, my script is still running very slowly.
for opco in Cn:
for service in Cn[opco]:
for jarid in Cn[opco][service]['RECV']:
if jarid in Pr['13']:
Cn[opco][service].setdefault('ACK', []).append(jarid)
elif jarid in Pr['14']:
Cn[opco][service].setdefault('NACK', []).append(jarid)
else:
if jarid in Pr['504'] or jarid in Pr['505']:
Cn[opco][service].setdefault('RETRY', []).append(jarid)
I have run my script with a profiler and I can see that this step in the script is taking the most time (1368 CPU secs last time I executed it!). Please note that I am new to Python and I still don't have a good grasp on everything that can be done with it.
UPDATE
I have managed to get my whole script to execute in under 10 seconds by changing the 'for loop' I posted above to the following:
for opco in Cn:
for service in Cn[opco]:
ack = set(Cn[opco][service]['RECV']) & set(Pr['13'])
for jarid in ack:
Cn[opco][service].setdefault('ACK', set()).add(jarid)
nack = set(Cn[opco][service]['RECV']) & set(Pr['14'])
for jarid in nack:
Cn[opco][service].setdefault('NACK', set()).add(jarid)
retry = set(Cn[opco][service]['RECV']) & set(Pr['504'])
for jarid in retry:
Cn[opco][service].setdefault('RETRY', set()).add(jarid)
opco
? \$\endgroup\$#!/usr/bin/env python
? \$\endgroup\$