Рекурсивный поиск файлов с и без ThreadPoolExecutor имеет одинаковую производительность - PullRequest
0 голосов
/ 23 октября 2019

Я работаю над кроссплатформенным приложением в PyQT 5.13 с Python 3.6 / 7. В начале мое приложение должно рекурсивно искать некоторые файлы, скажем, они представляют собой txts, в определенной пользователем группе корневых путей (так, потенциально, во всех дисках системы). Я подумал, что было бы неплохо использовать ThreadPoolExecutor для распараллеливания сканирования из корневых папок, и в итоге я написал этот класс:

class StartupScanDispatcher(QThread):
  scanComplete = pyqtSignal(dict)

  @classmethod
  def __scantree(cls, path: str) -> List[DirEntry]:
      result: List[DirEntry] = []

      for entry in os.scandir(path):
          if entry.is_dir(follow_symlinks=False):
              try:
                  result.extend(cls.__scantree(entry.path))
              except Exception as e:
                  print("EXCEPTION: {}".format(e))
          else:
              # Get entry extension
              ext: str = os.path.splitext(entry.path)[1].lower()
              # Filter entry based on it's extension
              if ext == ".txt":
                  result.append(entry)

      return result

  def __init__(self, parent: QObject, paths: List[str]) -> None:
      super(StartupScanDispatcher, self).__init__(parent)

      self.__paths: List[str] = paths

  def run(self) -> None:
      # Not passing "max_workers", fallback to default amount that's <CPU_CORES> * 5
      with ThreadPoolExecutor() as executor:
          future2Path: Dict[Future, str] = {}
          path2List: Dict[str, List[str]] = {}

          for mPath in self.__paths:
              future2Path[executor.submit(
                  StartupScanDispatcher.__scantree,
                  mPath
              )] = mPath

          for future in as_completed(future2Path):
              path: str = future2Path[future]
              try:
                  path2List[path] = future.result()
              except Exception as e:
                  print("EXCEPTION: {}".format(e))

      self.scanComplete.emit(path2List)

«Проблема» заключается в том, что при измерениивремя выполнения этого сценария в моих двух средах разработки (Windows 10 64-битная с одним SSD и 2 HDD; Ubuntu 18.10 с SSD) кажется примерно идентичным времени, затраченному на замену всего блока with ThreadPoolExecutor() as executor этим простым циклом:

for mPath in self.__paths:
    StartupScanDispatcher.__scantree(mPath.getPath())

Мне было интересно, как это возможно: я сделал несколько ошибок при написании кода? Неправильно использовать ThreadPoolExecutor вместе с os.scandir? Что мне не хватает? Спасибо всем заранее.

...