为什么显式调用os.mkdir()速度变慢?

3
我一直在处理一个需要创建大型目录结构的项目。我的第一个解决方案是保留所有目录的字典,如果遇到尚未创建的目录,则使用os.makedirs()创建它及任何缺失的中间目录。 当我对这段代码进行剖析时,我发现绝大部分时间(132秒中的105秒)都用于调用posix.stat()来确定中间目录不存在。然而,我是在一个空目录中构建整个结构,因此我已经知道没有任何中间目录存在。
为了利用这一点,我编写了代码的另一个版本,它保留了一个内部备忘录,描述了目录树的结构,以便可以确定已创建哪些目录,而无需查询os:
class DirTree:
  def __init__(self, root):
    self.root = os.path.abspath(root)
    self.tree = {}
  def makedirs(self, path):
    relpath = os.path.relpath(path, self.root).replace('\\', '/')
    built = self.root
    node = self.tree
    for directory in relpath.split('/'):
        built = os.path.join(built, directory)
        if directory in node:
            node = node[directory]
        else:
            node[directory] = {}
            node = node[directory]
            os.mkdir(built, 0777)

这段代码的运行速度确实更快,但是当我通过分析器运行它时,相同的4068次os.mkdir()调用现在需要4倍的时间(94秒而不是24秒)。我不明白为什么当从我的函数中调用该函数时,它需要更长的时间,而当由os.makedirs()调用时,它却不需要。有人知道为什么吗?

1个回答

1
您说得对,os.mkdirs在创建目录之前会检查路径组件的存在请参见此处,第136行。 您的代码和os.makedirs都使用c-python模块posixmodule.c来实现mkdir,在Linux上解析为系统调用mkdir。
看起来os.mkdir确实不必要地进行了stat操作,因为如果“a”不存在,则“a / b”肯定也不存在。
使用strace可以看到两个实现调用相同次数的mkdir,但是当路径为相对路径时,您创建的函数仍然构造绝对路径,而os.makedirs使用相对路径。
一个可能性是额外的时间是操作系统在目录结构中搜索正确的目录而不是每次添加到“。”。

os.mkdirs

stat("a/b/c", 0x7fff34b1c4d0)           = -1 ENOENT (No such file or directory)
stat("a/b", 0x7fff34b1c260)             = -1 ENOENT (No such file or directory)
stat("a", 0x7fff34b1bff0)               = -1 ENOENT (No such file or directory)
mkdir("a", 0777)                        = 0
mkdir("a/b", 0777)                      = 0
mkdir("a/b/c", 0777)                    = 0
mkdir("a/b/c/d", 0777)                  = 0

修改后的 mkdirs

mkdir("/tmp/a", 0777)                   = 0
mkdir("/tmp/a/b", 0777)                 = 0
mkdir("/tmp/a/b/c", 0777)               = 0
mkdir("/tmp/a/b/c/d", 0777)             = 0

话虽如此,但我无法复现你的结果。 我发现使用cProfile测量的os.mkdirs或您的源代码调用的mkdir所花费的时间大致相同。

os.mkdirs

 4003    0.132    0.000    0.132    0.000 {posix.mkdir}

修改后的mkdirs

 4003    0.147    0.000    0.147    0.000 {posix.mkdir}

但是在posixpath的新源代码中花费了大量时间。

 4000    0.104    0.000    1.003    0.000 posixpath.py:400(relpath)

也许这是分析方法或安装的微妙差别所导致的结果。

我认为这不是C端的问题。相反,似乎是在makedirs()中的if head and tail and not path.exists(head):执行了stat()调用。 - glglgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接