几天前,我创建了一个简单的基准测试(没有jmh和所有其他专门的东西,只是为了粗略测量).
我发现,对于同样简单的任务(遍历1000万个数字,对它们进行平方,仅过滤偶数并减少它们的总和),Java的工作速度要快得多.这是代码:
科特林:
fun test() {
println((0 .. 10_000_000L).map { it * it }
.filter { it % 2 == 0L }
.reduce { sum, it -> sum + it })
}
Run Code Online (Sandbox Code Playgroud)
Java的:
public void test() {
System.out.println(LongStream.range(0, 10_000_000)
.map(it -> it * it)
.filter(it -> it % 2 == 0)
.reduce((sum, it) -> sum + it)
.getAsLong());
}
Run Code Online (Sandbox Code Playgroud)
我正在使用Java版本1.8.0_144和Kotlin版本1.2.
在我的硬件上平均需要85毫秒的Java和4,470毫秒的Kotlin执行相应的功能.Kotlin工作速度慢了52倍.
我怀疑Java编译器会产生优化的字节码,但我没想到会看到如此巨大的差异.我想知道我做错了什么?我怎样才能迫使Kotlin更快地工作?我喜欢它,因为它的语法,但52次是一个很大的区别.我只是编写了类似Java 8的代码,而不是普通的旧迭代版本(我相信,它会比给定版本快得多).
Mar*_*nik 29
当您将苹果与橙子进行比较时,结果并不能说明问题.您将一个API与另一个API进行了比较,每个API都有完全不同的焦点和目标.
由于JDK的所有内容都与Kotlin特定的添加一样多,所以我写了更多关于苹果对苹果的比较,这也解决了一些"JVM微基准"问题.
科特林:
fun main(args: Array<String>) {
println("Warming up Kotlin")
test()
test()
test()
println("Measuring Kotlin")
val average = (1..10).map {
measureTimeMillis { test() }
}.average()
println("An average Kotlin run took $average ms")
println("(sum is $sum)")
}
var sum = 0L
fun test() {
sum += LongStream.range(0L, 100_000_000L)
.map { it * it }
.filter { it % 2 == 0L }
.reduce { sum, it -> sum + it }
.asLong
}
Run Code Online (Sandbox Code Playgroud)
Java的:
public static void main(String[] args) {
System.out.println("Warming up Java");
test();
test();
test();
System.out.println("Measuring Java");
LongSummaryStatistics stats = LongStream.range(0, 10)
.map(i -> measureTimeMillis(() -> test()))
.summaryStatistics();
System.out.println("An average Java run took " + stats.getAverage() + " ms");
System.out.println("sum is " + sum);
}
private static long sum;
private static void test() {
sum += LongStream.range(0, 100_000_000)
.map(it -> it * it)
.filter(it -> it % 2 == 0)
.reduce((sum, it) -> sum + it)
.getAsLong();
}
private static long measureTimeMillis(Runnable measured) {
long start = System.nanoTime();
measured.run();
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
}
Run Code Online (Sandbox Code Playgroud)
我的结果:
Warming up Kotlin
Measuring Kotlin
An average Kotlin run took 158.5 ms
(sum is 4276489111714942720)
Warming up Java
Measuring Java
An average Java run took 357.3 ms
sum is 4276489111714942720
Run Code Online (Sandbox Code Playgroud)
惊讶?我也是.
我想做出这样的结论,而不是进一步挖掘,试图找出预期结果的反转.
Kotlin的FP扩展Iterable是为了方便起见.在95%的用例中,您不关心在10-100个元素的列表上执行快速映射过滤器需要1或2μs.
Java的Stream API专注于大型数据结构上的批量操作的性能.它还提供了针对同一目标的自动并行化(虽然它几乎从未真正帮助过),但由于这些问题,它的API已经瘫痪并且有时很尴尬.例如,许多没有实现并行化的有用操作就不存在,并且非终端操作和终端操作的整个范例为您编写的每个Streams表达式增加了大量内容.
我还要谈谈你的一些发言:
我知道Java编译器会生成优化的字节码
这是a)不正确和b)在很大程度上无关紧要,因为(几乎)没有"优化字节码"这样的东西.字节码的解释执行总是比JIT编译的本机代码慢至少一个数量级.
我只是编写了类似Java 8的代码,而不是普通的旧迭代版本(我相信,它会比给定版本快得多).
你是这个意思?
科特林:
fun test() {
var sum: Long = 0
var i: Long = 0
while (i < 100_000_000) {
val j = i * i
if (j % 2 == 0L) {
sum += j
}
i++
}
total += sum
}
Run Code Online (Sandbox Code Playgroud)
Java的:
private static void test() {
long sum = 0;
for (long i = 0; i < 100_000_000; i++) {
long j = i * i;
if (j % 2 == 0) {
sum += j;
}
}
total += sum;
}
Run Code Online (Sandbox Code Playgroud)
这些是结果:
Warming up Kotlin
Measuring Kotlin
An average Kotlin run took 150.1 ms
(sum is 4276489111714942720)
Warming up Java
Measuring Java
An average Java run took 153.0 ms
sum is 4276489111714942720
Run Code Online (Sandbox Code Playgroud)
在这两种语言中,性能几乎与上面的Kotlin + Streams API相同.如上所述,Streams API针对性能进行了优化.
双方kotlinc并javac可能产生非常相似的字节码给这个简单的源代码,然后热点做了两个同样的方式工作.
| 归档时间: |
|
| 查看次数: |
2180 次 |
| 最近记录: |