Можно ли ускорить этот цикл в чистом Python? - PullRequest
1 голос
/ 13 января 2011

Я пытался провести эксперимент с Python, пытаясь выяснить, сколько раз он мог добавить единицу к целому числу за одну минуту.Предполагая, что два компьютера одинаковы, за исключением скорости ЦП, это должно дать оценку того, как быстро могут выполняться некоторые операции ЦП для рассматриваемого компьютера.

Приведенный ниже код является примером теста, предназначенного длявыполнить требования, указанные выше.Эта версия примерно на 20% быстрее, чем с первой попытки, и на 150% быстрее, чем с третьей попытки.Кто-нибудь может сделать какие-либо предложения относительно того, как получить наибольшее количество дополнений за минуту?Желательны более высокие числа.

РЕДАКТИРОВАТЬ 1: Этот эксперимент написан на Python 3.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))

EDIT 2: Относительно использования True вместо 1 в цикле while: не должно быть разницы в скорости.Следующий эксперимент доказывает, что они одинаковы.Сначала создайте файл с именем 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         

Излучаемые PVMI (байтовые коды) абсолютно одинаковы, поэтому оба цикла должны работать без разницы в скорости.

Ответы [ 3 ]

3 голосов
/ 13 января 2011

Я вижу почти такие же, но неизменно лучшие (~ 2%) результаты, чем @ Amber's one на моей машине на Python 3.1.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

Вот вариант, использующий модуль subprocess для запуска прерываемого цикла:

#!/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% медленнее, чем вариант signal.alarm() на моей машине.

2 голосов
/ 13 января 2011

Около 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))
0 голосов
/ 13 января 2011

Это упражнение по изучению 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 ГГц в IDLE получен следующий вывод:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...