문제

얼마나 읽습니까? ThreadLocal 일반 필드보다 변하는 가변?

더 구체적으로 간단한 물체 생성은 액세스보다 빠르거나 느립니다. ThreadLocal 변하기 쉬운?

나는 그것이 충분히 빠르다고 가정합니다. ThreadLocal<MessageDigest> 인스턴스가 훨씬 빠르면 인스턴스를 작성합니다 MessageDigest 매번. 그러나 예를 들어 바이트 [10] 또는 바이트 [1000]에도 적용됩니까?

편집 : 질문은 전화 할 때 실제로 무슨 일이 일어나고 있는지입니다. ThreadLocal얻어? 그것이 단지 다른 분야와 마찬가지로, 대답은 "항상 가장 빠른 것"이 될 것입니다.

도움이 되었습니까?

해결책

미공개 벤치 마크 실행, ThreadLocal.get 내 컴퓨터에서 반복 당 약 35 사이클이 필요합니다. 큰 거래가 아닙니다. Sun의 구현에서 맞춤형 선형 프로브 해시 맵 Thread 지도 ThreadLocals 값에. 단일 스레드에 의해서만 액세스되기 때문에 매우 빠를 수 있습니다.

작은 물체의 할당은 비슷한 수의 사이클을 사용하지만 캐시 소진으로 인해 단단한 루프에서 다소 낮은 수치를 얻을 수 있습니다.

의 구성 MessageDigest 상대적으로 비싸다. 그것은 상당한 양의 상태를 가지고 있으며 건설은 Provider SPI 메커니즘. 예를 들어 복제 또는 제공에 의해 최적화 될 수 있습니다. Provider.

캐시가 더 빠르기 때문에 ThreadLocal 생성보다는 반드시 시스템 성능이 증가한다는 것을 의미하지는 않습니다. GC와 관련된 추가 오버 헤드가있어 모든 것이 속도가 느려집니다.

응용 프로그램이 매우 많이 사용되지 않는 한 MessageDigest 대신 기존 스레드 안전 캐시를 사용하는 것을 고려할 수 있습니다.

다른 팁

2009 년에 일부 JVMS는 Thread.CurrentThread () 객체에서 동기화되지 않은 해시 맵을 사용하여 ThreadLocal을 구현했습니다. 이로 인해 정기적 인 필드 액세스를 사용하는 것만 큼 빠르지는 않지만 스레드가 죽었을 때 Threadlocal 객체가 정리되도록 보장했습니다. 2016 년 에이 답변을 업데이트하면 가장 새로운 JVMS가 선형 프로브와 함께 스레드로 팔 맵을 사용하는 것 같습니다. 나는 그것들의 성능에 대해 확실하지 않지만, 그것이 이전 구현보다 훨씬 나쁘다는 것을 상상할 수는 없습니다.

물론, 새로운 Object ()도 요즘 매우 빠르며 쓰레기 수집기는 단기간을 되 찾는 데 매우 능숙합니다.

객체 생성이 비싸지 않을 것이라고 확신하거나 스레드 기준으로 스레드에서 일부 상태를 지속 해야하는 경우 필요한 경우 더 간단한 할당을받는 것이 좋습니다. Profiler는 당신이 필요하다고 말합니다.

좋은 질문, 나는 최근에 스스로에게 물었다. 명확한 숫자를 제공하기 위해 아래의 벤치 마크 (Scala에서는 동등한 Java 코드와 거의 동일한 바이트 코드로 편집) :

var cnt: String = ""
val tlocal = new java.lang.ThreadLocal[String] {
  override def initialValue = ""
}

def loop_heap_write = {                                                                                                                           
  var i = 0                                                                                                                                       
  val until = totalwork / threadnum                                                                                                               
  while (i < until) {                                                                                                                             
    if (cnt ne "") cnt = "!"                                                                                                                      
    i += 1                                                                                                                                        
  }                                                                                                                                               
  cnt                                                                                                                                          
} 

def threadlocal = {
  var i = 0
  val until = totalwork / threadnum
  while (i < until) {
    if (tlocal.get eq null) i = until + i + 1
    i += 1
  }
  if (i > until) println("thread local value was null " + i)
}

