私はこのブログ投稿を読んでいました。
そして、作者はマルチスレッド環境への侵入について話していhashCode()
ました。String
持っていることによって:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
変更:
public int hashCode() {
if (hash == 0) {
int off = offset;
char val[] = value;
int len = count;
int h = 0;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return hash;
}
著者が言っていることと私が引用していること:
「ここで行ったことは、追加の読み取りを追加することです。ハッシュの2回目の読み取りは、返される前に行われます。奇妙なことに、発生する可能性は低いですが、最初の読み取りでは、正しく計算されたハッシュ値を返すことができます。 2番目の読み取りは0を返すことができます!これは、モデルが操作の広範な並べ替えを許可するため、メモリモデルで許可されます。2番目の読み取りは、コード内で実際に移動できるため、プロセッサは最初の読み取りの前にそれを実行します!」
それで、コメントをさらに調べて、誰かがそれをに並べ替えることができると言います
int h = hash;
if (hash == 0) {
...
}
return h;
そんなことがあるものか?並べ替えには、プログラムステートメントを上下に移動するだけだと思いました。それはどのような規則に従っていますか?Googleで検索し、JSR133 FAQを読み、Java Concurrency in Practiceの本を確認しましたが、特に並べ替えについて理解するのに役立つ場所が見つからないようです。誰かが私を正しい方向に向けることができれば、私は本当にそれをいただければ幸いです。
Louisが「並べ替え」の意味を明確にしてくれたおかげで、私は「byteCode」の観点から考えていませんでした。
ただし、なぜ2番目の読み取りを前面に移動できるのかはまだわかりません。これは、それをいくらか「バイトコード」形式に変換するための私の素朴な試みです。
簡略化のために、ハッシュコードの計算に使用される操作はとして表されcalchash()
ます。したがって、私はプログラムを次のように表現します。
if (hash == 0) {
h = calchash();
hash = h;
}
return hash;
そしてそれを「バイトコード」形式で表現しようとしています。
R1,R2,R3 are in the operands stack, or the registers
h is in the array of local variables
プログラム順:
if (hash == 0) { ---------- R1 = read hash from memory (1st read)
---------- Compare (R1 == 0)
h = calchash(); ---------- R2 = calchash()
---------- h = R2 (Storing the R2 to local variable h)
hash = h; ---------- Hash = h (write to hash)
}
return hash ---------- R3 = read hash from memory again(2nd read)
---------- return R3
並べ替えられた変換(コメントに基づく私のバージョン):
---------- R3 = read hash from memory (2nd read) *moved*
if (hash == 0) { ---------- R1 = read hash from memory (1st read)
---------- Compare (R1 == 0)
h = calchash(); ---------- R2 = calchash()
---------- h = R2 (Storing the R2 to local variable h)
hash = h; ---------- hash = h (write to hash)
}
return hash ---------- return R3
コメントをもう一度確認すると、著者がこれに答えていることがわかりました。
並べ替えられた変換(ブログから)
r1 = hash;
if (hash == 0) {
r1 = hash = // calculate hash
}
return r1;
このケースは実際には単一のスレッドで機能しますが、複数のスレッドで失敗する可能性があります。
JVMはに基づいて単純化を行っているようです
h = hash and it simplifies the use of R1, R2, R3 to single R1
したがって、JVMは命令を並べ替えるだけでなく、使用されるレジスタの量を減らすようにも見えます。