在 64 位 JVM 上读写双原子?
On a 64 bit JVM is reading and writing of a double atomic?
我正在对 Java 代码的正确同步进行一些研究,并且遇到了多个实例,如果它被声明 reading/writing 的双精度和长整数不是原子的。
所以我写了一个简单的基准测试,发现双精度通常更快(差一点),我唯一注意到一个很大的区别是当我在代码中出现错误并且我在双精度和双精度之间来回转换时浮动(保留为第三个测试用例)。一旦数学是纯双精度或纯浮点数,基准几乎完全相同。 (Java 1.8,64 位甲骨文 JVM)
但是,我读到双精度和长读写不是原子的,而是被 JVM 视为 2 个 32 位读写。
我不应该期待性能差异吗?
public class Benchmark
{
static double loopDouble (double aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
static double loopFloat (float aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
static float loopFloatFloat (float aAddValue)
{
float tResult = 0.0f;
while (tResult < 10000000.0f)
{
tResult += aAddValue;
}
return tResult;
}
static double loopInt (int aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
public static void main(String[] args)
{
long doubleTimeNs = - System.nanoTime();
loopDouble(1.0);
doubleTimeNs += System.nanoTime();
long floatTimeNs = - System.nanoTime();
loopFloat(1.0f);
floatTimeNs += System.nanoTime();
long floatfloatTimeNs = - System.nanoTime();
loopFloatFloat(1.0f);
floatfloatTimeNs += System.nanoTime();
long intTimeNs = -System.nanoTime();
loopInt(1);
intTimeNs += System.nanoTime();
long doubleTime2Ns = - System.nanoTime();
loopDouble(1.0);
doubleTime2Ns += System.nanoTime();
System.out.println("Double: " + doubleTimeNs + " (" + doubleTime2Ns + ") "+ " Float: " + floatTimeNs + " Float-float: " + floatfloatTimeNs + " Int: " + intTimeNs);
}
}
Double: 23944257 (23736683) Float: 24220307 Float-float: 24134056 Int: 25745599
Kelly Denehy 指出。
JLS section 17.7
“出于 Java 编程语言内存模型的目的,对非易失性 long 或 double 值的单次写入被视为两次单独的写入:每个 32 位一半。这可能导致以下情况线程从一次写入中看到 64 位值的前 32 位,从另一次写入中看到第二个 32 位。
volatile long 和 double 值的写入和读取始终是原子的。
对引用的写入和读取始终是原子的,无论它们是作为 32 位值还是 64 位值实现的。 “
我正在对 Java 代码的正确同步进行一些研究,并且遇到了多个实例,如果它被声明 reading/writing 的双精度和长整数不是原子的。
所以我写了一个简单的基准测试,发现双精度通常更快(差一点),我唯一注意到一个很大的区别是当我在代码中出现错误并且我在双精度和双精度之间来回转换时浮动(保留为第三个测试用例)。一旦数学是纯双精度或纯浮点数,基准几乎完全相同。 (Java 1.8,64 位甲骨文 JVM)
但是,我读到双精度和长读写不是原子的,而是被 JVM 视为 2 个 32 位读写。
我不应该期待性能差异吗?public class Benchmark
{
static double loopDouble (double aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
static double loopFloat (float aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
static float loopFloatFloat (float aAddValue)
{
float tResult = 0.0f;
while (tResult < 10000000.0f)
{
tResult += aAddValue;
}
return tResult;
}
static double loopInt (int aAddValue)
{
double tResult = 0.0;
while (tResult < 10000000.0)
{
tResult += aAddValue;
}
return tResult;
}
public static void main(String[] args)
{
long doubleTimeNs = - System.nanoTime();
loopDouble(1.0);
doubleTimeNs += System.nanoTime();
long floatTimeNs = - System.nanoTime();
loopFloat(1.0f);
floatTimeNs += System.nanoTime();
long floatfloatTimeNs = - System.nanoTime();
loopFloatFloat(1.0f);
floatfloatTimeNs += System.nanoTime();
long intTimeNs = -System.nanoTime();
loopInt(1);
intTimeNs += System.nanoTime();
long doubleTime2Ns = - System.nanoTime();
loopDouble(1.0);
doubleTime2Ns += System.nanoTime();
System.out.println("Double: " + doubleTimeNs + " (" + doubleTime2Ns + ") "+ " Float: " + floatTimeNs + " Float-float: " + floatfloatTimeNs + " Int: " + intTimeNs);
}
}
Double: 23944257 (23736683) Float: 24220307 Float-float: 24134056 Int: 25745599
Kelly Denehy 指出。 JLS section 17.7
“出于 Java 编程语言内存模型的目的,对非易失性 long 或 double 值的单次写入被视为两次单独的写入:每个 32 位一半。这可能导致以下情况线程从一次写入中看到 64 位值的前 32 位,从另一次写入中看到第二个 32 位。volatile long 和 double 值的写入和读取始终是原子的。
对引用的写入和读取始终是原子的,无论它们是作为 32 位值还是 64 位值实现的。 “