【问题标题】:Why is os.mkdir() slower when called explicitly?为什么显式调用 os.mkdir() 会变慢?
【发布时间】:2014-02-21 22:17:31
【问题描述】:

我一直在从事一个必须创建大型目录结构的项目。我的第一个解决方案是保留所有存在的目录的字典,如果遇到尚未制作的目录,请使用 os.makedirs() 来创建它和任何缺少的中介。 当我分析这段代码时,我发现大部分时间(132 秒中有 105 秒)都花在调用 posix.stat() 以确定中间目录不存在。但是我在一个空目录中构建了整个结构,所以我已经知道中间目录都不存在。

为了利用这一点,我编写了一个版本的代码,它保留了描述目录树结构的内部备忘录,以便它可以在不查询操作系统的情况下确定创建了哪些目录:

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)

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

【问题讨论】:

    标签: python linux python-2.7 operating-system


    【解决方案1】:

    你是对的,os.mkdirs 在创建目录see here, line 136 之前检查路径组件的存在。您的代码和 os.mkdirs 都使用 c-python 模块 posixmodule.c 来实际实现 mkdir,它在 linux 上解析为系统调用 mkdir。

    鉴于 stat 非常耗时,看起来 os.mkdir 确实不必要地进行统计,因为如果“a”不存在,那么“a/b”当然也不存在。

    使用strace 可以看出两种实现调用 mkdir 的次数相同,但是当路径是相对路径时,您创建的函数无论如何都会构造绝对路径,而 os.mkdirs 使用相对路径。

    一种可能是额外的时间是操作系统搜索目录结构以找到正确的目录,而不是每次都添加到“。”

    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
    

    话虽如此,我无法重现您的结果。我发现 os.mkdirs 或您的源调用(使用 cProfile)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() 调用。
    猜你喜欢
    • 2019-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-29
    • 1970-01-01
    • 2018-07-19
    • 2017-04-16
    • 1970-01-01
    相关资源
    最近更新 更多