【问题标题】:Python datetime.strptime() Eating lots of CPU TimePython datetime.strptime() 占用大量 CPU 时间
【发布时间】:2010-11-01 16:28:53
【问题描述】:

我有一些日志解析代码需要将时间戳转换为日期时间对象。我正在使用 datetime.strptime 但根据 cProfile 的 cumtime 列,此函数使用了大量的 cputime。时间戳的格式为01/Nov/2010:07:49:33

当前函数是:

new_entry['time'] = datetime.strptime(
        parsed_line['day'] +
        parsed_line['month'] +
        parsed_line['year'] +
        parsed_line['hour'] +
        parsed_line['minute'] +
        parsed_line['second']
        , "%d%b%Y%H%M%S"
)

有人知道我可以如何优化吗?

【问题讨论】:

    标签: python optimization datetime


    【解决方案1】:

    如果这些是固定宽度格式,则无需解析行 - 您可以使用切片和字典查找直接获取字段。

    month_abbreviations = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4,
                           'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8,
                           'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
    year = int(line[7:11])
    month = month_abbreviations[line[3:6]]
    day = int(line[0:2])
    hour = int(line[12:14])
    minute = int(line[15:17])
    second = int(line[18:20])
    new_entry['time'] = datetime.datetime(year, month, day, hour, minute, second)
    

    Glenn Maynard 所示的方式进行测试表明这大约快了 3 倍。

    【讨论】:

    • 把它变成了一个函数,并在我的代码中测试了同样的 100 万条日志行,在这个和 strptime() 之间来回多次。总解析时间从 80 秒降至 50 秒!
    • 很好的解决方案。如果我有几个小时的 12 小时格式,你能否建议我该怎么办。除了设置条件并手动执行之外,还有其他方法可以处理吗?
    • @Naman 您可以添加 am_pm_offset={'AM':0,'PM':12} 并将其添加到小时数中。
    • @MarkRansom 很抱歉这么晚才回来,但添加偏移量不起作用。从下午 12 点 45 分开始是有效时间,但 24:45 添加的偏移量不是。 en.wikipedia.org/wiki/12-hour_clock 。还有什么快速的方法吗?我不想设置条件
    • @Naman 你说得对,对不起,我自己没有想到这一点。您可以使用模运算来修复它:hour = int(line[12:14]) % 12 + am_pm_offset[??]
    【解决方案2】:

    在 Windows 平台上使用 strptime() 似乎使用了 Python 实现(Lib 目录中的 _strptime.py)。而不是C的。自己处理字符串可能会更快。

    from datetime import datetime
    import timeit
    
    def f():
        datetime.strptime ("2010-11-01", "%Y-%m-%d")
    
    n = 100000
    print "%.6f" % (timeit.timeit(f, number=n)/n)
    

    在我的系统上返回 0.000049,而

    from datetime import date
    import timeit
    
    def f():
        parts = [int (x) for x in "2010-11-01".split ("-")]
        return date (parts[0], parts[1], parts[2])    
    
    n = 100000
    print "%.6f" % (timeit.timeit(f, number=n)/n)
    

    返回 0.000009

    【讨论】:

      【解决方案3】:

      什么是“很多时间”? strptime 大约需要 30 微秒:

      from datetime import datetime
      import timeit
      def f():
          datetime.strptime("01/Nov/2010:07:49:33", "%d/%b/%Y:%H:%M:%S")
      n = 100000
      print "%.6f" % (timeit.timeit(f, number=n)/n)
      

      打印 0.000031。

      【讨论】:

      • 根据 cprofile,strptime 的累积时间是 38 CPU 秒。该程序的总时间为 154 CPU 秒。
      • 什么? (此评论的其余部分在这里,因为 StackOverflow 将其用户视为需要强制最小评论长度的孩子)
      • 他正在解析一个日志文件,strptime 占用了总执行时间 154 秒中的 38 秒。
      【解决方案4】:

      最新回答:如果直接改用strptime() 并没有提高运行时间,那么我怀疑这里实际上没有问题:您只是编写了一个程序,其中一个它在生活中的主要目的是多次调用strptime(),而且你已经把它写得很好——它所做的其他东西很少——以至于strptime()调用被完全允许支配运行时。我认为您可以将其视为成功而不是失败,除非您发现 (a) 某些 Unicode 或 LANG 设置使 strptime() 做额外的工作,或者 (b) 您调用它的频率超出了您的需要。当然,尝试对每个要解析的日期只调用一次。 :-)

      查看示例日期字符串后的后续回答:等等!坚持,稍等!为什么要解析行而不是仅使用格式字符串,例如:

      "%d/%b/%Y:%H:%M:%S"
      

      原始的即兴回答:如果月份是整数,您可以这样做:

      new_entry['time'] = datetime.datetime(
          int(parsed_line['year']),
          int(parsed_line['month']),
          int(parsed_line['day']),
          int(parsed_line['hour']),
          int(parsed_line['minute']),
          int(parsed_line['second'])
      )
      

      并避免创建一个大字符串只是为了使strptime() 再次将其拆分。我想知道是否有一种方法可以直接访问月份名称逻辑来进行文本转换?

      【讨论】:

      • 尝试不解析日期并让 strptime 根据您的编辑进行。运行时间没有太大区别...
      • 使用 strptime() 时,您应该只使用一个格式化字符串。这就是预期用途。
      • 好吧,我试着把那部分作为它自己的一组工作线程来加速它。我得到了我敢打赌大多数非线程大师在尝试这个时得到的结果......速度是原来的两倍;-)
      • striptime() 调用通常是一个非常快速和简单的调用,绝对任何你添加到它的开销只会减慢速度,这就是为什么我没有建议像缓存这样的东西,以防万一是重复的日期。我的意思是,毕竟它是用 C 而不是 Python 编写的。顺便说一句,线程更慢,因为标准 C Python 不是线程安全的,因此一次只能有一个线程可以运行 Python 代码(尽管许多线程可以安全地等待 I/O)。
      猜你喜欢
      • 1970-01-01
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 2022-10-20
      • 2017-08-29
      • 2014-12-11
      • 2021-07-07
      • 2012-06-20
      相关资源
      最近更新 更多