사용 가능 여기, AMD 4X 2.8 GHz 듀얼 코어 및 하이퍼 스레딩 (2.67GHz)이있는 쿼드 코어 i7에서 수행되었다.

이것들은 숫자입니다.

i7

사양 : 인텔 i7 2x 쿼드 코어 @ 2.67 GHz 테스트 : Scala.threads.paralleltests

테스트 이름 : loop_heap_read

스레드 번호 : 1 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 9.0069 9.0036 9.0017 9.0084 9.0074 (avg = 9.1034 min = 8.9986 max = 21.0306)

스레드 번호 : 2 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 4.5563 4.7128 4.5663 4.5617 4.5724 (avg = 4.6337 min = 4.5509 max = 13.9476)

스레드 번호 : 4 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 2.3946 2.3979 2.3934 2.3937 2.3964 (avg = 2.5113 min = 2.3884 max = 13.5496)

스레드 번호 : 8 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 2.4479 2.4362 2.4323 2.4472 2.4383 (avg = 2.5562 min = 2.4166 max = 10.3726)

테스트 이름 : ThreadLocal

스레드 번호 : 1 총 테스트 : 200

런 타임 : (지난 5 번 표시) 91.1741 90.8978 90.6181 90.6200 90.6113 (avg = 91.0291 min = 90.6000 max = 129.7501)

스레드 번호 : 2 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 45.3838 45.3858 45.6676 45.3772 45.3839 (avg = 46.0555 min = 45.3726 max = 90.7108)

스레드 번호 : 4 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 22.8118 22.8135 59.1753 22.8229 22.8172 (avg = 23.9752 min = 22.7951 max = 59.1753)

스레드 번호 : 8 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 22.2965 22.2415 22.3438 22.3109 22.4460 (avg = 23.2676 min = 22.2346 max = 50.3583)

AMD

사양 : AMD 8220 4X 듀얼 코어 @ 2.8GHz 테스트 : Scala.threads.paralleltests

테스트 이름 : loop_heap_read

총 작업 : 200000000 스레드 Num. : 1 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 12.625 12.631 12.634 12.632 12.628 (avg = 12.7333 min = 12.619 max = 26.698)

테스트 이름 : LOOP_HEAP_READ 총 작업 : 200000000

실행 시간 : (마지막 5 개 표시) 6.412 6.424 6.408 6.397 6.43 (avg = 6.5367 min = 6.393 max = 19.716)

스레드 번호 : 4 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 3.385 4.298 9.7 6.535 3.385 (avg = 5.6079 min = 3.354 max = 21.603)

스레드 번호 : 8 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 5.389 5.795 10.818 3.823 3.824 (avg = 5.5810 min = 2.405 max = 19.755)

테스트 이름 : ThreadLocal

스레드 번호 : 1 총 테스트 : 200

런 타임 : (지난 5 번 표시) 200.217 207.335 200.241 207.342 200.23 (avg = 202.2424 min = 200.184 max = 245.369)

스레드 번호 : 2 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 100.208 100.199 100.211 103.781 100.215 (avg = 102.2238 min = 100.192 max = 129.505)

스레드 번호 : 4 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 62.101 67.629 62.087 52.021 55.766 (avg = 65.6361 min = 50.282 max = 167.433)

스레드 번호 : 8 총 테스트 : 200

실행 시간 : (마지막 5 개 표시) 40.672 74.301 34.434 41.549 28.119 (avg = 54.7701 min = 28.119 max = 94.424)

요약

스레드 로컬은 힙 읽기의 스레드 약 10-20 배입니다. 또한이 JVM 구현과 프로세서 수와 함께 이러한 아키텍처에서 잘 확장되는 것 같습니다.

여기에는 또 다른 테스트가 있습니다. 결과는 ThreadLocal이 일반 필드보다 약간 느리지 만 같은 순서로 나타납니다. APROX 12% 느린 느린

