python;从纪元到 weekday/weekend,分钟的超快速转换

python; super fast conversion from epoch to weekday/weekend, minute

我正在尝试将数百万个纪元转换为一个元组 (X,Y),其中 X(布尔值)是是否是周末,Y 是它在 (0,1440) 范围内的一天中的分钟数

转换为日期时间的简单、正确的方法:

def _epoch_to_dinfo(epoch):
    d = datetime.utcfromtimestamp(epoch) #SLOW AS F
    is_weekday = d.isoweekday() in range(1, 6)
    minute_of_day = d.hour*60 + d.minute
    return is_weekday, minute_of_day

太慢了。我正在寻找一个近似值;以下是我的最佳尝试:

def _epoch_to_dinfo(epoch):  
    #return (epoch / 86400) % 7 not in [2,3], (epoch % 86400) / 60
    days_since_epoch = epoch / 86400
    days_after_thursday = days_since_epoch % 7  #the epoch was a thursday
    is_weekday = days_after_thursday not in [2,3]
    minute_of_day = (epoch % 86400) / 60
    return is_weekday, minute_of_day

有更快的方法吗?

假设你真的需要速度,唯一可以节省的(在 CPython 中)是减少你正在执行的字节码量,甚至存储到本地也会花费额外的字节码工作(即使它对每个字节码指令没有做太多工作,简单地处理它们也会有开销)。所以基本上,通过像在注释掉的代码中一样将中间存储(以及字节代码)最小化(尽管在非常旧的 Python 上,您需要一个 tuple 常量用于 not in 检查以避免 Python 每次都愚蠢地重建一个 list):

def _epoch_to_dinfo(epoch):  
    return (epoch // 86400) % 7 not in (2, 3), (epoch % 86400) // 60

单单说,我的 Python 2.7 x86 安装中每个 运行 的成本下降了 ~23%。

您可能认为您可以使用 divmod 立即计算纪元除以 86400 的商和余数,但是从内置命名空间查找 divmod 的成本 (由于 LEGB search),调用它很昂贵(比 //% 等基于语法的调用要昂贵得多),解压其结果,并从堆栈中加载解压后的结果意味着它结束甚至比非单线解决方案的成本要高得多;除非输入足够大以至于实际完成的数学工作远远超过查找成本和函数调用开销(这通常意味着数字必须足够大才能调用基于数组的数学,然后再调用一些;在 Py2 中使用 long,或超过 digit 大小的 ints,对于 32 位和 64 位系统,在 Py3 中为 15 或 30 位),divmod 几乎从不节省时间。

类似地,not in (2, 3) 的测试胜过所有其他方法,不是因为它在逻辑上更快,而是因为它简化了常量 tupleLOAD_CONST 并调用 COMPARE_OP for not in(之后比较在C层完成);针对 2 和 3 的单独测试将加载更多常量,调用更多 COMPARE_OPs 并在 Python 字节代码中执行条件跳转等,这更昂贵。

None 此建议适用于除 CPython 之外的任何解释器(其中大部分可能仅适用于 CPython 2.7 ), 因为这都是实现细节。

你可以预先计算出所有的星期六和星期日,然后将它们放入字典中,使用纪元以来的天数作为关键字。然后你可以这样做:

saturdays = {d: True for d in range(2,5000,7)}  # pre-calculate
sundays = {d: True for d in range(3,5000,7)}
saturdays_and_sundays = {**saturdays, **sundays} # join dicts (Python 3.5+)

# in your function
days_since_epoch = epoch / 86400
minute_of_day = (epoch % 86400) / 60
if days_since_epoch in saturdays_and_sundays :
    return True, minute_of_day
return False, minute_of_day