代码之家  ›  专栏  ›  技术社区  ›  Flavius

printf减慢了我的程序

  •  26
  • Flavius  · 技术社区  · 15 年前

    我有一个小的C程序来计算哈希(对于哈希表)。我希望代码看起来很干净,但是有一些与之无关的东西困扰着我。

    我可以在0.2-0.3秒内轻松生成大约一百万个哈希(以/usr/bin/time为基准)。但是,当我在for循环中打印f()时,程序将减速到大约5秒。

    1. 为什么会这样?
    2. 如何加快速度?可能是标准输出吗?
    3. stdlibc是如何设计的,如何改进呢?
    4. 内核如何更好地支持它?如何修改它,使本地“文件”(套接字、管道等)的吞吐量真正快速?

    我期待着有意思和详细的答复。谢谢。

    PS:这是一个编译器构造工具集,所以不要害羞地去了解细节。虽然这与问题本身无关,但我只是想指出我感兴趣的细节。

    补遗

    我正在寻找更多的解决方案和解释的编程方法。事实上,管道可以完成这项工作,但我无法控制“用户”的行为。

    当然,我现在正在做一个测试,这不是“普通用户”做的。但这并不能改变一个简单的printf()减慢进程的事实,这是我试图为之找到最佳编程解决方案的问题。


    附录-惊人的结果

    引用时间是在tty中对plain printf()调用的,大约需要4分钟20秒。

    在a/dev/pts(例如konsole)下进行测试可将输出加速到大约5秒。

    在我的测试代码中使用setbuffer()的时间大约是16384,在8192中几乎相同:大约6秒。

    设置缓冲区() 显然地 使用时没有效果:使用时间相同(在TTY上大约4分钟,在PTS上大约5秒)。

    令人惊讶的是 ,如果我开始测试tty1,然后 切换到另一个tty 它确实需要和pts上一样的时间:大约5秒。

    结论 :内核做了一些与可访问性和用户友好性有关的事情。呵呵!

    通常情况下,不管你是在TTY处于活动状态时盯着它看,还是切换到另一个TTY,它都应该同样慢。


    :运行输出密集型程序时,切换到另一个tty!

    9 回复  |  直到 11 年前
        1
  •  30
  •   qrdl    15 年前

    无缓冲输出非常慢。

    默认情况下 stdout 是完全缓冲的,但是当连接到终端时, 标准输出 未缓冲或线路缓冲。

    尝试打开缓冲 标准输出 使用 setvbuf() ,像这样:

    char buffer[8192];
    
    setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));
    
        2
  •  14
  •   edoloughlin    15 年前

    您可以将字符串存储在缓冲区中,并在缓冲区满后定期将其输出到文件(或控制台)中。

    如果输出到控制台,滚动通常是一个杀手。

        3
  •  9
  •   Andreas Bonini    15 年前

    如果您要打印到控制台,通常会非常慢。我不知道为什么,但我相信在控制台以图形方式显示输出的字符串之前,它不会返回。此外,不能将mmap()转换为stdout。

    写入文件的速度应该快得多(但仍然比计算哈希慢几个数量级,所有I/O都慢)。

        4
  •  7
  •   corvus    15 年前

    您可以尝试将shell中的输出从控制台重定向到文件。使用它,可以在几秒钟内创建大小为千兆字节的日志。

        5
  •  6
  •   Peter Mortensen icecrime    11 年前
    1. I/O总是比 直接计算。该系统具有 等待更多组件 可供使用。它 然后必须等待回应 才能继续下去。相反地 如果只是简单的计算,那么 只在 RAM和CPU寄存器。

    2. 我没有测试过这个,但是将散列附加到字符串上,然后在末尾打印字符串可能会更快。虽然如果你使用C,而不是C++,这可能是一种痛苦!

    恐怕3和4比我强。

        6
  •  4
  •   Marco    15 年前

    由于I/O总是比CPU计算慢得多,所以您可能首先将所有值存储在最快的I/O中。因此,如果您有足够的内存,请使用RAM;如果没有,请使用文件,但它比RAM慢得多。

    现在可以在以后或由另一个线程并行打印这些值。因此,计算线程可能不需要等待printf返回。

        7
  •  4
  •   Community CDub    8 年前

    我很久以前发现的 using this technique 应该是显而易见的。 不仅I/O速度慢,特别是控制台,而且格式化十进制数字也不快。如果你能把二进制的数字放入大的缓冲区,并把它们写到一个文件中,你会发现它要快得多。

    另外,谁来读呢?如果没有人需要阅读全部内容,那么就没有必要以人类可读的格式打印全部内容。

        8
  •  4
  •   AnthonyLambert    15 年前
    1. 为什么不按需创建字符串而不是在构建点创建字符串呢?在一秒钟内输出40屏数据是没有意义的,你怎么可能读取它呢?为什么不根据需要创建输出,只显示最后一个屏幕,然后根据需要用户滚动????

    2. 为什么不使用sprintf打印到一个字符串,然后在内存中构建一个包含所有结果的连接字符串,并在最后打印?

    3. 通过切换到sprintf,您可以清楚地看到格式转换花费了多少时间,以及向控制台显示结果花费了多少时间,并适当地更改代码。

    4. 根据定义,控制台输出很慢,创建散列只需要操作几个字节的内存。控制台输出需要经过操作系统的许多层,这些层将具有处理线程/进程锁定等的代码。一旦它最终到达显示驱动程序,可能是9600波特设备!或者大的位图显示,像滚动屏幕这样的简单功能可能涉及操作兆字节的内存。

        9
  •  2
  •   t0mm13b    15 年前

    我猜终端类型正在使用一些缓冲输出操作,所以当您执行printf时,它不会在split微秒内输出,而是存储在终端子系统的缓冲内存中。

    这可能会受到其他可能导致速度减慢的事情的影响,可能除了您的程序之外,还有一个内存密集型的操作正在运行。简而言之,可能同时发生的事情太多了,分页、交换、另一个进程的大量I/O、使用的内存配置、可能的内存升级等等。

    最好将字符串连接到达到某个限制,然后在达到该限制时立即将其全部写出。或者甚至使用pthreads来执行所需的进程执行。

    编辑: 至于2,3,我无法理解。4, 我对太阳不熟悉,但我知道,也曾弄乱了Solaris, 可能存在使用虚拟tty的内核选项。我承认,自从处理内核配置并重新编译它以来已经有一段时间了。因此,我的记忆可能不太好,有一个根周围的选项看。

    user@host:/usr/src/linux $ make; make menuconfig **OR kconfig if from X**
    

    这将启动内核菜单,在“设备”子树下的“视频设置”部分中进行挖掘。

    编辑: 但是,通过在proc文件系统中添加一个文件(如果确实存在这样的文件),或者可能是传递给内核的一个开关(这是富有想象力的,并不意味着它确实存在),可以对内核进行一些调整,fastio

    希望这有帮助, 最好的问候, 汤姆。