在python中使用format-strings是很好的,简单和快速的.所以我从来没有考虑过这个操作的性能损失.前一段时间,我把我的程序从float数据类型切换到Decimal,以消除舍入错误.一些性能下降是预期的,它是好的.但我很惊讶地看到,仅仅打印一个格式化的Decimal数字日志(log)就带来了多大的性能损失.

下面我用cProfile个结果来说明差异. 问题是-有没有什么有效的方法在python中格式化Decimal个数字?

以下是浮点数格式的测试:

from cProfile import Profile

a_float = 1234567890.12345

def format_float(value: float) -> str:
    if value is None:
        return ''
    result = f"{value:+,.6f}"
    return result

def test_float():
    for i in range(1000000):
        b = format_float(a_float)

p = Profile()
p.runcall(test_float)
p.print_stats()

它提供的结果是:每0.839 seconds个函数调用中有0.839 seconds0002个

下面是同样的测试,测试Decimal分:

from decimal import Decimal
from cProfile import Profile

a_decimal = Decimal('1234567890.12345')

def format_decimal(value: Decimal) -> str:
    if value is None:
        return ''
    result = f"{value:+,.6f}"
    return result

def test_decimal():
    for i in range(1000000):
        b = format_decimal(a_decimal)

p = Profile()
p.runcall(test_decimal)
p.print_stats()

结果是:每27.739 seconds个函数中有55000002个函数调用

有没有办法在更短的时间内很好地格式化Decimal

comments 中的讨论带来了一个好pip --其他人没有这样的问题.在这里,我认为分析器的输出是有意义的.

对于float的情况,它相当短,以{method 'disable' of '_lsprof.Profiler' objects}结束,我认为这表明了一些优化:

>>> p.print_stats()
         1000002 function calls in 0.735 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1000000    0.564    0.000    0.564    0.000 <stdin>:1(format_float)
        1    0.171    0.171    0.735    0.735 <stdin>:1(test_float)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

而对于Decimal,有很多行:

>>> p.print_stats()
         47000029 function calls in 23.731 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1000000    0.625    0.000   23.395    0.000 <stdin>:1(format_decimal)
        1    0.335    0.335   23.731   23.731 <stdin>:1(test_decimal)
  1000000    0.947    0.000    1.795    0.000 _pydecimal.py:2622(_rescale)
  1000000    3.218    0.000   22.771    0.000 _pydecimal.py:3758(__format__)
  1000000    0.533    0.000    0.665    0.000 _pydecimal.py:3844(_dec_from_triple)
        1    0.000    0.000    0.000    0.000 _pydecimal.py:3902(__init__)
        5    0.000    0.000    0.000    0.000 _pydecimal.py:3938(_set_integer_check)
        2    0.000    0.000    0.000    0.000 _pydecimal.py:3952(_set_signal_dict)
        9    0.000    0.000    0.000    0.000 _pydecimal.py:3963(__setattr__)
  1000000    0.294    0.000    0.405    0.000 _pydecimal.py:448(getcontext)
  1000000    1.974    0.000    3.767    0.000 _pydecimal.py:6188(_parse_format_specifier)
  1000000    0.870    0.000    0.988    0.000 _pydecimal.py:6268(_format_align)
  1000000    1.734    0.000    1.822    0.000 _pydecimal.py:6295(_group_lengths)
  1000000    6.271    0.000   10.549    0.000 _pydecimal.py:6318(_insert_thousands_sep)
  1000000    0.236    0.000    0.236    0.000 _pydecimal.py:6355(_format_sign)
  1000000    1.390    0.000   13.162    0.000 _pydecimal.py:6365(_format_number)
  3000000    0.466    0.000    0.466    0.000 _pydecimal.py:820(__bool__)
  1000000    0.132    0.000    0.132    0.000 {built-in method __new__ of type object at 0x7e4f98d56d40}
        7    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
 16000000    0.979    0.000    0.979    0.000 {built-in method builtins.len}
  4000000    0.732    0.000    0.732    0.000 {built-in method builtins.max}
  4000000    0.524    0.000    0.524    0.000 {built-in method builtins.min}
        1    0.000    0.000    0.000    0.000 {built-in method fromkeys}
  4000000    0.277    0.000    0.277    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'copy' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  1000000    0.112    0.000    0.112    0.000 {method 'get' of '_contextvars.ContextVar' objects}
  1000000    0.613    0.000    0.613    0.000 {method 'groupdict' of 're.Match' objects}
  1000000    0.289    0.000    0.289    0.000 {method 'join' of 'str' objects}
  1000000    1.180    0.000    1.180    0.000 {method 'match' of 're.Pattern' objects}
        1    0.000    0.000    0.000    0.000 {method 'set' of '_contextvars.ContextVar' objects}

Suramuthu R这里运行的快速测试here中,我看到他的Decimal测试与我的float测试类似,并以行结束:

{method 'disable' of '_lsprof.Profiler' objects}

在我的电脑上,似乎关闭了对Decimal的一些优化. 有谁知道怎么判断吗?

推荐答案

这种情况发生在Arch存储库提供的版本中,因为它强制在编译Python(see Python PKGBUILD)时使用安装在系统上的几个库,其中包括libmpdec.

因此,原因是缺少可选的mpdecimal依赖项,它的缺失迫使Python使用Decimal的Python实现,正如yuri kilochek在注释中正确地指出的那样:

...问题是您的配置使用的是纯十进制数 出于某种原因实现(您可以在 统计数据),而它应该使用C实现.

enter image description here

安装mpdecimal包可以解决这个问题:

sudo pacman -S install mpdecimal

enter image description here

比同一系统中的float个 case ,1000002 function calls in 1.104 seconds个 case 更快.

Python相关问答推荐

更改matplotlib彩色条的字体并勾选标签?

如何计算两极打印机中 * 所有列 * 的出现次数?

由于NEP 50,向uint 8添加-256的代码是否会在numpy 2中失败?

如何在箱形图中添加绘制线的传奇?

将数据框架与导入的Excel文件一起使用

用Python解密Java加密文件

C#使用程序从Python中执行Exec文件

Python解析整数格式说明符的规则?

启用/禁用shiny 的自动重新加载

UNIQUE约束失败:customuser. username

Python中的变量每次增加超过1

CommandeError:模块numba没有属性generated_jit''''

Pandas:计算中间时间条目的总时间增量

具有相同图例 colored颜色 和标签的堆叠子图

递归函数修饰器

Python类型提示:对于一个可以迭代的变量,我应该使用什么?

如何在基于时间的数据帧中添加计算值

合并Pandas中的数据帧,但处理不存在的列

Fake pathlib.使用pyfakefs的类变量中的路径'

如何在开始迭代自定义迭代器类时重置索引属性?