ThreadLocal 변수의 성능
ThreadLocal
일반 필드보다 느린 변수 에서 얼마나 많이 읽 습니까?
더 구체적으로 간단한 객체 생성이 ThreadLocal
변수에 대한 액세스보다 빠르거나 느린 가요?
ThreadLocal<MessageDigest>
인스턴스 를 갖는 것이 MessageDigest
매번 인스턴스를 만드는 것보다 훨씬 빠를 정도로 충분히 빠르다고 가정합니다 . 하지만 예를 들어 byte [10] 또는 byte [1000]에도 적용됩니까?
편집 : 질문은 ThreadLocal
's get을 호출 할 때 실제로 무슨 일이 일어나고 있습니까? 그게 다른 필드와 마찬가지로 필드라면 대답은 "항상 가장 빠릅니다"가 되겠죠?
게시되지 않은 벤치 마크를 실행하려면 ThreadLocal.get
내 컴퓨터에서 반복 당 약 35주기가 걸립니다. 별거 아니야. Sun의 구현에서 사용자 정의 선형 프로빙 해시 맵은 s를 값 으로 Thread
매핑 ThreadLocal
합니다. 단일 스레드에서만 액세스 할 수 있기 때문에 매우 빠를 수 있습니다.
작은 개체의 할당은 비슷한 수의주기를 필요로하지만 캐시 고갈로 인해 타이트한 루프에서 다소 낮은 수치를 얻을 수 있습니다.
의 건설은 MessageDigest
상대적으로 비용이 많이 듭니다. 그것은 상당한 양의 상태를 가지고 있으며 Provider
SPI 메커니즘을 통과합니다 . 예를 들어 .NET Framework를 복제하거나 제공하여 최적화 할 수 있습니다 Provider
.
ThreadLocal
생성 하는 것보다 캐시하는 것이 더 빠르다고해서 반드시 시스템 성능이 향상되는 것은 아닙니다. 모든 것을 느리게 만드는 GC와 관련된 추가 오버 헤드가 있습니다.
응용 프로그램이 매우 많이 사용하지 않는 MessageDigest
한 기존의 스레드 안전 캐시를 대신 사용하는 것이 좋습니다.
2009 년에 일부 JVM은 Thread.currentThread () 객체에서 동기화되지 않은 HashMap을 사용하여 ThreadLocal을 구현했습니다. 이로 인해 (물론 일반 필드 액세스를 사용하는 것만 큼 빠르지는 않지만) 매우 빠르며 ThreadLocal 객체가 Thread가 죽었을 때 정리되었는지 확인했습니다. 2016 년 에이 답변을 업데이트하면 대부분의 (모두?) 최신 JVM이 선형 프로브와 함께 ThreadLocalMap을 사용하는 것 같습니다. 나는 그것들의 성능에 대해 확신하지 못합니다. 그러나 이것이 이전 구현보다 훨씬 더 나쁘다는 것을 상상할 수 없습니다.
물론 최근에는 new Object ()도 매우 빠르며 가비지 콜렉터도 수명이 짧은 객체를 회수하는 데 매우 능숙합니다.
객체 생성이 비용이 많이들 것이라는 확신이 들지 않거나 스레드별로 스레드에서 일부 상태를 유지해야하는 경우가 아니라면 필요한 경우 더 간단한 할당 솔루션을 사용하고 다음과 같은 경우에만 ThreadLocal 구현으로 전환하는 것이 좋습니다. 프로파일 러가 필요하다고 알려줍니다.
좋은 질문입니다. 최근에 스스로에게 물어 봤습니다. 명확한 숫자를 제공하기 위해 아래 벤치 마크 (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.8GHz 듀얼 코어 및 하이퍼 스레딩 (2.67GHz)이있는 쿼드 코어 i7에서 수행되었습니다.
숫자는 다음과 같습니다.
i7
사양 : Intel i7 2x 쿼드 코어 @ 2.67GHz 테스트 : scala.threads.ParallelTests
테스트 이름 : loop_heap_read
스레드 수 : 1 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 9.0069 9.0036 9.0017 9.0084 9.0074 (평균 = 9.1034 최소 = 8.9986 최대 = 21.0306)
스레드 수 : 2 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 4.5563 4.7128 4.5663 4.5617 4.5724 (평균 = 4.6337 최소 = 4.5509 최대 = 13.9476)
스레드 수 : 4 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 2.3946 2.3979 2.3934 2.3937 2.3964 (평균 = 2.5113 최소 = 2.3884 최대 = 13.5496)
스레드 수 : 8 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 2.4479 2.4362 2.4323 2.4472 2.4383 (평균 = 2.5562 최소 = 2.4166 최대 = 10.3726)
테스트 이름 : threadlocal
스레드 수 : 1 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 91.1741 90.8978 90.6181 90.6200 90.6113 (평균 = 91.0291 최소 = 90.6000 최대 = 129.7501)
스레드 수 : 2 총 테스트 : 200
실행 시간 : (마지막 5 개 표시) 45.3838 45.3858 45.6676 45.3772 45.3839 (평균 = 46.0555 최소 = 45.3726 최대 = 90.7108)
스레드 수 : 4 총 테스트 : 200
Run times: (showing last 5) 22.8118 22.8135 59.1753 22.8229 22.8172 (avg = 23.9752 min = 22.7951 max = 59.1753 )
Thread num.: 8 Total tests: 200
Run times: (showing last 5) 22.2965 22.2415 22.3438 22.3109 22.4460 (avg = 23.2676 min = 22.2346 max = 50.3583 )
AMD
Specs: AMD 8220 4x dual-core @ 2.8 GHz Test: scala.threads.ParallelTests
Test name: loop_heap_read
Total work: 20000000 Thread num.: 1 Total tests: 200
Run times: (showing last 5) 12.625 12.631 12.634 12.632 12.628 (avg = 12.7333 min = 12.619 max = 26.698 )
Test name: loop_heap_read Total work: 20000000
Run times: (showing last 5) 6.412 6.424 6.408 6.397 6.43 (avg = 6.5367 min = 6.393 max = 19.716 )
Thread num.: 4 Total tests: 200
Run times: (showing last 5) 3.385 4.298 9.7 6.535 3.385 (avg = 5.6079 min = 3.354 max = 21.603 )
Thread num.: 8 Total tests: 200
Run times: (showing last 5) 5.389 5.795 10.818 3.823 3.824 (avg = 5.5810 min = 2.405 max = 19.755 )
Test name: threadlocal
Thread num.: 1 Total tests: 200
Run times: (showing last 5) 200.217 207.335 200.241 207.342 200.23 (avg = 202.2424 min = 200.184 max = 245.369 )
Thread num.: 2 Total tests: 200
Run times: (showing last 5) 100.208 100.199 100.211 103.781 100.215 (avg = 102.2238 min = 100.192 max = 129.505 )
Thread num.: 4 Total tests: 200
Run times: (showing last 5) 62.101 67.629 62.087 52.021 55.766 (avg = 65.6361 min = 50.282 max = 167.433 )
Thread num.: 8 Total tests: 200
Run times: (showing last 5) 40.672 74.301 34.434 41.549 28.119 (avg = 54.7701 min = 28.119 max = 94.424 )
Summary
A thread local is around 10-20x that of the heap read. It also seems to scale well on this JVM implementation and these architectures with the number of processors.
Here it goes another test. The results shows that ThreadLocal is a bit slower than a regular field, but in the same order. Aprox 12% slower
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);
}
}
}'
Output:
0-Running field sample
0-End field sample:6044
0-Running thread local sample
0-End thread local sample:6015
1-Running field sample
1-End field sample:5095
1-Running thread local sample
1-End thread local sample:5720
2-Running field sample
2-End field sample:4842
2-Running thread local sample
2-End thread local sample:5835
3-Running field sample
3-End field sample:4674
3-Running thread local sample
3-End thread local sample:5287
4-Running field sample
4-End field sample:4849
4-Running thread local sample
4-End thread local sample:5309
5-Running field sample
5-End field sample:4781
5-Running thread local sample
5-End thread local sample:5330
6-Running field sample
6-End field sample:5294
6-Running thread local sample
6-End thread local sample:5511
7-Running field sample
7-End field sample:5119
7-Running thread local sample
7-End thread local sample:5793
8-Running field sample
8-End field sample:4977
8-Running thread local sample
8-End thread local sample:6374
9-Running field sample
9-End field sample:4841
9-Running thread local sample
9-End thread local sample:5471
Field avg:5051
ThreadLocal avg:5664
Env:
openjdk version "1.8.0_131"
Intel® Core™ i7-7500U CPU @ 2.70GHz × 4
Ubuntu 16.04 LTS
@Pete is correct test before you optimise.
I would be very surprised if constructing a MessageDigest has any serious overhead when compared to actaully using it.
Miss using ThreadLocal can be a source of leaks and dangling references, that don't have a clear life cycle, generally I don't ever use ThreadLocal without a very clear plan of when a particular resource will be removed.
Build it and measure it.
Also, you only need one threadlocal if you encapsulate your message digesting behaviour into an object. If you need a local MessageDigest and a local byte[1000] for some purpose, create an object with a messageDigest and a byte[] field and put that object into the ThreadLocal rather than both individually.
참고URL : https://stackoverflow.com/questions/609826/performance-of-threadlocal-variable
'developer tip' 카테고리의 다른 글
이벤트 선언에 익명의 빈 대리자를 추가하는 데 단점이 있습니까? (0) | 2020.09.25 |
---|---|
비틀어 진 파이썬 : 어디서부터 시작해야할까요? (0) | 2020.09.25 |
write.table은 행 이름이있을 때 원하지 않는 선행 빈 열을 헤더에 씁니다. (0) | 2020.09.25 |
DataFrame의 문자열이지만 dtype은 객체입니다. (0) | 2020.09.25 |
R에서 %> % 함수는 무엇을 의미합니까? (0) | 2020.09.25 |