Programing

스레드를 만드는 데 비용이 많이 드는 이유는 무엇입니까?

lottogame 2020. 5. 27. 07:30
반응형

스레드를 만드는 데 비용이 많이 드는 이유는 무엇입니까?


자바 튜토리얼에서는 스레드를 만드는 것이 비싸다고 말합니다. 그러나 왜 정확히 비쌉니까? Java Thread가 생성 될 때 생성 비용이 많이 드는 상황은 정확히 무엇입니까? 나는 그 진술을 사실로 받아들이고 있지만 JVM에서 스레드 생성의 메커니즘에 관심이 있습니다.

스레드 수명주기 오버 헤드 스레드 생성 및 분해는 무료가 아닙니다. 실제 오버 헤드는 플랫폼마다 다르지만 스레드 작성에는 시간이 걸리므로 요청 처리에 지연 시간이 발생하며 JVM 및 OS의 일부 처리 활동이 필요합니다. 대부분의 서버 응용 프로그램 에서처럼 요청이 빈번하고 가벼운 경우 각 요청에 대해 새 스레드를 만들면 상당한 컴퓨팅 리소스가 소비 될 수 있습니다.

에서 연습 자바 동시성
으로 브라이언 게츠, 팀 Peierls, 조슈아 블로흐, 조셉 Bowbeer, 데이비드 홈즈, 더그 레아
ISBN-10을 인쇄 : 0-321-34960-1를


Java 스레드 작성은 비용이 많이 듭니다.

  • 스레드 스택에 대해 큰 메모리 블록을 할당하고 초기화해야합니다.
  • 호스트 OS에 원시 스레드를 작성 / 등록하려면 시스템 호출이 필요합니다.
  • 설명자는 작성, 초기화 및 JVM 내부 데이터 구조에 추가되어야합니다.

스레드가 살아있는 한 리소스를 묶는다는 점에서 비싸다. 예를 들어 스레드 스택, 스택에서 도달 할 수있는 모든 객체, JVM 스레드 설명자, OS 기본 스레드 설명자

이 모든 것들의 비용은 플랫폼에 따라 다르지만 내가 본 Java 플랫폼에서는 저렴하지 않습니다.


Google 검색에서 2002 빈티지 Linux를 실행하는 2002 빈티지 듀얼 프로세서 Xeon의 Sun Java 1.4.1에서 초당 ~ 4000 스레드 생성 속도를보고 하는 오래된 벤치 마크발견했습니다 . 더 현대적인 플랫폼은 더 나은 숫자를 줄 것이며 ... 방법론에 대해서는 언급 할 수 없지만 ... 적어도 스레드 생성 비용얼마나 드는지에 대한 야구장을 제공 합니다.

Peter Lawrey의 벤치마킹에 따르면 요즘 스레드 생성이 절대적으로 훨씬 빠르지 만 Java 및 / 또는 OS의 개선 또는 프로세서 속도의 향상이 어느 정도인지는 확실하지 않습니다. 그러나 스레드 풀을 사용할 때마다 매번 새 스레드를 작성 / 시작하면 그의 숫자는 여전히 150 배 이상 향상되었습니다. (그리고 그는 이것이 모두 상대적이라는 것을 강조합니다 ...)


(위의 내용은 "그린 스레드"가 아닌 "네이티브 스레드"를 가정하지만 최신 JVM은 모두 성능상의 이유로 네이티브 스레드를 사용합니다. 그린 스레드는 훨씬 저렴하지만 다른 지역에서는 비용을 지불합니다.)


Java 스레드의 스택이 실제로 어떻게 할당되는지 확인하기 위해 약간의 파기를 수행했습니다. Linux에서 OpenJDK 6의 경우 스레드 스택은 pthread_create기본 스레드를 작성하는 호출에 의해 할당됩니다 . (JVM은 pthread_create사전 할당 된 스택을 전달하지 않습니다 .)

그런 다음 pthread_create스택 에서 mmap다음과 같이 호출하여 할당됩니다 .

mmap(0, attr.__stacksize, 
     PROT_READ|PROT_WRITE|PROT_EXEC, 
     MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)

에 따르면 man mmap, MAP_ANONYMOUS플래그는 메모리가 0으로 초기화되도록한다.

따라서 (Java 사양에서) 새로운 Java 스레드 스택이 제로화 될 필요는 없지만 실제로는 (적어도 Linux에서는 OpenJDK 6에서는) 제로화됩니다.


다른 사람들은 스레딩 비용이 어디에서 오는지 논의했습니다. 스레드를 생성하는 많은 작업에 비해 비싸지 만하지 왜이 답변 커버 상대적 있는 작업 실행 대안에 비해 비용이 상대적으로 저렴합니다.

다른 스레드에서 작업을 실행하는 가장 확실한 대안은 동일한 스레드에서 작업을 실행하는 것입니다. 더 많은 스레드가 항상 더 좋다고 가정하는 사람들에게는 이해하기 어렵습니다. 논리는 다른 스레드에 작업을 추가하는 오버 헤드가 저장 시간보다 클 경우 현재 스레드에서 작업을 수행하는 것이 더 빠를 수 있다는 것입니다.

