【问题标题】:How to avoid loading a large file repeatedly?如何避免重复加载大文件?
【发布时间】:2012-01-27 23:55:02
【问题描述】:

我正在尝试从 python 中调用 Java 程序(Stanford Chinese Word Segmenter)。 Java 程序需要加载一个大的(100M)字典文件(辅助切分的单词列表),这需要 12 秒以上。我想知道是否可以加快加载过程,更重要的是,当我需要多次调用python脚本时,如何避免重复加载?

以下是代码的相关部分:

op = subprocess.Popen(['java',
                       '-mx2g',
                       '-cp',
                       'seg.jar',
                       'edu.stanford.nlp.ie.crf.CRFClassifier',
                       '-sighanCorporaDict',
                       'data',
                       '-testFile',
                       filename, 
                       '-inputEncoding',
                       'utf-8', 
                       '-sighanPostProcessing',
                       'true',
                       'ctb', 
                       '-loadClassifier',
                       **'./data/ctb.gz',**
                       '-serDictionary',
                       './data/dict-chris6.ser.gz',
                       '0'],
                       stdout = subprocess.PIPE,
                       stdin  = subprocess.PIPE,
                       stderr = subprocess.STDOUT,
                       )

在上面的代码中,'./data/ctb.gz'是加载大词表文件的地方。我认为这可能与流程有关,但我对此了解不多。

【问题讨论】:

    标签: java python performance process persistence


    【解决方案1】:

    您或许可以在此处使用特定于操作系统的解决方案。大多数现代操作系统都能够在内存中进行分区。例如,在 Linux 中你可以这样做

     mkfs -q /dev/ram1 8192
     mkdir -p /ramcache
     mount /dev/ram1 /ramcache
    

    将文件移动到该目录将大大加快 I/O

    【讨论】:

      【解决方案2】:

      可能有很多方法可以加快单词列表的加载速度,但这取决于细节。如果 IO(磁盘读取速度)是瓶颈,那么一种简单的方法可能是压缩文件并使用 ZipInputStream 读取它 - 但您需要对此进行基准测试。

      为避免多次加载,您可能需要保持 Java 进程运行,并通过文件或套接字从 Python 与其通信,向其发送命令,而不是每次都从 Python 实际启动 Java 进程。

      但是,这两个都需要修改 Java 代码。

      【讨论】:

      • 非常感谢您的 cmets。很高兴知道可以避免重复加载。但由于我不懂Java,我不确定我是否可以做到这一点。你能给我一些建议或例子吗?
      【解决方案3】:

      如果 java 程序在收到来自filename named pipe 的输入后立即产生输出,并且您无法更改 java 程序,那么您可以保持 Python 脚本运行,并通过文件/套接字与它进行通信,如 @DNA suggested for the Java process (相同的想法,但 Python 程序继续运行)。

      # ...
      os.mkfifo(filename)
      
      p = Popen([..., filename, ...], stdout=PIPE)
      with open(filename, 'w') as f:
           while True:
               indata = read_input() # read text to segment from files/sockets, etc
               f.write(indata)
               # read response from java process
               outdata = p.stdout.readline()# you need to figure out when to stop reading
               write_output(outdata) # write response via files/sockets, etc
      

      【讨论】:

      • 非常感谢,@J.F.塞巴斯蒂安。这为我提供了如何在我的代码中实现这一点的上下文。
      • 您提到我需要弄清楚何时停止阅读。你的意思是停止 JVM 还是仅仅找出分段器输出的结尾。换句话说,您是否建议创建一个 Java 守护进程并在后台持续运行分段器?
      • @aihaiyang:我的意思是“找出分段器输出的结尾”。先让 Python 程序作为脚本工作,以后可以将其守护。
      【解决方案4】:

      您可以运行 JVM 的单个实例并使用named pipes 允许 python 脚本与 JVM 通信。假设 JVM 执行的程序是无状态的,并且在其标准输出(可能还有标准错误)上响应通过其标准输入到达的请求,这将起作用。

      【讨论】:

      • 感谢亚当!我可以看到一般的想法,但因为我不了解 Java。所以我想知道你能给我更多关于如何运行 JVM 实例以及如何从 python 进行通信的帮助吗?
      • 好吧,如果我提到的假设是正确的,则无需修改 Java 应用程序,只需将其 stdin/stdout/stderr 重定向到适当的命名管道即可。然后,python 进程可以打开管道以与 Java 应用程序进行通信。
      • 我认为java应用只是做分词,很可能是无状态的。但是我不懂Java,也不知道从哪里开始。
      • 您无需了解 Java 即可应用此解决方案。您需要知道命名管道,这就是我添加参考链接的原因 :-) 只需像在命令行上一样正常运行 Java 应用程序,并重定向到命名管道。另见mkfifo 命令。
      • 感谢亚当,非常感谢。我认为现在是时候让我真正掌握一些 Java 的工作知识,以便顺利度过这个难关了。
      【解决方案5】:

      为什么不跟踪文件是否已经在 python 端被读取?我不是 python 高手,但我相信你可以拥有一些迄今为止已打开的所有文件的列表或地图/字典。

      【讨论】:

      • 谢谢@user949300。 wordlist 文件是编译后的字节码并以 Java 加载。我不确定是否可以在 python 中打开它,然后以某种方式将其发送到 Java...
      猜你喜欢
      • 1970-01-01
      • 2015-09-09
      • 2012-04-30
      • 2016-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-05
      • 1970-01-01
      相关资源
      最近更新 更多