printf减慢了我的程序
我有一个小的C程序来计算哈希值(哈希表)。 我希望代码看起来很干净,但有一些与之无关的东西让我烦恼。
我可以在大约0.2-0.3秒内轻松生成大约一百万个哈希值(以/ usr / bin / time为基准)。 但是,当我在for循环中使用printf()时,程序会减慢到大约5秒钟。
- 为什么是这样?
- 如何让它更快? mmapp()ing stdout也许?
- stdlibc是如何设计的,以及如何改进?
- 内核怎么能更好地支持它? 如何修改本地“文件”(套接字,管道等)的吞吐量真的很快?
我期待着有趣而详细的回复。 谢谢。
PS:这是一个编译器构造工具集,所以不要害羞进入细节。 虽然这与问题本身无关,但我只想指出细节让我感兴趣。
附录
我正在寻找更多解决方案和解释的程序方法。 确实,管道工作起了作用,但我无法控制“用户”的作用。
当然,我现在正在进行测试,“普通用户”不会这样做。 但是,这并没有改变一个简单的printf()减慢进程的事实,这是我试图找到最佳编程解决方案的问题。
附录 – 令人惊讶的结果
参考时间用于TTY内的普通printf()调用,大约需要4分20秒。
在/ dev / pts(例如Konsole)下进行测试可将输出速度提高到约5秒。
在我的测试代码中使用setbuffer()大小为16384时需要大约相同的时间,对于8192几乎相同:大约6秒。
setbuffer()在使用它时显然没有效果:它需要相同的时间(在TTY上大约4分钟,在PTS上大约5秒)。
令人惊讶的是 ,如果我在TTY1上开始测试然后切换到另一个TTY ,它确实与PTS上的相同:大约5秒。
结论 :内核做了一些与可访问性和用户友好性有关的事情。 呵呵!
通常情况下,无论您在活动时盯着TTY,还是切换到另一个TTY,它都应该同样慢。
课程 :运行输出密集型程序时,切换到另一个TTY!
无缓冲输出非常慢。
默认情况下, stdout
是完全缓冲的,但是当连接到终端时, stdout
要么是无缓冲的,要么是行缓冲的。
尝试使用setvbuf()
为stdout
启用缓冲,如下所示:
char buffer[8192]; setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));
您可以将字符串存储在缓冲区中,并在缓冲区已满时将其输出到文件(或控制台)或定期输出。
如果输出到控制台,滚动通常是一个杀手。
如果你是printf()到控制台,它通常非常慢。 我不确定为什么,但我相信它不会返回,直到控制台以图形方式显示输出的字符串。 另外,你不能mmap()到stdout。
写入文件应该快得多(但仍然比计算散列慢几个数量级,所有I / O都很慢)。
您可以尝试将shell中的输出从控制台重定向到文件。 使用此function,可以在几秒钟内创建大小为千兆字节的日志。
-
与直接计算相比,I / O总是很慢。 系统必须等待更多组件可用才能使用它们。 然后它必须等待响应才能继续。 相反,如果它只是简单的计算,那么它只是真正在RAM和CPU寄存器之间移动数据。
-
我没有对此进行过测试,但是将哈希附加到字符串上可能会更快,然后只需在末尾打印字符串。 虽然如果你使用C而不是C ++,这可能会很痛苦!
3和4超出我的恐惧。
由于I / O总是比CPU计算慢得多,因此您可以先将所有值存储在最快的I / O中。 因此,如果你有足够的RAM,请使用RAM,如果没有,则使用Files,但它比RAM慢得多。
现在可以在其他线程之后或并行地打印出值。 因此计算线程可能不需要等到printf返回。
我很久以前发现使用这种技术应该是显而易见的。 不仅I / O速度慢,尤其是控制台,但格式化十进制数也不快。 如果你可以将二进制数放入大缓冲区,并将它们写入文件,你会发现它更快。
此外,谁会读他们? 如果没有人需要阅读所有这些内容,那就没有必要以人类可读的格式打印它们。
-
为什么不按需创建字符串而不是在构造点? 在一秒钟内输出40个数据屏幕没有意义,你怎么可能阅读它? 为什么不按要求创建输出,只显示最后一个屏幕,然后根据需要用户滚动???
-
为什么不使用sprintf打印到字符串然后在内存中构建所有结果的连接字符串并在结尾打印?
-
通过切换到sprintf,您可以清楚地看到格式转换花费了多少时间以及将结果显示到控制台并适当更改代码所花费的时间。
-
根据定义,控制台输出速度很慢,创建散列只是操纵几个字节的内存。 控制台输出需要遍历操作系统的多个层,一旦最终到达显示驱动程序(可能是9600波特设备),它将具有处理线程/进程锁定等的代码! 或大型位图显示,滚动屏幕等简单function可能涉及操作兆字节的内存。
我猜终端类型正在使用一些缓冲输出操作,因此当你执行printf时,它不会以分秒微秒输出,而是存储在终端子系统的缓冲存储器中。
这可能会受到可能导致速度减慢的其他因素的影响,也许除了您的程序之外,还会运行内存密集型操作。 简而言之,太多的事情可能同时发生,分页,交换,另一个进程的繁重i / o,使用的内存配置,可能内存升级等等。
最好将字符串连接到达到某个限制,然后在达到某个限制时,立即将其全部写出来。 甚至使用pthread来执行所需的流程执行。
编辑:至于2,3它超出了我。 对于4, 我不熟悉Sun,但是确实知道并且已经搞乱了Solaris,可能有一个内核选项来使用虚拟tty ..我会承认它已经有一段时间了,因为搞乱内核配置并重新编译它。 因此,我的记忆在这方面可能不是很好,请根据需要查看。
user @ host:/ usr / src / linux $ make; make menuconfig **或kconfig,如果来自X **
这将激活内核菜单,在设备子树下查看video设置部分。
编辑: 但是你通过在proc文件系统中添加一个文件(如果确实存在这样的东西),或者可能是一个传递到内核中的开关,将内存放入内核中,就像这样(这是富有想象力的,并不意味着它实际上存在),fastio
希望这会有所帮助,最好的问候,汤姆。