将 DecimalFormat 与 ThreadLocal 一起使用对性能有何影响?
What are the performance impacts of using DecimalFormat with ThreadLocal?
我有一个使用 DecimalFormat
(https://docs.oracle.com/javase/7/docs/api/java/text/DecimalFormat.html) API 的实现。
解决方案1:争论是因为DecimalFormat
是不是线程安全的,我倾向于使用ThreadLocal
用于 DecimalFormat
创建以使其线程安全。此外,它将为每次调用保存 DecimalFormat 对象的创建
private static final ThreadLocal<DecimalFormat> restrictTo1DecimalPlace =
ThreadLocal.withInitial
(() -> new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)));
解决方案 2:另一个简单的解决方案是放弃对象的可重用性,每次都创建 DecimalFormat
的对象。
new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(decimalValueToFormat)
什么比较好?
不知是否有显着差异
In recent times, JVM performance has been multiplied manifold and so object creation is no longer considered as expensive as it was done earlier.
但是,新的 DecimalFormat 的分配确实会影响内存,也可能会消耗时间。
创建新实例是非常合理的,这就是 java 代码应该工作的方式。
您可以尝试的一件事是预先分配 DecimalFormat 实例并将它们保存在一个简单的 object pool.
中
在大多数应用程序中,差异无关紧要,因此您需要更简单的选项。
您可以通过对两种备选方案进行基准测试来验证这一点:
public abstract class Benchmark {
public static void main(String[] args) throws Exception {
final ThreadLocal<DecimalFormat> restrictTo1DecimalPlace =
ThreadLocal.withInitial
(() -> new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)));
Benchmark[] marks = {
new Benchmark("ThreadLocal") {
@Override
protected Object run(int iterations) throws Throwable {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append(restrictTo1DecimalPlace.get().format(i * 0.01));
}
return sb;
};
},
new Benchmark("new Format") {
@Override
protected Object run(int iterations) throws Throwable {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append(new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(i * 0.01));
}
return sb;
};
},
};
for (Benchmark mark : marks) {
System.out.println(mark);
}
}
final String name;
public Benchmark(String name) {
this.name = name;
}
@Override
public String toString() {
return name + "\t" + time() + " ns / iteration";
}
private BigDecimal time() {
try {
// automatically detect a reasonable iteration count (and trigger just in time compilation of the code under test)
int iterations;
long duration = 0;
for (iterations = 1; iterations < 1_000_000_000 && duration < 1_000_000_000; iterations *= 2) {
long start = System.nanoTime();
run(iterations);
duration = System.nanoTime() - start;
}
return new BigDecimal((duration) * 1000 / iterations).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/**
* Executes the code under test.
* @param iterations
* number of iterations to perform
* @return any value that requires the entire code to be executed (to
* prevent dead code elimination by the just in time compiler)
* @throws Throwable
* if the test could not complete successfully
*/
protected abstract Object run(int iterations) throws Throwable;
}
在我的机器上,打印出:
ThreadLocal 260.132 ns / iteration
new Format 363.199 ns / iteration
因此,从 ThreadLocal 获取格式或创建新格式之间的差异约为 0.0000001 秒。除非您的应用程序每秒格式化数百万个字符串,否则这不值得考虑:-)
我有一个使用 DecimalFormat
(https://docs.oracle.com/javase/7/docs/api/java/text/DecimalFormat.html) API 的实现。
解决方案1:争论是因为DecimalFormat
是不是线程安全的,我倾向于使用ThreadLocal
用于 DecimalFormat
创建以使其线程安全。此外,它将为每次调用保存 DecimalFormat 对象的创建
private static final ThreadLocal<DecimalFormat> restrictTo1DecimalPlace =
ThreadLocal.withInitial
(() -> new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)));
解决方案 2:另一个简单的解决方案是放弃对象的可重用性,每次都创建 DecimalFormat
的对象。
new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(decimalValueToFormat)
什么比较好?
不知是否有显着差异
In recent times, JVM performance has been multiplied manifold and so object creation is no longer considered as expensive as it was done earlier.
但是,新的 DecimalFormat 的分配确实会影响内存,也可能会消耗时间。 创建新实例是非常合理的,这就是 java 代码应该工作的方式。
您可以尝试的一件事是预先分配 DecimalFormat 实例并将它们保存在一个简单的 object pool.
中在大多数应用程序中,差异无关紧要,因此您需要更简单的选项。
您可以通过对两种备选方案进行基准测试来验证这一点:
public abstract class Benchmark {
public static void main(String[] args) throws Exception {
final ThreadLocal<DecimalFormat> restrictTo1DecimalPlace =
ThreadLocal.withInitial
(() -> new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)));
Benchmark[] marks = {
new Benchmark("ThreadLocal") {
@Override
protected Object run(int iterations) throws Throwable {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append(restrictTo1DecimalPlace.get().format(i * 0.01));
}
return sb;
};
},
new Benchmark("new Format") {
@Override
protected Object run(int iterations) throws Throwable {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append(new DecimalFormat("0.0%", DecimalFormatSymbols.getInstance(Locale.ENGLISH)).format(i * 0.01));
}
return sb;
};
},
};
for (Benchmark mark : marks) {
System.out.println(mark);
}
}
final String name;
public Benchmark(String name) {
this.name = name;
}
@Override
public String toString() {
return name + "\t" + time() + " ns / iteration";
}
private BigDecimal time() {
try {
// automatically detect a reasonable iteration count (and trigger just in time compilation of the code under test)
int iterations;
long duration = 0;
for (iterations = 1; iterations < 1_000_000_000 && duration < 1_000_000_000; iterations *= 2) {
long start = System.nanoTime();
run(iterations);
duration = System.nanoTime() - start;
}
return new BigDecimal((duration) * 1000 / iterations).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
/**
* Executes the code under test.
* @param iterations
* number of iterations to perform
* @return any value that requires the entire code to be executed (to
* prevent dead code elimination by the just in time compiler)
* @throws Throwable
* if the test could not complete successfully
*/
protected abstract Object run(int iterations) throws Throwable;
}
在我的机器上,打印出:
ThreadLocal 260.132 ns / iteration
new Format 363.199 ns / iteration
因此,从 ThreadLocal 获取格式或创建新格式之间的差异约为 0.0000001 秒。除非您的应用程序每秒格式化数百万个字符串,否则这不值得考虑:-)