Python Multiprocessing(Process) 사용한 데이터 처리 속도 개선

 

·       Version : MAC OS, Python 3.X, PIP3

 

대용량 데이터를 효율적으로 처리하기 위해서는 병렬 처리를 활용하는것이 좋다.  파이썬에서 병렬처리를 제공하는 대표적인 라이브러리는 Threading Multiprocessing 모듈이다. Threading 모들은 파이썬의 GIL(Global Interpreter Lock)라는불리우는 잠금 모델을 사용하기 때문에I/O 작업이 아닌 CPU 작업이 많을 경우 오히려 성능이 저하된다. 방식은 Lock 풀고 스레드를 교환하고 다시 Lock 거는 형태의 멀티스레드이기 떄문이다.

파이썬에서는 Multiprocessing 권장하고 있으며,   모듈에는 대표적으로 Pool Process 있지만 이번 글에서는 Process 대해서 다루기로 한다.

 

·       Pool 사용한 처리 속도 개선 : https://sungwookkang.com/1478

 

Process 하나의 프로세스에 하나의 함수를 할당하여 실행한다. Target= 파라메터에 작업을 할당하고, args=(agr1, ) 인수를 할당하여 프로세스 객체를 생성한다. start() 프로세스를 시작하여 join()으로 프로세스의 종료를 기다린다.

import os

import multiprocessing as mp

from multiprocessing import Pool, Process

import threading

import time

import datetime

 

def multiprocess():

    start = int(time.time())

 

    ojbect_list = []

    for i in range(1,12):

        task = Process(target=work_func, args=(i,))

        ojbect_list.append(task)

        task.start()

 

    for task in ojbect_list:

        task.join()

   

    end = int(time.time())

    print("***run time(sec) : ", end-start)   

   

    print("Number of Core : " + str(mp.cpu_count()))

   

 

def work_func(x):

    print("time : " + str(datetime.datetime.today()) +  " value :" + str(x)  + " PID : "  + str(os.getpid()))

 

if __name__ == '__main__':

# execute only if run as a script

   

    multiprocess()

 

 

코드를 실행한 결과를 살펴보면 실행마다 다른 프로세스(PID 각각 다름)에서 실행된것을 있다.

 

Pool Process 차이점은, Pool 경우 실행되어야 작업이 코어수 만큼 분할되고 코어수 만큼 프로세스가 생성되어 힐당받은 작업을 처리하는데, Process 경우 작업마다 새로운 프로세스가 할당되어 작업을 처리한다.

 

 

2021-03-02/ Sungwook Kang / http://sungwookkang.com

 

파이선파이썬, python, 병렬처리, Multiprocessing, Python Multiprocessing, 파이썬 병렬처리, 데이터 처리, Data Processing

 

Python Multiprocessing(Pool) 사용한 데이터 처리 속도 개선

 

·       Version : MAC OS, Python 3.X, PIP3

 

대용량 데이터를 효율적으로 처리하기 위해서는 병렬 처리를 활용하는것이 좋다. 대부분의 머신러닝/딥러닝에 사용되는 프레임워크들은 함수 내부에서 병렬처리가 가능하도록 설계되어 있기 때문에 시스템의 자원을 효율적으로 사용하지만, 일반적으로 많이 사용되는 데이터 가공 모듈인 pandas 같은 모듈은 병렬처리를 기본적으로 제공하지 않기 떄분에 별도의 병렬처리가 가능하도록 코딩을 해야한다.

 파이썬에서 병렬처리를 제공하는 대표적인 라이브러리는 Threading Multiprocessing 모듈이다. Threading 모들은 파이썬의 GIL(Global Interpreter Lock)라는불리우는 잠금 모델을 사용하기 때문에I/O 작업이 아닌 CPU 작업이 많을 경우 오히려 성능이 저하된다. 방식은 Lock 풀고 스레드를 교환하고 다시 Lock 거는 형태의 멀티스레드이기 떄문이다.

파이썬에서는 Multiprocessing 권장하고 있으며,   모듈에는 대표적으로 Pool Process 있지만 이번 글에서는 Pool 대해서 다루기로 한다.

 

아래 실습코드는 1에서 12까지 숫자를 1 간격으로 출력하는데, for 문을 사용한 싱글처리와 Pool 사용하여 병렬 처리를 하였을때의 처리 시간 할당된 프로세스를 확인한다.

import os

import multiprocessing as mp

from multiprocessing import Pool

import threading

import time

import datetime

 

def non_multiprocess():

    print("non multiprocess")

    start = int(time.time())

   

    for i in range(1,12):

        work_func(i)

 

    end = int(time.time())

 

    print("Number of Core : " + str(mp.cpu_count()))

    print("***run time(sec) : ", end-start)   

 

def multiprocess():

    print("non multiprocess")

 

     #멀티 프로세싱을 위한 CPU 숫자 확인 만들기

    num_cores = mp.cpu_count()

    pool = Pool(num_cores)

 

    start = int(time.time())

 

    ojbect_list = []

    for i in range(1,100):

        ojbect_list.append(i)

    #멀티 프로세싱 워커 호출

    pool.map(work_func, ojbect_list)

 

    end = int(time.time())

 

    #메모리 방지 위해 사용

    pool.close()

    pool.join()

 

    print("Number of Core : " + str(mp.cpu_count()))

    print("***run time(sec) : ", end-start)   

 

def work_func(x):

    print("time : " + str(datetime.datetime.today()) +  "value :" + str(x)  + " PID : "  + str(os.getpid()))

   

    time.sleep(1)

 

if __name__ == '__main__':

# execute only if run as a script

    #non_multiprocess()

    multiprocess()

   

 

 

