C malloc/free + fgets 性能
-
13-09-2019 - |
题
当我循环遍历文件 A 中的行时,我正在解析该行并将每个字符串 (char*
) 变成 char**
.
在一行的末尾,我运行一个过程,其中包括打开文件 B,使用 fgets
, fseek
和 fgetc
从该文件中抓取字符。然后我关闭文件 B。
我为每一行重复重新打开和重新关闭文件 B。
我想知道的是:
使用是否会对性能产生重大影响
malloc
和free
, ,这样我应该使用静态的东西,比如myArray[NUM_STRINGS][MAX_STRING_WIDTH]
而不是动态的char** myArray
?打开和关闭文件 B(概念上,数千次)是否会产生显着的性能开销?如果我的文件A已排序,有没有办法让我使用
fseek
在文件 B 中“向后”移动,重置我之前在文件 B 中的位置?
编辑 事实证明,双重方法大大缩短了运行时间:
我的文件 B 实际上是二十四个文件之一。而不是打开同一个文件 B1 一千次,然后 B2 一千次,等等。我打开文件 B1 一次,关闭它,打开 B2 一次,关闭它,等等。这减少了数千
fopen
和fclose
运营数量约为24。我用了
rewind()
重置文件指针。
这使得速度提高了大约 60 倍,这已经足够了。谢谢你指点我 rewind()
.
解决方案
如果您的动态数组随时间增长,则某些数据会产生复制成本 realloc
s。如果你使用“总是双倍”启发式,这会被摊销到 O(n),所以这并不可怕。如果您提前知道大小,堆栈分配的数组仍然会更快。
对于第二个问题,请阅读有关 rewind
. 。它必须比始终打开和关闭更快,并且可以让您减少资源管理。
其他提示
我想知道的是:
- 你的代码工作正常吗?
- 它的运行速度是否足以满足您的目的?
如果这两个答案都是“是”,则不要更改任何内容。
打开和关闭的开销可变,具体取决于其他程序是否竞争该资源。
首先测量文件大小 然后用它提前计算数组大小以进行一大堆分配。
您不会立即获得多维数组,但只需进行一些指针算术即可。
您能否不在另一个文件中缓存位置信息,然后使用先前的查找索引作为偏移量,而不是打开和关闭它?确实取决于确切的逻辑。
如果文件很大,磁盘 I/O 将比内存管理昂贵得多。在分析之前担心 malloc/free 性能表明它是瓶颈,这是过早的优化。
程序中频繁打开/关闭的开销可能很大,但实际的 I/O 可能会更昂贵,除非文件很小,在这种情况下,关闭和打开之间的缓冲区可能会丢失导致额外的磁盘 I/O。是的,您可以使用 ftell() 获取文件中的当前位置,然后使用 SEEK_SET fseek 来获取该位置。
使用动态内存总是会影响性能。使用静态缓冲区将提供速度提升。
重新打开文件也会对性能造成影响。您可以使用 fseek(pos, SEEK_SET) 将文件指针设置到文件中的任何位置,或使用 fseek(offset, SEEK_CUR) 进行相对移动。
显着的性能影响是相对的,您必须确定这对自己意味着什么。
我认为最好分配所需的实际空间,而且开销可能并不重要。这避免了浪费空间和堆叠溢出
是的。尽管IO被缓存,但您正在制作不必要的Syscall(开放和关闭)。可能会使用fseek
SEEK_CUR
或者SEEK_SET
.
在这两种情况下,都有 一些 性能会受到影响,但其重要性取决于文件的大小和程序运行的上下文。
如果您实际上知道字符串的最大数量和最大宽度,这会快得多(但如果您使用的字符串少于“最大”,则可能会浪费大量内存)。最好的办法是做 C++ 中许多动态数组实现所做的事情:每当您必须重新分配 myArray 时,请分配所需空间的两倍,并且只有在空间用完后才再次重新分配。这具有 O(log n) 性能成本。
这可能会对性能造成很大影响。我强烈建议使用 fseek,尽管详细信息取决于您的算法。
我经常发现性能开销被附带的直接内存管理所抵消。 malloc
以及内存上的那些低级 C 处理程序。除非内存的这些区域将在比接触该内存更长的摊余时间内保持静态且不受影响,否则坚持使用静态数组可能更有利。最后,这取决于你。