【问题标题】:Python equivalent of piping file output to gzip in Perl using a pipe使用管道在 Perl 中将管道文件输出到 gzip 的 Python 等效项
【发布时间】:2011-11-28 21:54:26
【问题描述】:

我需要弄清楚如何在Python中将文件输出写入压缩文件,类似于下面的两行:

open ZIPPED, "| gzip -c > zipped.gz";
print ZIPPED "Hello world\n";

在 Perl 中,这使用 Unix gzip 将您打印到 ZIPPED 文件句柄的任何内容压缩到文件“zipped.gz”。

我知道如何在 Python 中使用“import gzip”来执行此操作,如下所示:

import gzip
zipped = gzip.open("zipped.gz", 'wb')
zipped.write("Hello world\n")

但是,这非常慢。根据分析器的说法,使用该方法占用了我 90% 的运行时间,因为我正在将 200GB 的未压缩数据写入各种输出文件。我知道文件系统可能是这里问题的一部分,但我想通过使用 Unix/Linux 压缩来排除它。这部分是因为我听说使用同一个模块解压缩也很慢。

【问题讨论】:

  • 您是否需要在纯 Python 中完成它,或者您是否可以满足于调用文件系统上的二进制文件(在 Python 中,您会使用 subprocess 模块)?
  • 我不想在 Python 中这样做,因为纯 Python 方法太慢了。
  • 您是否在 200GB 未压缩数据上从 shell 运行 gzip 程序?我预计在 90-100% 的 CPU 利用率下会花费相当多的挂钟时间 - 在我的 Windows 机器上,它每 GB 运行大约 1 分钟,而 Python gzip 模块每 GB 大约需要 2 分钟。
  • 戴夫,是的,这是我追求的 2 分钟和 1 分钟之间的差异。

标签: python gzip pipe compression filehandle


【解决方案1】:

ChristopheD 建议使用subprocess module 是对这个问题的适当回答。但是,我不清楚它是否会解决您的性能问题。您必须测量新代码的性能才能确定。

要转换您的示例代码:

import subprocess

p = subprocess.Popen("gzip -c > zipped.gz", shell=True, stdin=subprocess.PIPE)
p.communicate("Hello World\n")

由于需要向子进程发送大量数据,因此应考虑使用 Popen 对象的stdin 属性。例如:

import subprocess

p = subprocess.Popen("gzip -c > zipped.gz", shell=True, stdin=subprocess.PIPE)
p.stdin.write("Some data")

# Write more data here...

p.communicate() # Finish writing data and wait for subprocess to finish

this question 上的讨论也很有帮助。

【讨论】:

  • 我验证了这种方法在 1GB 高度可压缩文件上的速度提高了 33%。与 gzip.open 相比,这是一个很好的改进。这是我用来测试它的代码: import subprocess text = "fjlaskfjioewru oijf alksfjlkqs jr jweqoirjwoiefjlkadsfj afjf\n" for i in xrange(1,25): text += text p = subprocess.Popen("gzip -c > zipped. gz", shell=True, stdin=subprocess.PIPE)` p.stdin.write(text) p.communicate() gzip.open 的时间:12.109u 1.194s 0:13.37 99.4% 0+0k 0+0io 0pf+ 0w 以上代码时间:8.379u 2.602s 0:10.17 107.8% 0+0k 0+0io 0pf+0w
  • 一定要接受你最喜欢的答案:-)。我们都喜欢额外的代表。
  • 出于好奇,当我使用这种方法而不是 gzip.open 时,大型测试的运行时间从 6h43m 下降到 4h31m。这是同一台机器上的苹果对苹果。这大约快了 33%,这正是我在较小的测试用例中看到的。谢谢大家!
  • @bu11d0zer:你应该使用 pastebin 来处理这种事情:pastebin.com/2kZHsbFH
【解决方案2】:

试试这样的:

from subprocess import Popen, PIPE
f = open('zipped.gz', 'w')
pipe = Popen('gzip', stdin=PIPE, stdout=f)
pipe.communicate('Hello world\n')
f.close()

【讨论】:

    【解决方案3】:

    使用 gzip module 是官方的一种方法,任何其他纯 python 方法都不太可能更快。尤其如此,因为您的数据大小排除了内存选项。最有可能的是,最快的方法是将整个文件写入磁盘并使用subprocess 对该文件调用 gz

    【讨论】:

      【解决方案4】:

      确保在比较速度时使用相同的压缩级别。默认情况下,linux gzip 使用 6 级,而 python 使用 9 级。我在 Python 3.6.8 中使用 gzip 1.5 版对此进行了测试,从 MySQL 转储中压缩了 600MB 的数据。使用默认设置:

      python 模块耗时 9.24 秒,文件大小为 47.1 MB
      子进程 gzip 耗时 8.61 秒,文件大小为 48.5 MB

      将其更改为 6 级后使其匹配:
      python 模块耗时 8.09 秒,文件大小为 48.6 MB
      子进程 gzip 耗时 8.55 秒,文件大小为 48.5 MB

      # subprocess method
      start = time.time()
      with open(outfile, 'wb') as f:
          subprocess.run(['gzip'], input=dump, stdout=f, check=True)
      print('subprocess finished after {:.2f} seconds'.format(time.time() - start))
      
      # gzip method
      start = time.time()
      with gzip.open(outfile2, 'wb', compresslevel=6) as z:
          z.write(dump)
      print('gzip module finished after {:.2f} seconds'.format(time.time() - start))
      

      【讨论】:

        【解决方案5】:

        除了@srgerg 的回答之外,我还想通过禁用shell 选项shell=False 来应用相同的方法,这也是在@Moishe Lettvin 的回答中完成的,并在(https://stackoverflow.com/a/3172488/2402577) 上推荐。

        import subprocess
        def zip():
            f = open("zipped.gz", "w")
            p1 = subprocess.Popen(["echo", "Hello World"], stdout=subprocess.PIPE)
            p2 = subprocess.Popen(["gzip", "-9c"], stdin=p1.stdout, stdout=f)
            p1.stdout.close()
            p2.communicate()
            f.close()
        

        请不要说我最初将这个p1s 输出用于git diff

        p1 = subprocess.Popen(["git", "diff"], stdout=subprocess.PIPE)

        【讨论】:

          猜你喜欢
          • 2015-07-15
          • 2020-01-22
          • 1970-01-01
          • 1970-01-01
          • 2012-09-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-25
          相关资源
          最近更新 更多