函数优化:先进行单线程优化(用line profiler),再进行多进程优化
line_profiler的使用
关于安装中出现的错误,参见这个lineprofiler安装错误
line_profiler的作用是得到程序每一行执行所使用的时间。
from line_profiler import LineProfiler
lp = LineProfiler()# 把函数传递到性能分析器
lp_wrapper = lp(transferdata)
lp_wrapper('C:/Users/Administrator.SC-201610171623/Desktop/湖南采集数据/不接地-所有/不接地-A/D/1200')
lp.print_stats()
解决了同时显示函数每行所用时间和调用函数每行所用时间的问题
也可以作为参考
多进程
所要分析的问题包括两部分:
(1)IO
(2)计算
多进程适合于计算密集型,
为什么不使用多线程
在实现Python解析器(CPython)时有GIL这一概念(大部分python执行环境默认为CPython),当然,也有JPython既没有GIL。
为了利用多核,Python开始支持多线程。而解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁。 也就是GIL锁。
Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。
但是多进程不受GIL影响
这篇解释的很好
改进了代码
def analyse(datname):#数据处理和数据写入都放在里面
data=transferdata(datname)
character=char(data)
df=pd.DataFrame(np.array(character),columns=['均值','标准差','偏度','峰度',
'd1/a6','d2/a6','d3/a6','d4/a6','d5/a6','d6/a6','能量比之和',
'd1方差','d2方差','d3方差','d4方差','d5方差','d6方差',
'd1最大','d2最大','d3最大','d4最大','d5最大','d6最大','a6最大',
'第一周期均值','第一周期标准差','第一周期偏度','第一周期峰度',
'第二周期均值','第二周期标准差','第二周期偏度','第二周期峰度'])
df.to_csv('my_csv.csv', mode='a', header=True)
def generate(dat_txt,workers):
with Pool(workers) as p:
p.map(analyse,dat_txt)#analyse是最终的数据处理函数
if __name__ == '__main__':
t = time.time()
generate(dat_txt,workers=8)
used = time.time()-t
print(used)
多个进程同时写入一个文件会出现阻塞
https://blog.csdn.net/Q_AN1314/article/details/51923022
多个进程同时写入一个文件会出现阻塞,解决办法:
1、对写入操作进行加锁,直到进程写完再放他离开,但是这样执行效率低下
2、更优雅的方法:使用multiprocessing的回调函数。
a、把写入操作抽象为单独的一个函数
b、把进程需要写入的内容,作为返回值返回
c、使用回调函数写入进程返回内容。
#数据分析函数
def analyse(datname):
data=transferdata(datname)
character=char(data)
df=pd.DataFrame(np.array(character),columns=['均值','标准差','偏度','峰度',
'd1/a6','d2/a6','d3/a6','d4/a6','d5/a6','d6/a6','能量比之和',
'd1方差','d2方差','d3方差','d4方差','d5方差','d6方差',
'd1最大','d2最大','d3最大','d4最大','d5最大','d6最大','a6最大',
'第一周期均值','第一周期标准差','第一周期偏度','第一周期峰度',
'第二周期均值','第二周期标准差','第二周期偏度','第二周期峰度'])
return df#把进程需写入的内容作为返回值返回
#写文件的函数单独放在一个函数里
def write2csv(df):
df.to_csv('my_csv.csv', mode='a', header=True)
if __name__ == '__main__':
t = time.time()
pool = Pool()
for i in dat_txt:
pool.apply_async(analyse,(i,),callback=write2csv)#analyse是进程运行的对象
used = time.time()-t
print(used)
- apply_async用于传递不定参数,同python中的apply函数一致。但他是非阻塞的且支持结果返回后进行回调。
- close()关闭pool,使其不再接受新的任务 join() 主进程阻塞等待子进程的退出, join方法要在close后使用。
在windows环境下,python的多进程没有像linux环境下的多进程一样,linux环境下的multiprocessing库是基于fork函数,父进程fork了一个子进程之后会把自己的资源,比如文件句柄都传递给子进程。但是在windows环境下没有fork函数,所以如果你在父进程里打开了一个文件,在子进程中写入,会出现ValueError: I/O operation on closed file这样的错误,而且在windows环境下最好加入if name == 'main’这样的判断,以避免一些可能出现的RuntimeError或者死锁。
使用df.to_csv()每次追加一组数据都会带上key,后面处理csv文件不方便,这里改进一下,将计算出来的特征直接输出到csv文件中,速度也快了不少
def analyse(datname):
data=transferdata(datname)
character=char(data)
return character
def write2csv(df):
writer = csv.writer(file_csv, dialect='excel')
for data in df:
writer.writerow(data)
def generate(dat_txt,workers):
with Pool(workers) as p:
p.map(analyse,dat_txt)
if __name__ == '__main__':
__spec__ = "ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>)"#spyder的错误,加上这一句就不错了
t = time.time()
file_csv = open('try.csv','a', newline='')#加上newline=''保证行之间无空行
pool = Pool()
for i in dat_txt:
pool.apply_async(analyse,(i,),callback=write2csv)
pool.close()
pool.join()
file_csv.close()
used = time.time()-t
print(used)