7월 22일 - GIL

Yullgiii·2024년 7월 22일
0

GIL (Global Interpreter Lock) 과 그로 인한 성능 문제

GIL의 개념

GIL은 Global Interpreter Lock의 약자로, 여러 스레드가 동시에 실행되는 것을 방지하는 락이다. GIL은 인터프리터 레벨에서 사용되며, 어느 시점이든 하나의 바이트코드만 실행되도록 강제한다. 이는 각 스레드가 다른 스레드에 의해 GIL이 해제되길 기다린 후에야 실행될 수 있음을 의미하며, 본질적으로 멀티 스레드 프로그램이 싱글 스레드처럼 동작하게 만든다.

성능 문제

GIL로 인해 성능 문제가 대두되는 경우는 주로 CPU 연산이 많은 작업(CPU bound)을 멀티 스레드로 수행할 때다. 예를 들어, 압축, 정렬, 인코딩 같은 작업이 해당된다. 이러한 경우 GIL 때문에 멀티 스레드로 작업을 수행해도 싱글 스레드일 때와 성능 차이가 크지 않다.

해결 방법

이 문제를 해결하기 위해서는 다음과 같은 방법을 사용할 수 있다:
1. 멀티 스레드는 파일, 네트워크 IO 같은 IO bound 프로그램에 사용한다.
2. CPU bound 작업의 경우 멀티 스레드 대신 멀티 프로세스를 활용한다.

GIL의 장점

GIL에는 몇 가지 장점도 있다:
1. 구현 용이성: GIL을 활용한 멀티 스레드는 그렇지 않은 멀티 스레드보다 구현이 쉽다.
2. 메모리 관리: 레퍼런스 카운팅을 사용하는 메모리 관리 방식에서 GIL 덕분에 오버헤드가 적어, 싱글 스레드일 때 fine grained lock 방식보다 성능이 우월하다.
3. C extension 활용: C 라이브러리를 사용할 때 GIL이 해제되므로, C 라이브러리를 사용하는 CPU bound 프로그램을 멀티 스레드로 실행하는 경우 더 빠를 수 있다.

예제 코드

GIL로 인해 멀티 스레드의 성능이 제한되는 예제와 멀티 프로세스를 활용하여 성능을 개선하는 예제를 살펴보자.

멀티 스레드 예제

public class ThreadExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 100000; i++) {
                // CPU bound 작업 예제
                double result = Math.pow(i, 2);
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        long startTime = System.currentTimeMillis();
        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("Execution time with threads: " + (endTime - startTime) + " ms");
    }
}

멀티 프로세스 예제

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ProcessExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Runnable task = () -> {
            for (int i = 0; i < 100000; i++) {
                // CPU bound 작업 예제
                double result = Math.pow(i, 2);
            }
        };

        long startTime = System.currentTimeMillis();
        executor.submit(task);
        executor.submit(task);

        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("Execution time with processes: " + (endTime - startTime) + " ms");
    }
}

So...

GIL은 멀티 스레드 프로그램에서 성능 저하를 일으킬 수 있지만, 특정 상황에서는 유용하게 사용할 수 있다. CPU bound 작업에서는 멀티 프로세스를 활용하여 성능을 향상시킬 수 있으며, IO bound 작업에서는 멀티 스레드를 효과적으로 사용할 수 있다. GIL의 존재를 이해하고 적절한 방법을 선택하여 성능을 최적화하는 것이 중요하다.

profile
개발이란 무엇인가..를 공부하는 거북이의 성장일기 🐢

0개의 댓글