2015-10-05 2 views
3

Я использую этот код (очень упрощенная версия оригинальной, но проблема остается), чтобы скопировать файл:копия файла слишком медленно, когда PyQt участвует

def copyfileobj_example(source, dest, buffer_size=1024*1024): 
    while 1: 
     copy_buffer = source.read(buffer_size) 
     if not copy_buffer: 
      break 
     dest.write(copy_buffer) 

Если я вызываю функцию без PyQt файлы копируются НАСТОЯЩЕМУ быстро, но когда я вызываю его внутри простого окна pyqt, копия в три раза медленнее.

Скопировать быстро огромное количество файлов является основной точкой приложения, я предполагаю, что включение gui немного замедлит работу, но не сделает его в три раза медленнее! и выполнение функции копирования с использованием потоков или многопроцессорности не вызывает удовлетворительных улучшений.

Это как есть? не могли бы вы рекомендовать что-то решить эту проблему?

EDIT: There является сутью с моим фактическим кодом копирования, работает и без PyQT

+0

Просьба представить полный PyQT пример, демонстрирующий эту проблему. Я не могу воспроизвести это с помощью PyQt-4.11.4 с помощью python 3.5 в Linux. – ekhumoro

+0

Добавлен тестовый код с использованием PyQT 5.4.1 и python 2.7 – Cesar

+0

У меня возникли проблемы с запуском вашего примера. Пожалуйста, попробуйте тест в моем ответе и confrim, видите ли вы ту же проблему. – ekhumoro

ответ

5

Это, вероятно, является эффект GIL. Когда PyQt работает в потоке пользовательского интерфейса, он «крадет» GIL каждый раз, когда ему приходится обрабатывать события. Это означает, что ваш цикл выше останавливается каждый раз. Это также происходит, когда вы работаете в другом потоке; замок глобальный.

Обходные:

  1. Используйте больший буфер. C-слой Python не влияет на GIL, поэтому, если вы копируете большие объемы данных, команды в цикле выполняются реже.
  2. Выполнение внешней команды для копирования (возможно, другого процесса Python).
  3. Используйте классы ввода-вывода Qt, чтобы скопировать файл, так как они не подвержены воздействию GIL (Kudos to ekhumoro для этой идеи).
  4. Напишите фрагмент кода в C, который передает данные.
  5. Используйте версию Python, которая не имеет GIL, такого как IronPython или Jython.
+0

Вы имеете в виду, что если я напишу функцию копирования как расширение переменного тока, я могу решить проблему? – Cesar

+1

@ Цезарь. Прежде чем отправиться в погоню за дикими гусями, я должен указать, что я не могу воспроизвести вашу проблему. Используя PyQt-4.11.4 в Linux, операция копирования выполняется в одно и то же время. Я серьезно сомневаюсь, что это имеет какое-либо отношение к GIL, так как операции с привязкой IO полностью не затронуты им. Но в любом случае, если вы хотите исключить питон из уравнения, просто выполните операцию копирования с использованием классов Qt IO. – ekhumoro

+0

@ekhumoro: Хорошее предложение об классах ввода-вывода. Цикл настолько прост, что GIL - это единственное, что может вызвать эффект. Обратите внимание, что в Windows Qt должен обработать множество событий, чтобы сделать ОС счастливой. –

2

Поскольку я не могу получить связанный код в вопросе, чтобы работать (он просто зависает и использует 100% -ный процессор), я отправлю более простой пример для целей тестирования.

Используя тестовый пример ниже, я получаю следующий результат при копировании файла размером 400 мегабайт (три трассы):

$ python copy_test.py 
2.9546546936035156 
2.9658050537109375 
$ python copy_test.py 
3.226983070373535 
3.192814826965332 
$ python copy_test.py 
2.935734748840332 
2.8552770614624023 

Как вы можете видеть, что нет существенной разницы. Для ясности, это с помощью следующей установки на Linux:

Python 3.5.0, Qt 5.5.0, PyQt 5.5 

и я получить аналогичные результаты со всеми комбинациями Python 2.7/3.5, 4/5 PyQt.

Вот тестовый пример:

import sys 
import time 
import os 

SRC_FILE = '/home/tmp/source/test/test.zip' 
DEST_FILE = '/home/tmp/source/test/test-copy.zip' 

def copy_file(src, dst=[], progress=None, only_new_file=True): 
    size = 1024 * 1024 
    with open(src, 'rb') as s, open(dst[0], 'wb') as d: 
     while 1: 
      copy_buffer = s.read(size) 
      if not copy_buffer: 
       break 
      d.write(copy_buffer) 

if __name__ == '__main__': 

    initTime = time.time() 
    copy_file(SRC_FILE, [DST_FILE], only_new_file=False) 
    print (time.time() - initTime) 

    time.sleep(5) 

    from PyQt5.QtWidgets import QApplication, QWidget 
#  from PyQt4.QtGui import QApplication, QWidget 

    app = QApplication(sys.argv) 

    w = QWidget() 
    w.resize(250, 150) 
    w.move(300, 300) 
    w.setWindowTitle('Simple') 
    w.show() 

    initTime = time.time() 
    copy_file(SRC_FILE, [DST_FILE], only_new_file=False) 
    print (time.time() - initTime) 

    sys.exit(app.exec_()) 
+0

Я не тороплюсь, чтобы установить новую и свежую систему, чтобы сделать некоторые тесты, я не знаю, что мешало мне в моей системе, теперь нет задержки делать обычную копию, но появляются, когда задействована многопроцессорность – Cesar

+1

@Cesar. Используя пример многопроцессорности, попробуйте использовать только строку импорта pyqt и ничего больше. Если вы все еще видите задержку, это должно доказать, что проблема каким-то образом вызвана многопроцессорностью. В этом случае я бы предложил вам переключиться на использование многопоточного подхода. Если вы используете 'QThread', это также должно сделать интеграцию с графическим интерфейсом намного проще. – ekhumoro

+0

есть !! замена многопроцессорной обработки потоками решает проблему задержки, но почему? Я использовал многопроцессорную систему, чтобы попытаться извлечь выгоду из многоядерных процессоров, и все идет хорошо, пока я не добавлю pyqt – Cesar

Смежные вопросы