2

我有一些巨大的(几千兆字节)ASCII 文本文件,我需要逐行读取,将某些列转换为浮点数,并对这些数字进行一些简单的操作。这是非常简单的事情,除了我认为必须有一种方法来加速它。该程序从不使用相当于 100% 的 CPU 内核,因为它花费大量时间等待 I/O。同时,它花费了足够的时间进行计算而不是 I/O,它只执行大约 8-10 MB/秒的原始磁盘 I/O。我已经看到我的硬盘驱动器做得比这要好得多。

在单独的线程中进行 I/O 和处理可能会有所帮助吗?如果是这样,实现这一点的有效方法是什么?一个重要的问题是如何分配内存来保存每一行,这样我就不会遇到瓶颈。

编辑:我现在使用的是 D 编程语言,版本 2 标准库,主要是更高级别的函数,用于大多数这些东西。std.stdio.File 使用的缓冲区大小为 16 KB。

4

4 回答 4

1

如果您没有达到 100% CPU,那么您将受到 I/O 限制,并且不会看到多线程带来太多/任何改进 - 您将只有几个线程等待 I/O。事实上,如果他们正在访问文件的不同部分,您可能会引入磁盘搜索,让事情变得更糟。

首先看看更简单的事情:你能增加 I/O 可用的缓冲 RAM 的数量吗?(例如,在 C++ 中,FILE 对象的标准 I/O 缓冲区很小(例如 4kB),设置更大的缓冲区(例如 64kB)会对吞吐量产生巨大影响)。

您能否在 I/O 请求中使用更大的缓冲区大小:例如,将 64KB 的原始数据读入一个大缓冲区,然后自己处理,而不是一次读取一行或一个字节。

你输出任何数据吗?通过将其缓存在 RAM 中而不是立即将其写回磁盘,您可以将 IO 限制为纯粹读取输入文件,并帮助事情进展得更快。

您可能会发现,一旦您开始加载大量数据缓冲区,就会开始受到 CPU 的限制,此时您可以考虑多线程 - 一个线程读取数据,其他线程处理数据。

于 2010-01-14T19:30:49.003 回答
0

如果您有足够的 RAM,您可以将整个文件读入一个字符串,在行分隔符上对其进行标记,然后根据需要处理这些标记。

在 java 中,您将使用 StringBuilder 对象将文件内容读入其中。您还希望使用以下内容启动具有足够内存限制(本示例中为 2GB)的 jvm:

java -Xmx 2048 -Xms 2048 -jar MyMemoryHungryApp.jar

如果您不想将整个文件读入字符串,则可以迭代地批量读取并处理批次。

实际上,根据文件格式的详细信息,您可能可以使用 CSVReader 一个开源 Java 包(项目页面)通过 readAll() 方法将文件读入内存,您最终会得到一个List<String[]>并且您可以去镇上就可以了:)。

于 2010-01-14T19:30:19.517 回答
0

首先,我会采用您拥有的程序,并获取它的堆栈截图。这将确定在 I/O 上花费了多少时间,以及在 CPU 上花费了多少时间。

然后,如果 I/O 占主导地位,我会确保我正在读取尽可能大的缓冲区,以尽量减少磁盘磁头的运动。

然后,如果我看到 I/O 在 CPU 上等待,然后是 CPU 在 I/O 上等待,我会尝试执行异步 I/O,以便在 CPU 在另一个缓冲区上运行时加载一个缓冲区。(或者您可以使用读取器线程来读取备用缓冲区。)

如果 I/O 不占主导地位,而 CPU 占主导地位,那么我会看到 stackshots 告诉我有关 CPU 活动的信息。如果过多的时间花费在浮点数的反格式上,并且如果这些数字的格式相当简单,我会考虑自己解析它们,因为我可以利用更简单的格式。

这有帮助吗?

于 2010-01-14T19:31:09.567 回答
0

通常操作系统会尝试提前读取,如果您不受 CPU 限制,您应该接近硬盘限制速度。

原因可能是:

  • 大文件碎片化(您可以对卷进行碎片整理并检查情况是否更好)
  • 操作系统不使用预读(作为一种解决方案:在 Windows 下,您可以使用带有您将扫描文件的标志的 CreateFile)
  • 您没有使用有效的缓冲(例如,如果您从操作系统文件中读取一次只处理几个字节,那么事情会很慢。(您可能会尝试一次读取更大的块)

在您受到 CPU 限制的那一刻,您是否应该开始考虑更有效地解析数据。

于 2010-01-14T19:33:04.520 回答