在纯Python中可以加快这个循环吗?

在纯Python中可以加快这个循环吗?,python,testing,performance,timing,Python,Testing,Performance,Timing,我在用Python做一个实验,试图找出它能在一分钟内把一个整数加上多少次。假设除了CPU的速度外,两台计算机是相同的,这应该可以估计出某些CPU操作对相关计算机的速度 下面的代码是为满足上述要求而设计的测试示例。此版本比第一次尝试快20%,比第三次尝试快150%。有人能就如何在一分钟内获得最多的添加量提出建议吗?需要更高的数字 编辑1:这个实验是用Python3.1编写的,比第四次加速尝试快15% def start(seconds): import time, _thread

我在用Python做一个实验,试图找出它能在一分钟内把一个整数加上多少次。假设除了CPU的速度外,两台计算机是相同的,这应该可以估计出某些CPU操作对相关计算机的速度

下面的代码是为满足上述要求而设计的测试示例。此版本比第一次尝试快20%,比第三次尝试快150%。有人能就如何在一分钟内获得最多的添加量提出建议吗?需要更高的数字

编辑1:这个实验是用Python3.1编写的,比第四次加速尝试快15%

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跑得稍微快一点。这主要是为了表明这两个事实上是相同的,这只是随机变化。