코드를 실행하면 아래와 같은 결과를 확인할 있다. non multiprocess 라고 되어 있는 결과는 for문을 사용한 싱글 프로세스이며, 개의 프로세스(PID : 49650) 사용되었으며, 순차적으로 실행되어 12초의 시간이 소요된 것을 확인할 있다. Multiprocess 경우 동시에 6개의 프로세스(필자의 테스트 컴퓨터는 6core이다.) 할당되었으며 PID 다른것을 확인할 있다. 전체 실행시간은 2초가 소요된 것을 확인할 있다.

 

간단한 테스트에서는 Multiprocessing 대한 효과를 크게 느끼지 못할수도 있으나 데이터가 기하급수적으로 커질때에는 데이터 처리 속도에 따른 시간이 엄청나게 차이난다.

 

 

2021-03-01/ Sungwook Kang / http://sungwookkang.com

 

파이선파이썬, python, 병렬처리, Multiprocessing, Python Multiprocessing, 파이썬 병렬처리, 데이터 처리, Data Processing

 

인덱스 리빌드 동작 (Gather Streams from SORT)

 

  • Version : SQL Server 2005, 2008, 2008R2, 2012, SQL2014

 

SQL Server에서는 인덱스 리빌드 작업을 통하여 조각난 인덱스를 다시 작성한다. SQL Server 데이터베이스 엔진에서는 기본 데이터에 삽입, 업데이트, 삭제 작업을 수행 할 때마다 인덱스를 자동으로 유지 관리한다. 이러한 수정이 빈번이 발생하면 시간이 흐름에 따라 인덱스의 정보가 조각화 되어 데이터베이스 내에 흩어지게 될 수 있다.

조각화는 인덱스의 논리적 페이지 순서가 데이터파일의 물리적 순서와 일치하지 않을 때 나타난다. 조각화가 심할 경우에는 쿼리의 성능이 저하될 수 있다.

 

이번 포스트는 CSS SQL Engineers에 게시된 내용으로 인덱스 리빌드 동안 발생하는 CPU 자원의 사용량에 대해서 알아본다. 필자가 읽고 이해한 내용을 정리하였으며 번역의 오류 및 기술적 오류 가능성이 있으므로 원문은 참고 하길 바란다.

 

64 CPU, 128 GB RAM 시스템에서 1조 행이 있는 테이블에 인덱스 리빌드 작업을 테스트 하였다. 인덱스 리빌드를 시작하였을 때 64개의 CPU 사용량이 100%였다. 그러나 일정 시간이 지나서 1개의 CPU만이 100% 사용되는 것을 확인 할 수 있다.

 

리빌드가 시작되면 64개의 작업단위로 나누어 각각의 nested loop와 sort 작업이 수행되며 마지막에 Gather Streams을 거쳐 인덱스의 인서트 과정이 진행된다. 실행 계획은 아래 그림과 같으며 Sort 작업 다음에 Gather Streams 작업에서 CPU Node 사용이 1로 된 것을 확인 할 수 있다.

 

 

 

소비자는 각 작업자에서 행을 가져와 메모리 트리를 유지 한다. 64개의 병렬 작업이 있을 경우 64개의 트리 항목이 있으며 MAXOP 16일 경우 트리는 16 항목이 포함된다. 4개의 프로세스가 있는 경우 다음과 같은 순서로 진행 된다.

1.Get Row From Worker/Partition #1 – Insert into tree

2.Get Row From Worker/Partition #2 – Insert into tree

3.Get Row From Worker/Partition #3 – Insert into tree

4.Get Row From Worker/Partition #4 – Insert into tree

5.While (entries in tree)

{

Output lowest, sorted value from tree

Get Row from Worker/Partition you just removed from tree as lowest value

}

 

 

 

리빌드가 진행 되는 동안 sys.dm_exec_requests, sys.dm_os_waiting_tasks를 확인해 보면 CPU 노드의 활동을 확인 할 수 있다. Sys.dm_os_wait_stats를 확인해 보면 CXPACKET 대기가 증가한 것을 볼 수 있는데 병렬 처리에서 나머지 작업이 완료되기를 기다리는 부분이다.

 

병렬계획을 최적화 하기 위해 MAXDOP를 조절하며 테스트를 진행한 결과이다. 많은 CPU를 사용할 때 더 빠른 작업을 나타내었지만 계획의 마지막 CXPACKET 대기는 DOP 수준으로 크게 변경되지는 않았다.

  • 64 CPUs = 01:50:00
  • 32 CPUs = 02:17:00
  • 16 CPUs = 03:16:00

 

테스트를 통하여 인덱스 리빌드는 병렬자원을 활용하는 것을 알 수 있으며 각 작업에 대해서는 많은 CPU를 사용할 때 더 빠른 작업을 나타내었다. 하지만 CXPACKET 대기에서는 큰 차이가 나지 않아 여러 쿼리가 실행되는 서버의 경우 MAXDOP를 조절하여 다른 서비스에 영향을 주지 않도록 하면 최적화된 리빌드 작업을 할 수 있을 듯 하다.

 

[참고자료]

  • How It Works: Behavior of a 1 Trillion Row Index Build (Gather Streams from SORT)

http://blogs.msdn.com/b/psssql/archive/2014/04/29/how-it-works-behavior-of-a-1-trillion-row-index-build-gather-streams-from-sort.aspx

 

 

 

 

 

2014-05-02 / 강성욱 / http://sqlmvp.kr

 

SQLSERVER, mssql, SQL튜닝, SQL강좌, DB튜닝, 쿼리튜닝, 데이터베이스튜닝, 인덱스리빌드, 병렬처리, CXPACKET

+ Recent posts