Очень медленная загрузка бинарных файлов в многопоточной программе Python на Raspberry Pi

Я пишу многопоточное приложение Python на Raspberry Pi 4, которому иногда необходимо загружать двоичные файлы порядка ~ 200 килобайт с сервера, которым во время тестирования является мой ноутбук в локальной сети. Я проверил, что эти файлы обслуживаются моим ноутбуком примерно за секунду с использованием Curl или вызова Python 3 CLI Requests.get на RPi, но внутри моего приложения вызов загрузки запросов зависает не менее чем на 2 минуты до завершения. Затронутый код находится здесь:

# requests current composite from server 
# args:     timestamp: timestamp of last update
# returns:  SUCCESS_RETURN if updated, NONE_RETURN otherwise, OFFLINE_RETURN on failure to connect

def getcomposite(self, timestamp=None):
    try:
        self.slplogger.info("Downloading composite for timestamp %s" % (dt.utcfromtimestamp(timestamp).strftime("%Y-%m-%d-%H:%M:%S") if timestamp else "None"))

        # HANGS HERE
        compositeresp = requests.post(SERVER_URL + "getcomposite", data={'mac' : self.mac, 'timestamp' : timestamp})

        self.slplogger.info("Downloaded new composite: %s" % str(compositeresp.text[:min(10, len(compositeresp.text))]))

        if compositeresp.text != NONE_RETURN and compositeresp.content:
            with self.compositelock:
                self.compositedata = np.load(BytesIO(compositeresp.content), allow_pickle=False)
                # compute new input norm for adding subsequent input
                self.compositenorm = np.mean(self.compositedata[:]['value'], dtype=int)
                self.emptycomposite = False
                self.slplogger.info("Set new composite: %s" % str(self.compositedata[:min(10, len(self.compositedata))]))
            return SUCCESS_RETURN
        return FAILURE_RETURN
    except requests.exceptions.ConnectionError:

        self.slplogger.info("Composite download failed. Unable to connect to server")
        return OFFLINE_RETURN

Здесь определяется поток, не являющийся демоном, который вызывает этот метод (COMPOSITE_POLL_INTERVAL равен 2 секундам):

# --------------------------------------------------------------------
#   CompositePollingThread - Thread superclass that periodically 
#   polls strangeloop server for new additions to the composite loop
# --------------------------------------------------------------------

class CompositePollingThread(Thread):

    # overloaded Thread constructor
    # args:     pedal: parent Pedal object that instantiated this thread

    def __init__(self, pedal):
        Thread.__init__(self)
        self.stop = Event()
        self.pedal = pedal
        self.timestamp = None

        self.pedal.slplogger.debug("Initialized composite polling thread")

    # main thread execution loop

    def run(self):

        self.pedal.slplogger.debug("Started composite polling thread")

        while self.pedal.running:
            time.sleep(COMPOSITE_POLL_INTERVAL)

            # timestamp to determine whether any new data needs to be downloaded
            if not self.pedal.recording and self.pedal.getcomposite(timestamp=self.timestamp) == SUCCESS_RETURN:
                self.timestamp = dt.utcnow().timestamp()

                self.pedal.slplogger.debug("Downloaded new composite at %s" % dt.utcfromtimestamp(self.timestamp).strftime("%Y-%m-%d-%H:%M:%S"))

        self.pedal.slplogger.debug("Ended composite polling thread")

Я предполагаю, что эта медленная загрузка вызвана проблемой с потоками в программе. Ему также поручена обработка ввода в реальном времени, которая занимает большую часть ЦП. Могу ли я что-нибудь сделать, чтобы улучшить эту скорость загрузки? Есть ли способ дать потоку больший приоритет, или мне следует переключиться на модуль многопроцессорности, чтобы воспользоваться преимуществами нескольких ядер RPi 4?


person kid-c-plus    schedule 07.12.2020    source источник
comment
Вы пытались запустить тот же вызов requests.post(...) в отдельной программе без потоков? Возможно, requests делает что-то, что не нравится серверу, независимо от потоков.   -  person user4815162342    schedule 07.12.2020
comment
Не в отдельной программе, но я запустил эту строку request.post в интерфейсе командной строки Python, и она завершилась менее чем за секунду. Я также проверил отсутствие разницы в производительности между request.get и request.post, хотя последний обычно не используется для больших загрузок.   -  person kid-c-plus    schedule 07.12.2020


Ответы (1)


В итоге проблема была на сервере. Я изменил ответ конечной точки с flask.send_file с оболочкой BytesIO вокруг данных, которые мне нужны, на простой объект flask.Response с данными в виде текста (аналогично Обслуживать изображение, хранящееся в столбце SQLAlchemy LargeBinary), и теперь оно загружается менее чем за десять секунд. Я не знаю, почему раньше загрузка занимала так много времени только при доступе через программу (в отличие от запросов CLI), но это изменение позаботилось об этом.

person kid-c-plus    schedule 07.12.2020