Java 中的字符串与长性能
String vs long performance in Java
正如我在另一个问题中读到的,int 的性能比 long 好得多,比 string 的性能好一点。
假设我有一个大于 int 的数字,我想用它来与类似的数字进行比较:对于这种情况,哪种类型的变量具有更好的性能?长还是字符串?
例如比较 111.222.333.444 和 555.666.777.888:
long x = 111222333444;
long y = 555666777888;
if(x == y){ /*code*/ }
VS
string x = "111222333444";
string y = "555666777888";
if(x.equals(y)){ /*code*/ }
哪种情况下表现最好?差异显着?
long 会更快——在 64 位平台上,它会和 int 一样快。但除非这是一个非常紧密的循环,否则它可能不会对您的应用程序整体产生任何重大影响。微优化的第一条规则是:不要。
我写了一个快速的 JMH 基准来测试 int、long 和 String 比较,下面是我笔记本电脑上的结果:
EqualityComparisons.cInt avgt 100 0.004 ± 0.001 us/op
EqualityComparisons.cLong avgt 100 0.006 ± 0.001 us/op
EqualityComparisons.cString avgt 100 0.011 ± 0.001 us/op
基准测试的相关位是:
@Setup
public void setup() {
one = (int) System.nanoTime();
two = QuickRand.next(one);
oneS = String.format("%11d", one);
twoS = String.format("%11d", two);
oneL = one;
twoL = two;
}
@Benchmark
public boolean cInt() { return one == two; }
@Benchmark
public boolean cLong() { return oneL == twoL; }
@Benchmark
public boolean cString() { return oneS.equals(twoS); }
注意几点:
- long 和 int 差不多快 -- 在误差范围内
- 字符串速度大约慢 2.75 倍
- 即使是那些慢速字符串,每次比较也需要大约 0.011 微秒,这意味着您每秒可以执行大约 1 亿个字符串。换句话说,这并不重要。
As I read in another questions, int has a much better performance than a long ...
嗯,你读到的可能是错误的。或者更有可能的是,您从阅读的内容中理解的内容是错误的。
的确,在某些机器上 long
的算术运算可能 比类似的 int
运算花费更长的时间。但是在现代机器上,可能没有区别(对于算术本身),即使有区别也只有 1 或 2 个时钟周期;即纳秒。
所以“好多了”是夸张。
... and a little better performance than a string.
这也是错误的。事实上,对 int
值的操作将比对 String
对象的操作快很多。例如,考虑
String x = "111222333444";
String y = "555666777888";
if (x.equals(y)) { /*code*/ }
equals
方法将执行以下操作:
- 测试是否
x == y
和 return true
如果是。
- 测试
y
是否为字符串,如果不是 return false
。
- 测试 2 个字符串的长度是否相同,如果不相同,return
false
。
- 遍历字符串中的字符,比较相同位置的字符,如果不相等则returning
false
。
- Return
true
如果循环结束但没有找到不相等的字符。
加上方法调用的开销(太大而无法内联)。
我们正在谈论(我估计)至少 20 到 30 条机器指令,如果两个字符串相等,或者在开始时相等,则更多。
相比之下,x == y
使用 int
或 long
只是一条指令1.
不过,这里还有一个更大的问题。
很可能您实际上是在这里浪费时间。除非您对 数百万 个数字执行这些操作,否则性能差异可能不会很明显。
我的建议是:
- 除非必须,否则不要优化。
- 除非万不得已,否则不要想这种事情。
- 如果您确实必须这样做(即您有真实的、硬性的性能数据表明您的代码太慢),那么请科学地进行。编写一些涉及 运行 >>真实<< 代码的基准测试 >>真实<< 数据,并使用配置文件找出代码实际花费大部分时间的地方。然后只优化那部分代码。
1 - 我不是指令级性能方面的专家,但 this document 似乎是说英特尔 CMP
指令需要 1、2 或 3 个时钟周期。
正如我在另一个问题中读到的,int 的性能比 long 好得多,比 string 的性能好一点。
假设我有一个大于 int 的数字,我想用它来与类似的数字进行比较:对于这种情况,哪种类型的变量具有更好的性能?长还是字符串?
例如比较 111.222.333.444 和 555.666.777.888:
long x = 111222333444;
long y = 555666777888;
if(x == y){ /*code*/ }
VS
string x = "111222333444";
string y = "555666777888";
if(x.equals(y)){ /*code*/ }
哪种情况下表现最好?差异显着?
long 会更快——在 64 位平台上,它会和 int 一样快。但除非这是一个非常紧密的循环,否则它可能不会对您的应用程序整体产生任何重大影响。微优化的第一条规则是:不要。
我写了一个快速的 JMH 基准来测试 int、long 和 String 比较,下面是我笔记本电脑上的结果:
EqualityComparisons.cInt avgt 100 0.004 ± 0.001 us/op
EqualityComparisons.cLong avgt 100 0.006 ± 0.001 us/op
EqualityComparisons.cString avgt 100 0.011 ± 0.001 us/op
基准测试的相关位是:
@Setup
public void setup() {
one = (int) System.nanoTime();
two = QuickRand.next(one);
oneS = String.format("%11d", one);
twoS = String.format("%11d", two);
oneL = one;
twoL = two;
}
@Benchmark
public boolean cInt() { return one == two; }
@Benchmark
public boolean cLong() { return oneL == twoL; }
@Benchmark
public boolean cString() { return oneS.equals(twoS); }
注意几点:
- long 和 int 差不多快 -- 在误差范围内
- 字符串速度大约慢 2.75 倍
- 即使是那些慢速字符串,每次比较也需要大约 0.011 微秒,这意味着您每秒可以执行大约 1 亿个字符串。换句话说,这并不重要。
As I read in another questions, int has a much better performance than a long ...
嗯,你读到的可能是错误的。或者更有可能的是,您从阅读的内容中理解的内容是错误的。
的确,在某些机器上 long
的算术运算可能 比类似的 int
运算花费更长的时间。但是在现代机器上,可能没有区别(对于算术本身),即使有区别也只有 1 或 2 个时钟周期;即纳秒。
所以“好多了”是夸张。
... and a little better performance than a string.
这也是错误的。事实上,对 int
值的操作将比对 String
对象的操作快很多。例如,考虑
String x = "111222333444";
String y = "555666777888";
if (x.equals(y)) { /*code*/ }
equals
方法将执行以下操作:
- 测试是否
x == y
和 returntrue
如果是。 - 测试
y
是否为字符串,如果不是 returnfalse
。 - 测试 2 个字符串的长度是否相同,如果不相同,return
false
。 - 遍历字符串中的字符,比较相同位置的字符,如果不相等则returning
false
。 - Return
true
如果循环结束但没有找到不相等的字符。
加上方法调用的开销(太大而无法内联)。
我们正在谈论(我估计)至少 20 到 30 条机器指令,如果两个字符串相等,或者在开始时相等,则更多。
相比之下,x == y
使用 int
或 long
只是一条指令1.
不过,这里还有一个更大的问题。
很可能您实际上是在这里浪费时间。除非您对 数百万 个数字执行这些操作,否则性能差异可能不会很明显。
我的建议是:
- 除非必须,否则不要优化。
- 除非万不得已,否则不要想这种事情。
- 如果您确实必须这样做(即您有真实的、硬性的性能数据表明您的代码太慢),那么请科学地进行。编写一些涉及 运行 >>真实<< 代码的基准测试 >>真实<< 数据,并使用配置文件找出代码实际花费大部分时间的地方。然后只优化那部分代码。
1 - 我不是指令级性能方面的专家,但 this document 似乎是说英特尔 CMP
指令需要 1、2 或 3 个时钟周期。