18

フォーマット文字列+引数リストを使用してロギング関数を呼び出すのとインラインでフォーマットするのが有利ですか?

インライン文字列フォーマットを使用するロギングコードを見た(そして書いた):

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

それでも私は、以下を使用する方が(パフォーマンス的に、より慣用的である)より良いと思います。

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

2番目の形式は、ロギング関数を呼び出す前の文字列フォーマット操作を回避するためです。現在のログレベルでログメッセージが除外される場合は、フォーマットは不要であり、計算時間とメモリ割り当てが削減されます。

私はここで正しい方向に進んでいますか、それとも何かを逃したことがありますか?

4

3 に答える 3

21

IMHO、に与えられたメッセージerrorwarnあまり違いがないメッセージなど、表示される可能性が非常に高いメッセージの場合。

表示される可能性が低いメッセージについては、主にパフォーマンス上の理由から、間違いなく2番目のバージョンを選択します。私はしばしば大きなオブジェクトをパラメータとして与えますが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

これが役立つ場合は、2つのフォーマットオプションの簡単なタイミングテストを次に示します。

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

2番目のアプローチにエッジを与えるようです。

于 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 に答える