public class Test {
private static final int N = 100000000;
private static int fieldExecTime = 0;
private static int threadLocalExecTime = 0;

public static void main(String[] args) throws InterruptedException {
    int execs = 10;
    for (int i = 0; i < execs; i++) {
        new FieldExample().run(i);
        new ThreadLocaldExample().run(i);
    }
    System.out.println("Field avg:"+(fieldExecTime / execs));
    System.out.println("ThreadLocal avg:"+(threadLocalExecTime / execs));
}

private static class FieldExample {
    private Map<String,String> map = new HashMap<String, String>();

    public void run(int z) {
        System.out.println(z+"-Running  field sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            map.put(s,"a");
            map.remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        fieldExecTime += t;
        System.out.println(z+"-End field sample:"+t);
    }
}

private static class ThreadLocaldExample{
    private ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<Map<String,String>>() {
        @Override protected Map<String, String> initialValue() {
            return new HashMap<String, String>();
        }
    };

    public void run(int z) {
        System.out.println(z+"-Running thread local sample");
        long start = System.currentTimeMillis();
        for (int i = 0; i < N; i++){
            String s = Integer.toString(i);
            myThreadLocal.get().put(s, "a");
            myThreadLocal.get().remove(s);
        }
        long end = System.currentTimeMillis();
        long t = (end - start);
        threadLocalExecTime += t;
        System.out.println(z+"-End thread local sample:"+t);
    }
}
}'

산출:

0 실행 필드 샘플

0- 엔드 필드 샘플 : 6044

0 실행 스레드 로컬 샘플

0- 엔드 스레드 로컬 샘플 : 6015

1- 실행 필드 샘플

1 엔드 필드 샘플 : 5095

1 실행 스레드 로컬 샘플

1- 엔드 스레드 로컬 샘플 : 5720

2 실행 필드 샘플

2- 엔드 필드 샘플 : 4842

2 실행 스레드 로컬 샘플

2- 엔드 스레드 로컬 샘플 : 5835

3 실행 필드 샘플

3- 엔드 필드 샘플 : 4674

3 실행 스레드 로컬 샘플

3- 엔드 스레드 로컬 샘플 : 5287

4 실행 필드 샘플

4- 엔드 필드 샘플 : 4849

4 실행 스레드 로컬 샘플

4- 엔드 스레드 로컬 샘플 : 5309

5 실행 필드 샘플

5- 엔드 필드 샘플 : 4781

5 실행 스레드 로컬 샘플

5- 엔드 스레드 로컬 샘플 : 5330

6 실행 필드 샘플

6- 엔드 필드 샘플 : 5294

6 실행 스레드 로컬 샘플

6- 엔드 스레드 로컬 샘플 : 5511

7 실행 필드 샘플

7- 엔드 필드 샘플 : 5119

7 실행 스레드 로컬 샘플

7- 엔드 스레드 로컬 샘플 : 5793

8 실행 필드 샘플

8- 엔드 필드 샘플 : 4977

8 실행 스레드 로컬 샘플

8- 엔드 스레드 로컬 샘플 : 6374

9 실행 필드 샘플

9- 엔드 필드 샘플 : 4841

9 실행 스레드 로컬 샘플

9- 엔드 스레드 로컬 샘플 : 5471

필드 AVG : 5051

ThreadLocal AVG : 5664

ENV :

OpenJDK 버전 "1.8.0_131"

Intel® Core ™ i7-7500U CPU @ 2.70GHz × 4

우분투 16.04 LTS

@pete는 최적화하기 전에 올바른 테스트입니다.

MessageDigest를 구성하는 것이 Actaully 사용과 비교할 때 심각한 오버 헤드를 가지고 있다면 매우 놀랄 것입니다.

ResdleLocal을 사용하는 것은 유출 및 매달려있는 참조의 원천이 될 수 있습니다. 명확한 수명주기가 없습니다. 일반적으로 특정 자원이 제거 될시기에 대한 명확한 계획 없이는 ThreadLocal을 사용하지 않습니다.

그것을 만들고 측정하십시오.

또한 메시지 소화 동작을 객체에 캡슐화하는 경우 threadLocal이 하나만 필요합니다. 어떤 목적을 위해 로컬 메시지 게스트와 로컬 바이트 [1000]가 필요한 경우 MessageDigest와 바이트 [] 필드가있는 객체를 만들고 그 객체를 개별적으로보다는 스레드 로컬에 넣으십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top