h5py内存文件与多进程错误

1 投票
1 回答
3448 浏览
提问于 2025-04-17 17:20

这段内容是给那些精通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 个回答

2

在查看一些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())]

撰写回答