在纯Python中可以加快这个循环吗?
我在用Python做一个实验,试图找出它能在一分钟内把一个整数加上多少次。假设除了CPU的速度外,两台计算机是相同的,这应该可以估计出某些CPU操作对相关计算机的速度 下面的代码是为满足上述要求而设计的测试示例。此版本比第一次尝试快20%,比第三次尝试快150%。有人能就如何在一分钟内获得最多的添加量提出建议吗?需要更高的数字 编辑1:这个实验是用Python3.1编写的,比第四次加速尝试快15%在纯Python中可以加快这个循环吗?,python,testing,performance,timing,Python,Testing,Performance,Timing,我在用Python做一个实验,试图找出它能在一分钟内把一个整数加上多少次。假设除了CPU的速度外,两台计算机是相同的,这应该可以估计出某些CPU操作对相关计算机的速度 下面的代码是为满足上述要求而设计的测试示例。此版本比第一次尝试快20%,比第三次尝试快150%。有人能就如何在一分钟内获得最多的添加量提出建议吗?需要更高的数字 编辑1:这个实验是用Python3.1编写的,比第四次加速尝试快15% def start(seconds): import time, _thread
def start(seconds):
import time, _thread
def stop(seconds, signal):
time.sleep(seconds)
signal.pop()
total, signal = 0, [None]
_thread.start_new_thread(stop, (seconds, signal))
while signal:
total += 1
return total
if __name__ == '__main__':
print('Testing the CPU speed ...')
print('Relative speed:', start(60))
编辑2:关于在while循环中使用True而不是1:应该没有速度差。下面的实验证明它们是相同的。首先,创建一个名为main.py的文件,并将以下代码复制到其中
def test1():
total = 0
while 1:
total += 1
def test2():
total = 0
while True:
total += 1
if __name__ == '__main__':
import dis, main
dis.dis(main)
运行代码应该会产生以下输出,显示代码实际是如何编译的,以及生成的Python虚拟机指令是什么
Disassembly of test1:
2 0 LOAD_CONST 1 (0)
3 STORE_FAST 0 (total)
3 6 SETUP_LOOP 13 (to 22)
4 >> 9 LOAD_FAST 0 (total)
12 LOAD_CONST 2 (1)
15 INPLACE_ADD
16 STORE_FAST 0 (total)
19 JUMP_ABSOLUTE 9
>> 22 LOAD_CONST 0 (None)
25 RETURN_VALUE
Disassembly of test2:
7 0 LOAD_CONST 1 (0)
3 STORE_FAST 0 (total)
8 6 SETUP_LOOP 13 (to 22)
9 >> 9 LOAD_FAST 0 (total)
12 LOAD_CONST 2 (1)
15 INPLACE_ADD
16 STORE_FAST 0 (total)
19 JUMP_ABSOLUTE 9
>> 22 LOAD_CONST 0 (None)
25 RETURN_VALUE
发出的PVMIs字节码完全相同,因此两个循环的运行速度应该没有任何差异。大约有20-25%的改进,FWIW-但是像其他人一样,我认为Python递增整数可能不是最好的基准测试工具
def start(seconds):
import time, _thread
def stop(seconds):
time.sleep(seconds)
_thread.interrupt_main()
total = 0
_thread.start_new_thread(stop, (seconds,))
try:
while True:
total += 1
except:
return total
if __name__ == '__main__':
print('Testing the CPU speed ...')
print('Relative speed:', start(60))
大约有20-25%的改进,FWIW——但和其他人一样,我认为Python递增整数可能不是最好的基准测试工具
def start(seconds):
import time, _thread
def stop(seconds):
time.sleep(seconds)
_thread.interrupt_main()
total = 0
_thread.start_new_thread(stop, (seconds,))
try:
while True:
total += 1
except:
return total
if __name__ == '__main__':
print('Testing the CPU speed ...')
print('Relative speed:', start(60))
我看到的代码几乎与Python 3.1.2上的结果相同,但始终比Python 3.1.2上的结果好~2%:
import signal
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
def jfs_signal(seconds):
# set signal handler
signal.signal(signal.SIGALRM, alarm_handler)
# raise Alarm in `seconds` seconds
signal.alarm(seconds)
total = 0
try:
while 1:
total += 1
finally:
signal.alarm(0) # disable the alarm
return total
以下是使用子流程模块运行可中断循环的变体:
#!/usr/bin/env python
# save it as `skytower.py` file
import atexit
import os
import signal
import subprocess
import sys
import tempfile
import time
def loop():
@atexit.register
def print_total():
print(total)
total = 0
while 1:
total += 1
def jfs_subprocess(seconds):
# start process, redirect stdout/stderr
f = tempfile.TemporaryFile()
p = subprocess.Popen([sys.executable, "-c",
"from skytower import loop; loop()"],
stdout=f, stderr=open(os.devnull, 'wb'))
# wait
time.sleep(seconds)
# raise KeyboardInterrupt
#NOTE: if it doesn't kill the process then `p.wait()` blocks forever
p.send_signal(signal.SIGINT)
p.wait() # wait for the process to terminate otherwise the output
# might be garbled
# return saved output
f.seek(0) # rewind to the beginning of the file
d = int(f.read())
f.close()
return d
if __name__ == '__main__':
print('total:', jfs_subprocess(60))
它比信号慢约20%。在我的机器上,alarm是一种变体。我看到的代码几乎与Python 3.1.2上的结果相同,但始终比Python 3.1.2上的结果好约2%:
import signal
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
def jfs_signal(seconds):
# set signal handler
signal.signal(signal.SIGALRM, alarm_handler)
# raise Alarm in `seconds` seconds
signal.alarm(seconds)
total = 0
try:
while 1:
total += 1
finally:
signal.alarm(0) # disable the alarm
return total
以下是使用子流程模块运行可中断循环的变体:
#!/usr/bin/env python
# save it as `skytower.py` file
import atexit
import os
import signal
import subprocess
import sys
import tempfile
import time
def loop():
@atexit.register
def print_total():
print(total)
total = 0
while 1:
total += 1
def jfs_subprocess(seconds):
# start process, redirect stdout/stderr
f = tempfile.TemporaryFile()
p = subprocess.Popen([sys.executable, "-c",
"from skytower import loop; loop()"],
stdout=f, stderr=open(os.devnull, 'wb'))
# wait
time.sleep(seconds)
# raise KeyboardInterrupt
#NOTE: if it doesn't kill the process then `p.wait()` blocks forever
p.send_signal(signal.SIGINT)
p.wait() # wait for the process to terminate otherwise the output
# might be garbled
# return saved output
f.seek(0) # rewind to the beginning of the file
d = int(f.read())
f.close()
return d
if __name__ == '__main__':
print('total:', jfs_subprocess(60))
它比信号慢约20%。警报在我的机器上是一种变体。这个关于学习更多Python和计算机的练习令人满意。这是最后一个节目:
def start(seconds, total=0):
import _thread, time
def stop():
time.sleep(seconds)
_thread.interrupt_main()
_thread.start_new_thread(stop, ())
try:
while True:
total += 1
except KeyboardInterrupt:
return total
if __name__ == '__main__':
print('Testing the CPU speed ...')
print('Relative speed:', start(60))
在Windows 7 Professional上使用2.16 GHz CPU运行时,会在空闲时间内产生以下输出:
Python 3.1.3 (r313:86834, Nov 27 2010, 18:30:53) [MSC v.1500 32 bit (Intel)]
on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
Testing the CPU speed ...
Relative speed: 673991388
>>>
编辑:上面的代码只在一个内核上运行。下面的程序就是为了解决这个问题而编写的
#! /usr/bin/env python3
def main(seconds):
from multiprocessing import cpu_count, Barrier, SimpleQueue, Process
def get_all(queue):
while not queue.empty():
yield queue.get()
args = seconds, Barrier(cpu_count()), SimpleQueue()
processes = [Process(target=run, args=args) for _ in range(cpu_count())]
for p in processes:
p.start()
for p in processes:
p.join()
print('Relative speed:', sorted(get_all(args[-1]), reverse=True))
def run(seconds, barrier, queue):
from time import sleep
from _thread import interrupt_main, start_new_thread
def terminate():
sleep(seconds)
interrupt_main()
total = 0
barrier.wait()
start_new_thread(terminate, ())
try:
while True:
total += 1
except KeyboardInterrupt:
queue.put(total)
if __name__ == '__main__':
main(60)
这个关于进一步学习Python和计算机的练习令人满意。这是最后一个节目:
def start(seconds, total=0):
import _thread, time
def stop():
time.sleep(seconds)
_thread.interrupt_main()
_thread.start_new_thread(stop, ())
try:
while True:
total += 1
except KeyboardInterrupt:
return total
if __name__ == '__main__':
print('Testing the CPU speed ...')
print('Relative speed:', start(60))
在Windows 7 Professional上使用2.16 GHz CPU运行时,会在空闲时间内产生以下输出:
Python 3.1.3 (r313:86834, Nov 27 2010, 18:30:53) [MSC v.1500 32 bit (Intel)]
on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>>
Testing the CPU speed ...
Relative speed: 673991388
>>>
编辑:上面的代码只在一个内核上运行。下面的程序就是为了解决这个问题而编写的
#! /usr/bin/env python3
def main(seconds):
from multiprocessing import cpu_count, Barrier, SimpleQueue, Process
def get_all(queue):
while not queue.empty():
yield queue.get()
args = seconds, Barrier(cpu_count()), SimpleQueue()
processes = [Process(target=run, args=args) for _ in range(cpu_count())]
for p in processes:
p.start()
for p in processes:
p.join()
print('Relative speed:', sorted(get_all(args[-1]), reverse=True))
def run(seconds, barrier, queue):
from time import sleep
from _thread import interrupt_main, start_new_thread
def terminate():
sleep(seconds)
interrupt_main()
total = 0
barrier.wait()
start_new_thread(terminate, ())
try:
while True:
total += 1
except KeyboardInterrupt:
queue.put(total)
if __name__ == '__main__':
main(60)
值得指出的是,代码是为Python 3编写的。如果您想要获得原始的算术速度,Python不是适合您的语言。这里测试的主要内容似乎是您的基准测试工具。估计一些CPU操作可能需要的速度以及如何编写可能被中断的快速循环,这在一起没有多大意义。我能看到一个或另一个。我不能两者都理解。请您更新这个问题以符合您的实际目标,好吗?在Python2.x上,while 1和while True并不等同:值得指出的是,代码是为Python3编写的如果您追求原始的算术速度,Python不是适合您的语言。看起来您的基准测试工具是这里测试的主要内容。估计一些CPU操作的速度以及如何编写可能中断的快速循环在一起没有多大意义。我能看到一个或另一个。我不能两者都理解。请您更新这个问题以符合您的实际目标,好吗?在Python2.x上,while 1和while True并不等同:谢谢!这是一个惊人的加速。你比我更了解线程模块。这大概是我们能写的最快的循环了。现在,我将加入gc模块,看看关闭垃圾收集器是否会加快速度。在计时函数开始时运行gc.disable,在结束时运行gc.enable不会产生明显的加速。@Noctis Skytower:在CPython中,对象通常通过引用计数进行管理。gc模块只参与清理引用计数器未处理的引用循环。这就是为什么关闭gc没有什么区别。遗憾的是,代码一次只测试一个内核。修订版应同时在所有CPU上运行。谢谢!这是一个惊人的加速。你比我更了解线程模块。这大概是我们能写的最快的循环了。现在,我将加入gc模块,看看关闭垃圾收集器是否会加快速度。在计时函数开始时运行gc.disable,在结束时运行gc.enable不会产生明显的加速。@Noctis Skytower:在CPython中,对象通常通过引用计数进行管理。gc模块只参与清理没有被处理的引用循环
参考计数器。这就是为什么关闭gc没有什么区别。遗憾的是,代码一次只测试一个内核。修订版应同时在所有CPU上运行。谢谢!如果时间不是那么晚,我也不是那么懒,我会从Windows跳转到Linux并测试它。也许明天吧-是的,我认为while 1比while True稍微快一些。@kindall-有趣的是,我在测试上述代码时得到了完全相反的结果。而True跑得稍微快一点。这主要是为了表明这两个事实上是相同的,这只是随机变化。谢谢!如果时间不是那么晚,我也不是那么懒,我会从Windows跳转到Linux并测试它。也许明天吧-是的,我认为while 1比while True稍微快一些。@kindall-有趣的是,我在测试上述代码时得到了完全相反的结果。而True跑得稍微快一点。这主要是为了表明这两个事实上是相同的,这只是随机变化。