91

ThreadLocal変数からの読み取りは、通常のフィールドからの読み取りよりもどれくらい遅いですか?

ThreadLocalより具体的には、単純なオブジェクトの作成は、変数へのアクセスよりも速いですか、それとも遅いですか?

十分に高速であるため、インスタンスを作成するよりも毎回ThreadLocal<MessageDigest>インスタンスを作成する方がはるかに高速であると思います。MessageDigestしかし、それはたとえば byte[10] や byte[1000] にも当てはまりますか?

ThreadLocal編集: getを呼び出すときに実際に何が起こっているのかという質問です。それが他のフィールドと同じように単なるフィールドである場合、答えは「常に最速」ですよね?

4

6 に答える 6

58

2009年には、一部のJVMは、オブジェクトThreadLocalで非同期を使用して実装されました。これにより、非常に高速になり(もちろん、通常のフィールドアクセスを使用する場合ほど高速ではありませんが)、死んだときにオブジェクトが整理されるようになりました。2016年にこの回答を更新すると、ほとんどの(すべて?)新しいJVMは線形プロービングでを使用しているようです。それらのパフォーマンスについてはよくわかりませんが、以前の実装よりも大幅に悪いとは想像できません。HashMapThread.currentThread()ThreadLocalThreadThreadLocalMap

もちろん、new Object()最近は非常に高速であり、ガベージコレクターは短命のオブジェクトを再生するのにも非常に優れています。

オブジェクトの作成に費用がかかることが確実でない場合、またはスレッドごとに状態を維持する必要がある場合を除いて、必要なときに単純な割り当てを行いThreadLocal、プロファイラーの場合にのみ実装に切り替えることをお勧めします。あなたがする必要があることをあなたに伝えます。

于 2009-03-04T11:36:20.250 に答える
41

未公開のベンチマークをThreadLocal.get実行すると、私のマシンでは反復ごとに約 35 サイクルかかります。大したことではありません。ThreadSun の実装では、s を値にマップするカスタム線形プローブ ハッシュ マップを使用ThreadLocalします。単一のスレッドによってのみアクセスされるため、非常に高速になります。

小さなオブジェクトの割り当てにも同様のサイクル数がかかりますが、キャッシュが枯渇するため、タイトなループでは数値がいくらか低くなる場合があります。

の建設はMessageDigest比較的高価になる可能性があります。かなりの量の状態があり、構築はProviderSPI メカニズムを介して行われます。たとえば、Provider.

作成するよりもキャッシュする方が速いからThreadLocalといって、必ずしもシステムのパフォーマンスが向上するとは限りません。GC に関連する追加のオーバーヘッドが発生し、すべてが遅くなります。

アプリケーションで非常に頻繁に使用しない限り、MessageDigest代わりに従来のスレッドセーフ キャッシュの使用を検討することをお勧めします。

于 2009-03-04T11:56:55.117 に答える
36

良い質問です、私は最近それを自問しています。明確な数値を提供するために、以下のベンチマーク(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.67 GHz)を備えたクアッドコアi7で実行されました。

これらは数字です:

i7

仕様:Inteli72xクアッドコア@2.67GHzテスト: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(平均=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

実行時間:(最後の5つを表示)22.8118 22.8135 59.1753 22.8229 22.8172(平均=23.9752最小=22.7951最大=59.1753)

スレッド番号:8合計テスト:200

実行時間:(最後の5つを表示)22.2965 22.2415 22.3438 22.3109 22.4460(平均=23.2676最小=22.2346最大=50.3583)

AMD

仕様:AMD82204xデュアルコア@2.8GHzテスト:scala.threads.ParallelTests

テスト名:loop_heap_read

総作業量:20000000スレッド数: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総作業量:20000000

実行時間:(最後の5つを表示)6.412 6.424 6.408 6.397 6.43(avg =6.5367最小=6.393最大=19.716)

スレッド番号:4合計テスト:200

実行時間:(最後の5つを表示)3.385 4.298 9.7 6.535 3.385(平均=5.6079最小=3.354最大=21.603)

スレッド番号:8合計テスト:200

実行時間:(最後の5つを表示)5.389 5.795 10.818 3.823 3.824(平均=5.5810分=2.405最大=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(平均=65.6361最小=50.282最大=167.433)

スレッド番号:8合計テスト:200

実行時間:(最後の5つを表示)40.672 74.301 34.434 41.549 28.119(平均=54.7701分=28.119最大=94.424)

概要

ローカルスレッドは、読み取られたヒープの約10〜20倍です。また、このJVM実装と、プロセッサの数に応じたこれらのアーキテクチャで適切に拡張できるようです。

于 2011-01-21T07:59:41.400 に答える
3

@Pete は、最適化する前の正しいテストです。

MessageDigest を実際に使用する場合と比較して、MessageDigest の作成に重大なオーバーヘッドがあるとしたら、私は非常に驚かれることでしょう。

ThreadLocal の使用ミスは、明確なライフ サイクルを持たないリークやダングリング リファレンスの原因となる可能性があります。通常、特定のリソースがいつ削除されるかについて明確な計画がなければ、ThreadLocal を使用することはありません。

于 2009-03-04T10:14:47.717 に答える
0

作って測ります。

また、メッセージ ダイジェスト動作をオブジェクトにカプセル化する場合、必要なスレッドローカルは 1 つだけです。何らかの目的でローカルの MessageDigest とローカルの byte[1000] が必要な場合は、messageDigest と byte[] フィールドを持つオブジェクトを作成し、そのオブジェクトを両方を個別にではなく ThreadLocal に配置します。

于 2009-03-04T09:42:48.090 に答える