2011-01-13 3 views
1

필자는 파이썬에 대한 실험을 시도하여 1 분 안에 정수에 하나를 더할 수있는 횟수를 알아 내려고했습니다. 두 컴퓨터가 CPU의 속도를 제외하고 동일하다고 가정하면, 이것은 문제의 컴퓨터에 대해 일부 CPU 작업이 얼마나 빨라질 수 있는지를 예측해야합니다.순수 파이썬에서이 루프를 빠르게 할 수 있습니까?

아래 코드는 위에 제시된 요구 사항을 충족하도록 설계된 테스트의 예입니다. 이 버전은 첫 번째 시도보다 약 20 % 빠르고 세 번째 시도보다 약 150 % 빠릅니다. 누구든지 1 분 안에 가장 많은 추가 기능을 얻는 방법에 대한 제안을 할 수 있습니까? 높은 숫자는 바람직합니다.

EDIT 1 :이 실험은 Python 3.1로 작성되었으며 네 번째 속도 향상 시도보다 15 % 빠릅니다. 대신 while 루프에서의 1True를 사용하는 관련된 :

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)) 

편집이 어떤 속도 차이가 없어야한다. 다음 실험은 그것들이 동일하다는 것을 증명합니다. 먼저 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) 

생성 파이썬 가상 머신 지침이 밝혀졌다 방법 코드가 실제로 컴파일하고 보여줍니다 다음과 같은 출력을 생성한다 코드를 실행합니다.

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 (바이트 코드)와 완전히 동일하므로 모두 루프 속도 차이없이 실행한다.

+1

코드가 파이썬 3임을 나타냅니다. – Day

+2

원시 연산 속도를 사용한다면 파이썬은 당신을위한 언어가 아닙니다. –

+1

벤치마킹 하네스가 여기에서 테스트되는 주요 것 같습니다. –

답변

2

FWIW가 20-25 % 향상되었지만 다른 사람들과 마찬가지로 정수를 증가시키는 파이썬이 아마도 최상의 벤치마킹 도구는 아니라고 제안 할 것입니다. 다음은 인터럽트 실행 subprocess 모듈을 사용 변종의

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 

:

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)) 
+0

고마워요! 놀라운 속도 향상이었습니다. '_thread' 모듈은 내가하는 것보다 더 잘 알고 있습니다. 그것은 가장 빠른 루프가 쓸 수있는 것입니다. 이제는 가비지 콜렉터를 끄면 속도가 빨라지는지 확인하기 위해'gc' 모듈을 던질 것입니다. –

+0

타이밍 함수의 끝에서'gc.disable()'을 시작하고'gc.enable()'을 실행하면 속도가 향상되지 않습니다. –

+1

@Noctis Skytower : CPython에서 객체는 일반적으로 참조 카운팅을 통해 관리됩니다. 'gc' 모듈은 참조 카운터에 의해 처리되지 않는 참조주기를 정리하는 데에만 관여합니다. 그래서 'gc'를 끈 것은 별 차이가 없었습니다. –

3

나는 코드를 파이썬 3.1.2에 my machinethe @Amber's one에 비해 거의 동일하지만, 지속적으로 더 나은 (~ 2 %) 결과를 볼 루프 :

#!/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)) 

이 ~ 내 컴퓨터에 signal.alarm()의 변형 20 % 이상 느립니다.

+0

감사! 시간이 너무 늦지 않고 게으른 게 아니라면, 나는 (Windows에서) 리눅스로 건너 뛰고 그것을 테스트 할 것이다. 어쩌면 내일. :-) –

+0

예, 'while 1'은 'while true'보다 약간 빠릅니다. – kindall

+1

@kindall - 흥미롭게도 위의 코드를 테스트 할 때 정확한 결과를 얻었습니다. 'True '는 약간 더 빨리 달렸다. 대부분 두 가지가 실제로 동일하다는 것을 보여주기 위해 주로 사용됩니다. – Amber

0

파이썬과 컴퓨터에 대해 더 많이 배우는이 연습은 만족 스러웠습니다.

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 
>>> 

편집 :까지만 위의 코드는 하나에서 실행되는 2.16 GHz의 CPU와 윈도우 프로 7을 실행

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)) 

가 IDLE 내에서 다음과 같은 출력을 생성 : 이것은 최종 프로그램입니다 핵심. 다음 프로그램은이 문제를 해결하기 위해 작성되었습니다.

#! /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) 
관련 문제