다른 대안은 스레드 풀을 사용하는 것입니다. 스레드 풀은 두 가지 이유로 더 효율적일 수 있습니다. 1) 이미 작성된 스레드를 재사용합니다. 2) 스레드 수를 조정 / 조정하여 최적의 성능을 보장 할 수 있습니다.

다음 프로그램이 인쇄됩니다.

Time for a task to complete in a new Thread 71.3 us
Time for a task to complete in a thread pool 0.39 us
Time for a task to complete in the same thread 0.08 us
Time for a task to complete in a new Thread 65.4 us
Time for a task to complete in a thread pool 0.37 us
Time for a task to complete in the same thread 0.08 us
Time for a task to complete in a new Thread 61.4 us
Time for a task to complete in a thread pool 0.38 us
Time for a task to complete in the same thread 0.08 us

이것은 각 스레딩 옵션의 오버 헤드를 노출하는 사소한 작업에 대한 테스트입니다. 이 테스트 작업은 현재 스레드에서 실제로 가장 잘 수행되는 작업입니다.

final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
Runnable task = new Runnable() {
    @Override
    public void run() {
        queue.add(1);
    }
};

for (int t = 0; t < 3; t++) {
    {
        long start = System.nanoTime();
        int runs = 20000;
        for (int i = 0; i < runs; i++)
            new Thread(task).start();
        for (int i = 0; i < runs; i++)
            queue.take();
        long time = System.nanoTime() - start;
        System.out.printf("Time for a task to complete in a new Thread %.1f us%n", time / runs / 1000.0);
    }
    {
        int threads = Runtime.getRuntime().availableProcessors();
        ExecutorService es = Executors.newFixedThreadPool(threads);
        long start = System.nanoTime();
        int runs = 200000;
        for (int i = 0; i < runs; i++)
            es.execute(task);
        for (int i = 0; i < runs; i++)
            queue.take();
        long time = System.nanoTime() - start;
        System.out.printf("Time for a task to complete in a thread pool %.2f us%n", time / runs / 1000.0);
        es.shutdown();
    }
    {
        long start = System.nanoTime();
        int runs = 200000;
        for (int i = 0; i < runs; i++)
            task.run();
        for (int i = 0; i < runs; i++)
            queue.take();
        long time = System.nanoTime() - start;
        System.out.printf("Time for a task to complete in the same thread %.2f us%n", time / runs / 1000.0);
    }
}
}

보시다시피, 새로운 스레드를 생성하는 데는 ~ 70 µs 밖에 없습니다. 대부분의 경우는 아니지만 많은 사용 사례에서 이는 사소한 것으로 간주 될 수 있습니다. 상대적으로 말하면 대안보다 비싸고 일부 상황에서는 스레드 풀을 사용하거나 스레드를 전혀 사용하지 않는 것이 더 나은 솔루션입니다.


In theory, this depends on the JVM. In practice, every thread has a relatively large amount of stack memory (256 KB per default, I think). Additionally, threads are implemented as OS threads, so creating them involves an OS call, i.e. a context switch.

Do realize that "expensive" in computing is always very relative. Thread creation is very expensive relative to the creation of most objects, but not very expensive relative to a random harddisk seek. You don't have to avoid creating threads at all costs, but creating hundreds of them per second is not a smart move. In most cases, if your design calls for lots of threads, you should use a limited-size thread pool.


There are two kinds of threads:

  1. Proper threads: these are abstractions around the underlying operating system's threading facilities. Thread creation is, therefore, as expensive as the system's -- there's always an overhead.

  2. "Green" threads: created and scheduled by the JVM, these are cheaper, but no proper paralellism occurs. These behave like threads, but are executed within the JVM thread in the OS. They are not often used, to my knowledge.

The biggest factor I can think of in the thread-creation overhead, is the stack-size you have defined for your threads. Thread stack-size can be passed as a parameter when running the VM.

Other than that, thread creation is mostly OS-dependent, and even VM-implementation-dependent.

Now, let me point something out: creating threads is expensive if you're planning on firing 2000 threads per second, every second of your runtime. The JVM is not designed to handle that. If you'll have a couple of stable workers that won't be fired and killed over and over, relax.


Creating Threads requires allocating a fair amount of memory since it has to make not one, but two new stacks (one for java code, one for native code). Use of Executors/Thread Pools can avoid the overhead, by reusing threads for multiple tasks for Executor.


Obviously the crux of the question is what does 'expensive' mean.

A thread needs to create a stack and initialize the stack based on the run method.

It needs to set up control status structures, ie, what state it's in runnable, waiting etc.

There's probably a good deal of synchronization around setting these things up.

참고URL : https://stackoverflow.com/questions/5483047/why-is-creating-a-thread-said-to-be-expensive

반응형