在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
的一些优化.
有谁知道怎么判断吗?