<jupyter_start><jupyter_text>2.1.구글 Colab 기반 이그나이트는 작성 시점을 기준으로 다음의 백엔드들을 지원한다.- backends from native torch distributed configuration: “nccl”, “gloo”, “mpi”- XLA on TPUs via pytorch/xla- using Horovod framework as a backend각 의미를 이해하기 위해 가능한 단순한 내용의 코드를 준비하고, 이를 구글 Colab 기반으로 동작시켜 본다.  2.1.2. GPU단 분산처리 구글 Colab은, 별도로 런타임 유형을 지정하지 않는 경우 GPU나 TPU가 없는 VM(Virtual Machine)이 기본 할당된다. GPU단에서 이그나이트를 사용해보기 위해서는, Colab 페이지의 상단메뉴에서 ‘런타임’ > ‘런타임 유형 변경’ 선택 시 나오는 대화상자에서, 하드웨어 가속기를 아래 그림에서처럼 GPU로 변경한다. 만일 가이드라인 문서에서 제시된 링크([2_1_2.ipynb](https://github.com/secutron/Practice_Ignite/blob/main/2_1_2.ipynb))를 통해 본 colab 페이지에 접근한 경우라면, 이미 GPU 사용으로 지정되어 있는 상태일 것이다. ~~마지막으로 사용한 런타임 유형 상태가 저장된다~~   2.1.2.1. 패키지 설치 먼저 현 시점 기준 Colab에서 제공하는 VM은, 이그나이트가 사전 설치되어 있지 않은 상태이다. 따라서 다음과 같이 이그나이트의 최신 version을 설치한다. 참고로 pip 명령어는 package installer for python)의 약자이며, 아래 명령문 실행 시 이그나이트의 pre-release를 PyPI(python package index)로부터 설치하게 된다.<jupyter_code>!pip install --pre pytorch-ignite<jupyter_output>Collecting pytorch-ignite
  Downloading pytorch_ignite-0.5.0.dev20210911-py3-none-any.whl (233 kB)
[?25l
[K     |█▍                              | 10 kB 19.0 MB/s eta 0:00:01
[K     |██▉                             | 20 kB 22.7 MB/s eta 0:00:01
[K     |████▏                           | 30 kB 13.1 MB/s eta 0:00:01
[K     |█████▋                          | 40 kB 9.8 MB/s eta 0:00:01
[K     |███████                         | 51 kB 5.5 MB/s eta 0:00:01
[K     |████████▍                       | 61 kB 6.1 MB/s eta 0:00:01
[K     |█████████▉                      | 71 kB 5.8 MB/s eta 0:00:01
[K     |███████████▎                    | 81 kB 6.5 MB/s eta 0:00:01
[K     |████████████▋                   | 92 kB 5.0 MB/s eta 0:00:01
[K     |██████████████                  | 102 kB 5.3 MB/s eta 0:00:01
[K     |███████████████▌                | 112 kB 5.3 MB/s eta 0:00:01
[K     |████████████████▉               | 122 kB 5.3 MB/s eta 0:00:01
[K     |██████████████████▎             | 133 k[...]<jupyter_text>결과에 따르면, 본 가이드라인 작성 시점의 최신 버전인 xxxx년 x월 xx일자 (예: 2021년 9월 10일자) 이그나이트 x.x.x (예: 0.5.0)가 설치 되었음을 알 수 있다..  2.1.1.2. 노드 1개, 노드 당 프로세스 수 1개 실행 설치가 완료되었다면, 아래의 코드를 실행한다. 코드에서 수행하는 작업은 아래와 같다.~~코드라인 27번부터 36번까지인 idist.Parallel 컨텍스트 매니징 방식만 주의해서 보자. 그 이외는 중요하지 않다~~  (라인 1)에서는 이그나이트의 distributed 패키지를 idist라는 이름으로 불러들이고, (라인 32-34)에서는 백엔드로 nccl를 할당하는 등의 설정 작업을 지정한 후, (라인 27)에서의 training 함수를, (라인 36-37)에서 컨택스트 매니징이 가능한 idist.Parallel을 이용하여 run 시킨다. (라인 27)의 training 함수는 몇 가지 정보를 출력한 후, 무의미한 작업을 반복하도록 작성되었다. 그리고 (라인 27)에서는 분산처리 설정에 해당하는 dist_configs 딕셔너리에 nproc_per_node 키에 해당하는 값으로 1를 설정하였다. Colab에서는 GPU를 1개만 지원하므로, 1개의 자식 프로세스를 생성(spawn)하여 처리를 진행하라는 명령이다.<jupyter_code>import ignite.distributed as idist

from functools import wraps
import time
import random

import torch

def fn_timer(function):
   @wraps(function)
   def function_timer(*args, **kwargs):
       t0 = time.time()
       result = function(*args, **kwargs)
       t1 = time.time()
       print (idist.get_rank(), " : Total time running %s: %s seconds"
              % (function.__name__, str(t1-t0)))
       return result
   return function_timer

@fn_timer
def torch_random_sort(n):
   for _ in range(n):
       x = torch.randn(3,4)
       x = x.to(idist.device())
       sorted, indices = torch.sort(x)

def training(local_rank, config, **kwargs):
   print(idist.get_rank(), ': run with config:', config, '- backend=', idist.backend())
   torch_random_sort(100000) # was 2500000

backend = 'nccl' #'gloo' # or "xla-tpu" or None
dist_configs = {'nproc_per_node': 1, "start_method": "fork"}  # or dist_configs = {...}
config = {'c': 12345}

with idist.Parallel(backend=backend, **dist_configs) as parallel:
   parallel.run(training, config, a=1, b=2)<jupyter_output>2021-09-11 02:50:14,653 ignite.distributed.launcher.Parallel INFO: Initialized distributed launcher with backend: 'nccl'
2021-09-11 02:50:14,655 ignite.distributed.launcher.Parallel INFO: - Parameters to spawn processes: 
	nproc_per_node: 1
	nnodes: 1
	node_rank: 0
	start_method: fork
2021-09-11 02:50:14,656 ignite.distributed.launcher.Parallel INFO: Spawn function '<function training at 0x7f3640329d40>' in 1 processes<jupyter_text>결과의 첫번 째 출력 항목은 아래와 같으며, distributed launcher가 nccl 백엔드로 초기화 되었음을 표시한다. ~~누군가의 실행에 따라 날짜 등은 계속 변한다. 그리고 이그나이트 버전 변경에 따라 표시되는 내용도 변할 수 있다.~~  >*2021-06-29 01:44:00,550 ignite.distributed.launcher.Parallel INFO: Initialized distributed launcher with backend: 'nccl'* 상기 ‘’’1.4 분산 딥러닝 기본 지식’’’ 항목에서 언급된 바와 같이 분산처리를 위해서는 컴퓨팅 코어 간 통신이 필요하며, 본 예제에서는 컴퓨팅 코어가 GPU인 경우이므로 nccl이 백엔드로 이용된 것이다. 그리고 만일 이 컴퓨팅 코어가 CPU인 경우 gloo나 mpi 백엔드 이용이 가능하다.  이와 관련해서는 DISTRIBUTED COMMUNICATION PACKAGE - TORCH.DISTRIBUTED 페이지의 [rule of thumb](https://pytorch.org/docs/stable/distributed.html) 항목을 참조한다. ~~분산처리의 역사만큼이나 다양한 분산처리 방식이 존재한다. TCP 등을 이용해 직접 프로세스간 통신을 처리하는 방법도 있지만, 하이레벨 관점에서 CPU는 gloo, GPU는 nccl, TPU는 xla 백엔드를 사용해야 한다고 생각하는 것이 정신건강에 좋다.~~  두번 째 출력 항목은 node 1개(nnodes: 1)에서, 노드 당 1개의 처리 작업 (nproc_per_node: 1)이 진행될 것임을 알려준다. >*2021-06-29 01:44:00,551 이그나이트.distributed.launcher.Parallel INFO: - Parameters to spawn processes:*  >>*nproc_per_node: 1*  	*nnodes: 1*  	*node_rank: 0*  	*start_method: fork*   세번 째 출력 항목은 각 프로세스 내에서 print문에 의한 출력과 측정된 소요 시간을 보여주고 있다. rank가 0번 하나로 표기되었고, 이는 프로세스 총 합이 1개임을 보여 준다.>*2021-06-29 01:44:00,552 이그나이트.distributed.launcher.Parallel INFO: Spawn function '' in 1 processes*  *0 : run with config: {'c': 12345} - backend= nccl*  *0  : Total time running torch_random_sort: 14.128706693649292 seconds* 그리고 마지막 출력 항목은 분산처리가 모두 끝났음을 알리고 있다.>*2021-06-29 01:44:26,624 이그나이트.distributed.launcher.Parallel INFO: End of run* 여기서 프로세스 시작을 알리는 시간(세번 째 출력 항목)이 01시 44분 00초이고, 분산처리가 모두 끝났음을 알리는 시간(마지막 출력 항목)은 01시 44분 26초임을 확인해 보자. 약 14초 정도의 시간이 소요되는 작업(training 함수)을 총 1번 진행하였으나, 전체 처리 시간은 약 26초이다. 만일 전체 처리 시간에 대해 의문을 가진다면, 기존 코드에서와 달리 GPU로 데이터 전달을 줄인 코드를 추가하고 처리 시간을 다시 측정해본다.   ~~처리 시간은 Colab에서 할당해주는 VM 종류에 따라 변할 수 있다~~<jupyter_code>@fn_timer
def torch_random_sort(n):
   x = torch.randn(3,4)
   x = x.to(idist.device())
   for _ in range(n):
       sorted, indices = torch.sort(x)

with idist.Parallel(backend=backend, **dist_configs) as parallel:
   parallel.run(training, config, a=1, b=2)<jupyter_output>2021-09-11 02:54:58,383 ignite.distributed.launcher.Parallel INFO: Initialized distributed launcher with backend: 'nccl'
2021-09-11 02:54:58,384 ignite.distributed.launcher.Parallel INFO: - Parameters to spawn processes: 
	nproc_per_node: 1
	nnodes: 1
	node_rank: 0
	start_method: fork
2021-09-11 02:54:58,389 ignite.distributed.launcher.Parallel INFO: Spawn function '<function training at 0x7f3640329d40>' in 1 processes<jupyter_text>출력을 살펴보면, 약 6.7초가 소요되는 작업을 1개의 프로세스에서 처리한 시간이 약 10초로, 전체적인 시간 변동이 발생하고 있다. >*2021-06-29 01:48:00,551 ignite.distributed.launcher.Parallel INFO: Spawn function '' in 1 processes*  *0 : run with config: {'c': 12345} - backend= nccl*  *0  : Total time running torch_random_sort: 6.692128693786271 seconds**2021-06-29 01:48:10,330 ignite.distributed.launcher.Parallel INFO: End of run* 즉 GPU나 TPU와 같은 가속장치로 데이터를 이동하기 과정에서 오버헤드가 발생하고, 이에 따라 처리시간이 변동할 수 있음을 알아야 한다.   2.1.2.3. 가용 GPU 수량보다 많은 프로세스를 생성 Colab으로 할당받은 GPU는 1장 뿐인데, 프로세스를 여러 개 만들어 실행하면 어떻게 될까? 궁금해할 분들을 위해 실행 결과를 보이면 다음과 같다. ~~에러 뿜뿜~~ 그리고 nproc_per_node를 8로 지정해도 결과는 ~에러 뿜품으로~~ 동일하다.<jupyter_code>dist_configs['nproc_per_node'] = 2

with idist.Parallel(backend=backend, **dist_configs) as parallel:
    parallel.run(training, config, a=1, b=2)<jupyter_output>2021-09-11 03:12:17,236 ignite.distributed.launcher.Parallel INFO: Initialized distributed launcher with backend: 'nccl'
2021-09-11 03:12:17,240 ignite.distributed.launcher.Parallel INFO: - Parameters to spawn processes: 
	nproc_per_node: 2
	nnodes: 1
	node_rank: 0
	start_method: fork
2021-09-11 03:12:17,246 ignite.distributed.launcher.Parallel INFO: Spawn function '<function training at 0x7f3640329d40>' in 2 processes<jupyter_text>여기에서 보이는 주요 오류 메시지는 다음과 같다.>*RuntimeError: NCCL error in: /pytorch/torch/lib/c10d/ProcessGroupNCCL.cpp:911, invalid usage, NCCL version 2.7.8ncclInvalidUsage: This usually reflects invalid usage of NCCL library (such as too many async ops, too many collectives at once, mixing streams in a group, etc).*  2.1.2.4. VM 리소스 확인 이제 상기 분산처리 작업을 진행한 Colab의 VM에 대해 확인해보자.<jupyter_code>!cat /proc/cpuinfo<jupyter_output>processor	: 0
vendor_id	: GenuineIntel
cpu family	: 6
model		: 85
model name	: Intel(R) Xeon(R) CPU @ 2.00GHz
stepping	: 3
microcode	: 0x1
cpu MHz		: 1999.999
cache size	: 39424 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat md_clear arch_capabilities
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
bogomips	:[...]<jupyter_text>상기 실험 시 Colab으로부터 할당받은 VM은 총 2개의 인텔 제온 프로세서가 탑재되어 있고, 각 프로세서 당 코어 수는 1개이다. GPU를 이용해 작업하였으므로 CPU 정보는 중요하지 않다. 다만 ‘’’2.1.1. CPU 단에서 분산처리'’’ 시 할당받은 VM은 2.30GHz 였는데, 이번에는 2.20GHz를 할당받았다는 차이가 존재한다.  ~~즉 VM 할당받을 때마다, 또는 런타임 변경 때마다 달라질 수 있다~~>*processor	: 0*  ...  *model name	: Intel(R) Xeon(R) CPU @ 2.20GHz*  ...  *cpu cores	: 1*  ...  *processor	: 1*  ...  *model name	: Intel(R) Xeon(R) CPU @ 2.20GHz*  ...  *cpu cores	: 1*  ...<jupyter_code>!nvidia-smi<jupyter_output>Sat Sep 11 03:16:13 2021       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.63.01    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 00000000:00:04.0 Off |                    0 |
| N/A   33C    P8    28W / 149W |      0MiB / 11441MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-------[...]<jupyter_text>할당된 VM은 NVidia Tesla T4를 포함하고 있음을 알 수 있다.>...  *|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |*  *| N/A   37C    P8    10W /  70W |      0MiB / 15109MiB |      0%      Default |*  ...  2.1.1.5. 결과 해석 이제까지 Colab 기반으로 GPU단에서 이그나이트를 이용해 작업을 처리하는 방식을 살펴보았다. 그리고 ‘’’2.1.2.4절의 VM 리소스 확인’’’ 항목에서, 분산처리를 위해 Colab으로부터 할당받은 VM에는 Tesla T4  GPU를 1장 포함하고 있었었음을 확인하였다.  분산처리에서는 여러 개의 프로세스를 생성하여 동시에 작업을 진행시키는 것이 중요하다. 여기서 동시에 작업을 진행시킨다는 의미는 동시에 작업을 하는 것처럼 보이는 것([concurrent computing](https://en.wikipedia.org/wiki/Concurrent_computing))이 아니라, 말 그대로 동시에 작업이 진행되는 것([parallel computing](https://en.wikipedia.org/wiki/Parallel_computing))을 말한다. 본 가이드라인에서 보이는 바와 같이 이그나이트 분산처리 프레임워크를 직접적으로 이용하는 경우, GPU 1장에서는 프로세스가 1개만 실행될 수 있다라고 생각해야 한다. 멀티코어(multi-core)인 CPU 에서와는 달리, 매니코어(mani-core)인 GPU에서는 시분할 스케쥴링을 통해 여러 개의 프로세스를 동시에 처리하지 않기 때문이다. ~~다만 분산처리에는 역사만큼이나 다양한 방식이 있어서, GPU에 탑재된 컴퓨팅 코어가 단순하기는 하지만, 이를 시분할로 이용하고자 하는 시도도 있다.~~ 따라서 이그나이트 경우에는, GPU에서 한 시점에 실행되는 프로세스는 코어당 1개가 아니라 보드 당 1개이다. ~~일단 그렇다고 생각하자~~ GPU가 여러 장 확보되는 경우, 상기 코드의 nproc_per_node 항목에 적절한 숫자를 할당해 주는 것만으로 코드를 재사용할 수 있다. 이는 이후 GCP 또는 OnPrem 환경을 다루는 항목에서 다시 설명한다.  그리고 아래 그림은 wandb(weight and bias) tool을 이용하여 추적한 GPU utilization 확인 결과이다.  Note: This is not an official [LG AI Research](https://www.lgresearch.ai/) product but sample code provided for an educational purposeauthor: <NAME>  email: <EMAIL> / <EMAIL><jupyter_code><jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13242756588639712, 'fraction_numerical': 0.06678670813553773, 'mean_word_length': 2.859444093493367, 'pattern_counts': {'":': 1, '<': 29, '<?xml version=': 0, '>': 40, 'https://': 5, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 6, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '398553', 'license': 'MIT', 'n_tokens_mistral': 7043, 'n_tokens_neox': 7736, 'n_words': 1540, 'path': '2_1_2.ipynb', 'repo_name': 'secutron/Practice_Ignite', 'size': '12249'}