在Java中从大文件中读取和处理字符串的最快方法是什么?

4

我有一个很长的字符串存在一个文件中(它是经过我的自定义编码的数据),我想要读取并将其处理成我的特殊格式(解码)。我想知道最快的方法是什么,以便获得最终格式。我考虑了一些方法,但不确定哪种方法最好。

1)一次性读取整个字符串,然后对该字符串进行处理。

2)逐个字符从文件中读取并在读取时进行处理。

有人能帮忙吗? 谢谢


你是说这两种方法基本上是一样的吗?但第一种方法不会占用更多的内存吗? - omega
我将把我的评论转化为答案,因为在评论区会变得太复杂。 - Victory
我认为你也可以在Java中查找mmap的等效方法。 - Alon
这在很大程度上取决于您的实际格式。 - njzk2
此外,请定义“大文件”。 同时,定义“大文件”。 - njzk2
根据文件大小,我进一步完善了我的答案。 - drrob
3个回答

4
很可能这个过程会受到IO限制而非CPU限制,所以它可能并不重要。如果确实重要的话,那就是因为解码函数,但该函数在问题中没有给出。
理论上有两种情况可以选择,这将决定哪个更快:(1)或(2)。
假设解码很快,所以您的进程将受到IO限制。
如果一次将整个文件读入内存,可以减少上下文切换次数,从而浪费更少的CPU周期,并提高读取整个文件的速度。
如果逐个字符读入文件时不会过早地把时间让给CPU,则理论上可以利用IO等待CPU周期来运行解码,因此逐个字符读取将更快。
以下是一些时间轴:
TIME    -------------------------------------------->
IO:     READ CHAR --> wait -->   READ CHAR --> wait 
DECODE: wait ------> DECODE --> wait --->  DECODE ...

逐字符读取的不良案例

TIME    -------------------------------------------->
IO:     READ CHAR --> YIELD          -->  READ CHAR --> wait 
DECODE: wait ------>  YIELD          --> DECODE --->  wait DECODE ---> ...

读取整个文件

TIME    -------------------------------------------->
IO:     READ CHAR .....  READ CHAR --> FINISH
DECODE: -----------------------------> DECODE --->

如果解码速度非常慢,那么生产者消费者模型可能会更快。你最好使用BufferedReader,在浪费/让出最少的CPU周期的同时尽可能多地进行IO操作。

如果不同的Java程序同时尝试从同一个文件中读取数据,会发生什么? - omega
当然,这取决于文件系统,但一般来说这不会加快任何速度。你仍然会受到IO限制。 - Victory
我认为并发文件读取是一种功能要求,而不是试图更快地读取 - @omega,你能确认吗? - drrob

3
使用BufferedReader或BufferedInputStream逐个字符地处理文本是可行的,缓冲区会自动透明地读入多个字符,对于典型需求来说性能足够好。将整个字符串读入内存被称为“一次性读取”,由于给定的内存开销通常被认为是文件处理的最后手段。如果您正在逐个字符地处理内存中的字符串,那么它可能甚至没有可检测的速度优势,因为您只是在使用自己的(非常大的)缓冲区。使用BufferedReader或BufferedInputStream可以调整缓冲区大小,因此必要时可以设置得很大。考虑到您的文件大小(20-30MB),根据该文件的编码方式,请注意Java char是16位的,因此对于ASCII文本文件或UTF-8文件中的少量扩展字符,您必须为典型JVM实现的内存使用量增加一倍。

但实际上,Java程序的不同实例将运行,并且一堆实例可以尝试同时访问同一个文件。这会改变你的看法吗? - omega
1
只读访问?不,无论哪种方式,操作系统都可能会缓存,你看不到任何区别。我已经添加了您可以调整缓冲区大小的信息,甚至可以等效于加载整个字符串。BufferedReader / BufferesInputStream 可以让您在不重新设计的情况下改变主意,只需调整一个数字即可。 - drrob

0

这取决于解码处理。

如果您可以并行化处理,可以考虑使用map/reduce方法。将文件内容分成单独的map步骤,然后在reduce步骤中组合它们以获得最终结果。

大多数机器都有多个核心。如果处理器之间不需要通信,则可以通过拥有N个核心来将处理时间减少1/N。如果您有可以利用的GPU,则会得到真正的东西。


如果算法中没有“严格串行”的部分,您只能将时间缩短到1/N。您不能假设所有的IO都不是串行的,实际上从像Windows这样的文件系统(不确定是否适用于10)来看,即使您对其进行了分块,它基本上仍然是串行的。请参见https://en.wikipedia.org/wiki/Amdahl%27s_law。 - Victory
我会在类似Hadoop文件系统中复制文件,并让每个map步骤读取它的数据块。 - duffymo

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