betterway64 concerrent.futures


本格的な並列性を向上させるにはconcurrentを使用します。futuresを使用します。


  • Pythonではc言語を用いて高い演算性能を実現するのはよいが,コストが高い.

  • Pythonの同時futures組み込みモジュールで使用できるマルチプロセッサ組み込みモジュールは、難題を解決する方法である可能性があります.

  • Threadpool ExecuterモジュールはCPUコアを利用することができる.
    -メイン割り込みプログラムとサブプロセスは別々に実行されます.

  • たとえば,複数のCPUコアを用いてPython方式で実行するには,大量の計算が必要なタスクが必要である.
    ・大量計算を必要とするアルゴリズムの代わりに、ここでは2つの最大公約数(GCD)を求める実装が用いられる.
  • # mymodule.py
    def gcd(pair):
        a, b = pair
        low = min(a, b)
        for i in range(low, 0, -1):
            if a % i == 0 and b % i == 0:
                return i
        assert False, '도달할 수 없음'
    #기존 순차적 계산
    # run_serial.py
    import my_module
    import time
    
    NUMBERS = [
        (1963309, 2265973), (2030677, 3814172),
        (1551645, 2229620), (2039045, 2020802),
        (1823712, 1924928), (2293129, 1020491),
        (1281238, 2273782), (3823812, 4237281),
        (3812741, 4729139), (1292391, 2123811),
    ]
    
    def main():
        start = time.time()
        results = list(map(my_module.gcd, NUMBERS))
        end = time.time()
        delta = end - start
        print(f'총 {delta:.3f} 초 걸림')
    
    
    if __name__ == '__main__':
        main()
    全部で1.301秒かかります

    ThreadPoolExecutorクラスの使用

    # run_threads.py
    import my_module
    from concurrent.futures import ThreadPoolExecutor
    import time
    
    NUMBERS = [
        (1963309, 2265973), (2030677, 3814172),
        (1551645, 2229620), (2039045, 2020802),
        (1823712, 1924928), (2293129, 1020491),
        (1281238, 2273782), (3823812, 4237281),
        (3812741, 4729139), (1292391, 2123811),
    ]
    
    def main():
        start = time.time()
        pool = ThreadPoolExecutor(max_workers=2)
        results = list(pool.map(my_module.gcd, NUMBERS))
        end = time.time()
        delta = end - start
        print(f'총 {delta:.3f} 초 걸림')
    
    if __name__ == '__main__':
        main()
    全部で1.292秒かかります
    私のパソコンの標準は
  • 向上しましたが、あまり違いはなく、本にはかえって長い時間がかかりました.
  • の起動と通信スレッドプールに時間がかかるためです.
  • ProcessPoolExecutorに変更すると、より高速です。


    ProcessPoolExecutorクラスは、プロセスプールを使用して呼び出しを非同期で実行するExecutorサブクラスです.ProcessPoolExecutorは、マルチプロセッサモジュールを使用します.これは、グローバルエージェントの使用を避けることができますが、スワップ可能なオブジェクトのみを実行および戻すことができます.
  • グローバルエージェントロック
    -CPython interpreterが使用するメカニズムで、一度に1つのスレッドだけがPythonバイトコードを実行することを確保します.オブジェクトモデル(dictなどの重要な組み込みコンポーネントを含む)の同時アクセスに対するデフォルトのセキュリティを使用することで、CPythonの実装を簡素化します.
  • multiprocessing
    -マルチハンドラ・パッケージは、地域性とリモート同期性を提供し、スレッドではなくサブプロセスを使用してグローバル・エージェントを効果的に回避します.
    したがって、マルチプロセッサモジュールは、プログラマが所定のマシン上で複数のプロセッサを十分に利用することを可能にする.
  • プロセスとThreadの違い

  • プロセスとスレッドの根本的な違いは、プロセスがオペレーティングシステムによって独立した時間および空間リソースを割り当てて実行され、スレッドが1つのプロセス内で大量のリソースを共有し、並列に実行されることである.他の違いはすべてこの根本的な違いに由来している.
  • から派生した様々な違いは以下の通りである.
  • まず、プロセスはより独立しています.割り当てられたリソースが必要でない場合は、他のプロセスに影響を及ぼさずに実行されます.逆に、スレッドはプロセスのサブセットであり、複数のスレッドは同じプロセスリソースを共有するため、独立していません.同様に、プロセスは、リソースを持つ独立したアドレス空間を有するが、スレッドはアドレス空間を共有する.
  • # run_parallel.py
    import my_module
    from concurrent.futures import ProcessPoolExecutor
    import time
    
    NUMBERS = [
        (1963309, 2265973), (2030677, 3814172),
        (1551645, 2229620), (2039045, 2020802),
        (1823712, 1924928), (2293129, 1020491),
        (1281238, 2273782), (3823812, 4237281),
        (3812741, 4729139), (1292391, 2123811),
    ]
    
    def main():
        start = time.time()
        pool = ProcessPoolExecutor(max_workers=2) # 이 부분만 바꿈
        results = list(pool.map(my_module.gcd, NUMBERS))
        end = time.time()
        delta = end - start
        print(f'총 {delta:.3f} 초 걸림')
    
    if __name__ == '__main__':
        main()
    合計0.822秒
  • 親プロセスと子プロセスの間でデータを往復転送する場合、常にシリアル化と逆シリアル化が必要であるため、ProcessPoolExecutorによって複数の処理モジュールを使用する追加コストは非常に高い
  • である.

    この方法はよく隔離されており、レバーの大きいタイプに適しています。

  • 分離とは、プログラムの他の部分と状態を共有する必要がない関数
  • を指す.
  • レバレッジとは、親プロセスと子プロセスとの間で交換するデータ量が小さいが、子プロセスが計算する計算量が非常に大きい場合、
  • である.
  • の計算を行う必要がある場合、これらの特徴がなければ、計算速度はあまり速くない可能性があります.
  • は、最初にマルチプロセッサを用いずにプログラミングすることができ、速度を向上させるために実行することができる.