Python-使用.format进行日志记录会创建额外的CPU周期吗
test.py 我上面有以下代码 当我使用python test.py-log=INFO执行时,是否真的会导致额外的CPU周期,因为它将首先格式化字符串,然后由于调试级别而不输出任何内容 是否仍然可以使用.format样式来记录日志,并且如果日志最终没有输出,就不会产生额外的CPU周期 编辑: 第1部分->是的,通过这个测试代码它是正确的,尽管非常小 old-format.py new-format.py python-mcprofile~/Desktop/old-format.py->500464函数调用只需0.176秒Python-使用.format进行日志记录会创建额外的CPU周期吗,python,python-3.x,logging,Python,Python 3.x,Logging,test.py 我上面有以下代码 当我使用python test.py-log=INFO执行时,是否真的会导致额外的CPU周期,因为它将首先格式化字符串,然后由于调试级别而不输出任何内容 是否仍然可以使用.format样式来记录日志,并且如果日志最终没有输出,就不会产生额外的CPU周期 编辑: 第1部分->是的,通过这个测试代码它是正确的,尽管非常小 old-format.py new-format.py python-mcprofile~/Desktop/old-format.py->5004
python-mcprofile~/Desktop/new-format.py->600464函数调用只需0.237秒是的,这样做时,它必须做额外的工作,创建永远不会使用的字符串。这就是说,一个小黑客将允许您切换到惰性格式的消息 Python人员提供了一个用于切换的方法,或者使用一个带有_str___________________________________________________。后一种方法在使用时是最干净的。《操作指南》中的简单示例如下:
import logging
class Message(object):
def __init__(self, fmt, args):
self.fmt = fmt
self.args = args
def __str__(self):
return self.fmt.format(*self.args)
class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra=None):
super(StyleAdapter, self).__init__(logger, extra or {})
def log(self, level, msg, *args, **kwargs):
if self.isEnabledFor(level):
msg, kwargs = self.process(msg, kwargs)
self.logger._log(level, Message(msg, args), (), **kwargs)
logger = StyleAdapter(logging.getLogger(__name__))
def main():
logger.debug('Hello, {}', 'world!')
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()
在实际代码中,消息和样式适配器可能隐藏在单独的模块中,在使用它们的模块中只留下很少的自定义代码。说真的,您担心的是错误的问题。是的,字符串将被格式化,可能最终会被丢弃。不,没关系 我怎么知道 你量过了吗?我打赌你没有,如果你有,你会发现格式化字符串的时间很短,数量级少于写入磁盘的时间,接近调用函数所需的时间 如果日志记录处于一个被称为数千次的紧密循环中,那么您的日志将很难看并且很难使用。所以我打赌它不在那里,或者在调试完成后不会在那里 如果日志记录不是在一个紧密的循环中,那么丢失消息的总格式化时间将是微乎其微的 如果程序是CPU密集型的,并且在Python解释器中花费了大量时间,那么日志不是原因。您需要寻找能够减轻解释器工作负担的Python技术/库,或者寻找能够减轻解释器工作负担的语言 如果日志模块的滥用是许多程序员非常担心的问题,那么文档中会有警告或代码中的修复。你有太多的公司独立发现了一个微不足道的优化问题 我见过使用重载调试日志的C程序,其中printf造成了很大的开销。但那是因为C的其余部分太少了。如果C具有垃圾收集、动态名称解析和列表理解等功能,格式化I/O将开始变得便宜
使用日志模块最有效的方式是最容易编程的方式,因为我希望您的时间!这不便宜。如果有人抱怨你浪费了周期,请他说明它如何占你程序运行时间的1%。然后你可以把注意力放在重要的事情上 为什么需要使用格式?是的,如果日志记录级别不相关,如果使用正确的方法,可以避免字符串插值。如果您担心这一分钟的CPU周期,那么您使用的语言是错误的。尝试C++或生锈,如果这是一个大的关注。@ TexEndiad它是一种解释性语言,所以我最好做一切在^ 2’。故意将其发挥到极致,但没有理由肆意使用运行时。字符串插值非常昂贵,而且在一个紧密的循环中可能会有大量调试消息。@roganjosh我同意您可以尽可能多地压缩性能,并在相同的环境中工作。。。如果他发现自己不得不担心很多这类事情,我只是提出一些建议。我根本不相信这种说法。我使用循环进行启发式,可能会运行30万次,直到问题得到解决。我可能想调试我在调度问题中所做的所有交换。循环使用python,转换使用numpy。您可以轻松地将调试消息的“我的运行时”翻倍。我基本上是站在你这边的,但当我知道python库适合我的应用程序时,我不喜欢横扫断言,但我不会故意放慢速度。@roganjosh,是的,日志消息可能会影响性能。不,你如何格式化它们并不重要。在300K循环中,是否生成调试语句?如果是的话,I/O比字符串格式更昂贵,而且格式是不可避免的,对吗?如果您没有,并且仍然在调用log函数,那么字符串格式化的成本占函数调用的百分比是多少?我是说:不多,所以没关系。
import logging
# V1
for x in range(0, 100000):
logging.debug('%s before you %s', 'Look', 'leap!')
import logging
# V2
for x in range(0, 100000):
logging.debug('{} before you {}'.format('Look', 'leap!'))
import logging
class Message(object):
def __init__(self, fmt, args):
self.fmt = fmt
self.args = args
def __str__(self):
return self.fmt.format(*self.args)
class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra=None):
super(StyleAdapter, self).__init__(logger, extra or {})
def log(self, level, msg, *args, **kwargs):
if self.isEnabledFor(level):
msg, kwargs = self.process(msg, kwargs)
self.logger._log(level, Message(msg, args), (), **kwargs)
logger = StyleAdapter(logging.getLogger(__name__))
def main():
logger.debug('Hello, {}', 'world!')
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()