同时写入大量文件的最佳进程数



我想同时写入大量文件(比如10000个文件)。我发现我可以为此使用多处理。我武断地选择使用100个进程来编写这些文件。我需要知道是否有一种方法可以找到要使用的最佳流程数量?此外,在该代码或所有进程自动终止后,我是否需要进行任何清理?

我也想知道是否有更有效的方法可以同时写入这么多文件。

from multiprocessing import Pool
def write(x):
fopen=open('file_'+str(x),'w')
fopen.write('anything')
fopen.close()
if __name__ == '__main__':
pool = Pool(processes=100)              
pool.map(write, range(10000))          

首先,对于纯I/O,threading可能与multiprocessing一样好,而且通常更好。它也没有关于"我需要清理吗"的神秘之处。所以,你可能想测试一下。

其次,如果你想知道最快的方法,唯一真正的选择就是使用timeit进行测试,或者使用你的shell的time或等效的方法。听起来你已经在这么做了。如果您正在寻找一种方法,根据您可以阅读的系统信息以编程方式确定理想的池大小(SSD与10K HD与5200 HD与远程共享、LAN与WAN、快速LAN与慢速LAN、SMB与NFS、Windows与POSIX等),您可能需要在各种机器上进行测试并进行一些统计分析。其中一些信息不是静态可用的,所以你真的需要启动这个过程,然后在进行时调整池的大小。这将是非常复杂的——我想所有这些工作在大多数情况下只会给你带来10%的好处。

如果您真的需要从文件I/O中挤出最后几个百分点,您可能需要降低一两个级别。

至少,您可能希望将Python和/或stdio缓冲区从等式中删除(假设文件真的这么小),并使用os.openos.write。创建一个字节而不是字符串的原始缓冲区甚至可能会有所帮助(尤其是如果这是Python3)。如果你实际上在向每个文件,甚至只是向多个文件写入完全相同的东西,那么使用相同的缓冲区可能会让操作系统识别出你在向多个文档写入相同的内容,这意味着缓存可以是完美的,而不是近乎完美。

您甚至可能想要下降到特定于平台的API。例如,在Windows上,使用重叠I/O可以让操作系统尽可能高效地调度写入,并且在IOCP周围创建一个本地线程池来处理完成操作也可以消除写入调度之外的所有开销。(您可以通过ctypeswin32api访问CreateFileWriteFileEx等。或者在谷歌上搜索"IOCP Python"以获取示例代码——这些代码都是不完整或部分无关的,尤其是因为其中大部分都是为使用c10k套接字服务器而设计的,但在MSDN和试错的帮助下,它至少可以证明自己能够将其余部分组装在一起。)我想不出在POSIX上有什么等效的东西(好吧,aio_write等效于WriteFileEx,但据我所知,它对任何现实世界的*nix平台上的性能都没有帮助)。

或者,您可能希望将向上移动一步。如果你真的在向所有或多个文件写入相同的数据,为什么不直接将其写入一个文件,然后要求操作系统复制该文件呢?它可能会做得更好。

或者,更简单——更快——将其写入一个文件,然后将其余文件创建为硬链接或符号链接。


由于您询问了最后一个选项:

创建链接的想法是只创建一个文件,但创建10000个不同的名称来访问它。

这意味着,如果编辑一个文件,则会编辑所有10000个文件。如果这不是你想要的,链接是不合适的。

但如果这是你想要的,有两种基本的链接:硬链接和符号链接。

现代文件系统允许多个目录条目指向同一个文件。创建硬链接是一种创建另一个目录项的方法,该目录项指向与现有文件相同的文件。在Python中,可以使用os.link执行此操作。因此:

with open('file_0', 'w') as f:
f.write('anything')
for i in range(1, 10000):
os.link('file_0', 'file_{}'.format(i))

现在,您的文件系统有10000个名为file_0file_9999的条目,但它们都是磁盘上相同实际数据的名称。编辑一个,另一个9999全部更改。删除一个,其余9999个仍然存在。

硬链接有一些小问题,还有一个大问题。次要的问题是,除了常规文件之外,每个平台都有不同的硬链接规则,而且通常不能跨文件系统进行硬链接。主要问题是Windows。首先,你需要像Vista和NTFS 6这样的完全支持,Win2000和NTFS 4这样的部分支持。但是,更重要的是,os.link在Windows上并不存在。因此,您必须使用ctypeswin32api来调用底层的CreateHardLink函数(或使用subprocess来运行mklinkfsutil命令)。

象征性的联系是更高层次的想法。它是一种特殊类型的文件,通过路径引用另一个文件。这意味着您可以读取关于符号链接本身的信息(请参见statlstat),创建一个保存链接信息的tarball,等等。这也意味着,如果您删除file_0,所有其他链接都会变成指向不存在的文件的断开链接。无论如何,在Python中,您可以使用os.symlink来创建它们(使用与上面完全相同的代码)。

符号链接没有硬链接的大多数限制,但对Windows来说更糟——在Vista之前根本没有符号链接,普通文件和目录的规则不同,可以遍历的链接数量限制,需要非管理员用户没有的特殊权限,等等。当然,你不能使用Python中的os.symlink

还有一些特定于平台的东西,如Windows快捷方式和Mac别名,与符号链接具有相似但不完全相同的功能。

最新更新