h5py内存文件与多进程错误
这段内容是给那些精通HDF5和多进程的朋友们的……首先,我知道Python的h5py和multiprocessing模块不一定能很好地配合,但我遇到了一个错误,实在搞不明白。我的脚本是创建一个临时的“内存中”的hdf5文件,把一个(pickle格式的)输入文件里的数据存到这个内存文件里,然后用一个多进程池对这个临时HDF5文件里的数据进行只读操作。
我已经找到了导致错误的代码,所以这里有一段简化的代码片段。当我在一个生成器函数中创建一个“内存中”的hdf5文件,然后用这个生成器给多进程池提供参数时,我就会遇到一系列HDF5的错误。以下是我尽量简化后的代码,用来重现这个错误:
import h5py
from multiprocessing import Pool
from itertools import imap
useMP = True
def doNothing(arg):
print "Do nothing with %s"%arg
def myGenerator():
print "Create hdf5 in-memory file..."
hdfFile = h5py.File('test.hdf',driver='core',backing_store=False)
print "Finished creating hdf5 in-memory file."
yield 3.14159
'''
# uncommenting this section will result in yet another HDF5 error.
print "Closing hdf5 in-memory file..."
hdfFile.close()
print "Finished closing hdf5 in-memory file."
'''
if useMP:
pool = Pool(1)
mapFunc = pool.imap
else:
mapFunc = imap
data = [d for d in mapFunc(doNothing,myGenerator())]
当我使用itertools.imap(设置“useMP=False”)时,输出是我预期的结果:
Create hdf5 in-memory file...
Finished creating hdf5 in-memory file.
Do nothing with 0
但当我使用Pool.imap时,即使这个池只创建了一个工作线程,我也会得到这样的输出:
Create hdf5 in-memory file...
HDF5-DIAG: Error detected in HDF5 (1.8.9) thread 139951680009984:
#000: ../../../src/H5F.c line 1538 in H5Fopen(): unable to open file
major: File accessability
minor: Unable to open file
#001: ../../../src/H5F.c line 1227 in H5F_open(): unable to open file: time = Wed Feb 27 09:32:32 2013
, name = 'test.hdf', tent_flags = 1
major: File accessability
minor: Unable to open file
#002: ../../../src/H5FD.c line 1101 in H5FD_open(): open failed
major: Virtual File Layer
minor: Unable to initialize object
#003: ../../../src/H5FDcore.c line 464 in H5FD_core_open(): unable to open file
major: File accessability
minor: Unable to open file
Finished creating hdf5 in-memory file.
Do nothing with 0
奇怪的是,这个错误并没有让程序崩溃。我写的脚本虽然出现了这个错误,但实际上运行得还算正常——不过它在每次创建内存文件时都会报这个错误。使用itertools.imap时没有错误,读取已有的HDF5文件时也没有错误,只有在多进程和内存HDF5文件结合使用时才会出现这个问题。
h5py版本 2.1.1
hdf5版本 1.8.9
Python版本 2.7.3
1 个回答
在查看一些h5py的文件后,我找到了一部分答案,不过还不完整。h5py.File这个类是在h5py/_hl/files.py里定义的。错误发生在创建File对象时,具体是在调用make_fid()的时候:
def make_fid(name, mode, userblock_size, fapl):
""" Get a new FileID by opening or creating a file.
Also validates mode argument."""
...
elif mode == 'a' or mode is None:
try:
fid = h5f.open(name, h5f.ACC_RDWR, fapl=fapl)
try:
existing_fcpl = fid.get_create_plist()
if userblock_size is not None and existing_fcpl.get_userblock() != userblock_size:
raise ValueError("Requested userblock size (%d) does not match that of existing file (%d)" % (userblock_size, existing_fcpl.get_userblock()))
except:
fid.close()
raise
except IOError:
fid = h5f.create(name, h5f.ACC_EXCL, fapl=fapl, fcpl=fcpl)
如果文件存在,它会以读写模式打开。如果文件不存在(比如在内存中的文件),那么h5f.open()这个调用就会抛出一个异常。正是这个h5f.open的调用导致了H5Fopen()的错误信息出现。
现在还有一个问题没有解决,那就是为什么这个错误只有在使用多进程时才会打印出来?首先,我原以为生成HDF5文件的函数是由主线程调用的。其实并不是。多进程池实际上会创建一个新线程来处理imap和imap_unordered的任务,但map/map_async则不是。如果把pool.imap换成pool.map,生成函数就会在主线程中被调用,这样就不会打印错误信息。那么,为什么在单独的线程中创建HDF5文件会引发错误呢?
-------- 更新 -----------
显然,h5py会自动在主线程中抑制HDF5的错误信息,因为h5py会自己处理这些错误。不过,它还没有自动抑制子线程中的错误。解决办法是使用h5py._errors.silence_errors()
。这个函数会在当前线程中禁用自动打印HDF5错误信息。具体可以查看h5py的问题206。这段代码可以抑制HDF5的错误:
import h5py
from multiprocessing import Pool
from itertools import imap
import threading
useMP = True
def doNothing(arg):
print "Do nothing with %s"%arg
def myGenerator():
h5py._errors.silence_errors()
print "Create hdf5 in-memory file..."
hdfFile = h5py.File('test.hdf',driver='core',backing_store=False)
print "Finished creating hdf5 in-memory file."
yield 3.14159
if useMP:
pool = Pool(1)
mapFunc = pool.map
else:
mapFunc = imap
data = [d for d in mapFunc(doNothing,myGenerator())]