18

使用格式字符串 + args 列表调用日志函数与内联格式化是否有利?

我已经看到(并编写了)使用内联字符串格式的日志记录代码:

logging.warn("%s %s %s" % (arg1, arg2, arg3))

但我认为使用更好(性能方面,更惯用):

logging.warn("%s %s %s", arg1, arg2, arg3)

因为第二种形式在调用日志记录函数之前避免了字符串格式化操作。如果当前日志级别会过滤掉日志消息,则无需格式化,从而减少计算时间和内存分配。

我在正确的轨道上,还是我错过了什么?

4

3 回答 3

21

恕我直言,对于很可能会显示的消息,例如那些给予errorwarn没有太大区别的消息。

对于不太可能显示的消息,我肯定会选择第二个版本,主要是出于性能原因。我经常将大对象作为参数传递给info,它实现了一种代价高昂的__str__方法。显然,发送这个预先格式化的文件info会浪费性能。

更新

我刚刚检查了logging模块的源代码,实际上,格式化是检查日志级别后完成的。例如:

class Logger(Filterer):
    # snip
    def debug(self, msg, *args, **kwargs):
        # snip
        if self.isenabledfor(debug):
            self._log(debug, msg, args, **kwargs)

可以观察到这一点,msg并且在调用和检查日志级别args之间保持不变。log

更新 2

受 Levon 的启发,让我为具有昂贵__str__方法的对象添加一些测试:

$ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s', range(0,100))"
1000000 loops, best of 3: 1.52 usec per loop
$ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s' % range(0,100))"
1000000 loops, best of 3: 10.4 usec per loop

在实践中,这可以提供相当高的性能提升。

于 2012-08-14T15:39:04.963 回答
9

如果这有帮助,这里是对两个格式化选项的快速时序测试:

In [61]: arg1='hello'
In [62]: arg2='this'
In [63]: arg3='is a test'

In [70]: timeit -n 10000000 "%s %s %s" % (arg1, arg2, arg3)
10000000 loops, best of 3: 284 ns per loop

In [71]: timeit -n 10000000  "%s %s %s", arg1, arg2, arg3
10000000 loops, best of 3: 119 ns per loop

似乎给第二种方法带来了优势。

于 2012-08-14T15:33:23.710 回答
5

如果当前的日志记录级别过滤了日志消息(如我所料),那么避免内联字符串格式确实可以节省一些时间——但不会太多:

In [1]: import logging

In [2]: logger = logging.getLogger('foo')

In [3]: logger.setLevel(logging.ERROR)

In [4]: %timeit -n 1000000 logger.warn('%s %s %s' % ('a', 'b', 'c'))
1000000 loops, best of 3: 1.09 us per loop

In [12]: %timeit -n 1000000 logger.warn('%s %s %s', 'a', 'b', 'c')
1000000 loops, best of 3: 946 ns per loop

因此,正如user1202136 指出的那样,整体性能差异取决于格式化字符串所需的时间(这可能很重要,具体取决于调用__str__传递给日志记录函数的参数的成本。)

于 2012-08-14T16:11:14.053 回答