作者丨Jakub Anio a
译者丨姜雨生
策划丨田晓旭
跟着对 Kotlin 越来越深化的了解,我发现市面上关于 Kotlin 方面,比较深化的材料简直是 0,所以我决议,将 Kotlin 各个方面的研讨作为我的研讨生课题,而功能问题往往是程序员最佳重视的内容,所以榜首篇,我决议先比照一下 Java 和 Kotlin 之间的功能,经过闻名的计算机言语的基准测验游戏来对两者的功能做一个全方位的比照。
大约一年前,我仍是一名 Poznań 大学的大学生,学习软件工程相关的内容,考虑硕士论文相关的内容。简直一切引荐学习的内容都超级无聊,我对它们提不起任何的爱好,所以决议自己做一些研讨课题。
与此同时,10 月份,我和 RSQ 技能安排的朋友一同前往阿姆斯特丹参与 KotlinConf 2018 的技能峰会。与来自 Kotlin 社区的许多风趣的朋友,一同参与了这场封闭式的座谈会。这其间就有德克斯大学的教授 Wiliam Cook,他在会中说到,Kotlin 在科学界没有太多的读者,关于 Kotlin 相关的论文也不多。这个时分我心里默默地问自己——是不是能够做点什么,试着写一些与 Kotlin 相关的内容?
会议完毕的几天之后,我在家里听着旧的 KotlinConf 讲演内容,发现了 Duncan McGregor 提出的,关于缩短 Kotlin 代码巨细和缩短履行时刻的讲演。他点醒了我,让我愈加深化地研讨功能相关的问题,并开端在 Java 和 Kotlin 之间进行比照。
几个月后的现在,我作为一名研讨生,在这儿企图经过 Medium 渠道的这篇文章,来展现我的研讨效果,期望你能喜爱并能从中获益。
研讨问题
在文章开端之前,我想说我的研讨生论文中,研讨的首要有两个问题,可是在这篇文章里边,我先介绍其间的一个——功能相关的问题。
问题:在相同的基准测验内容下,不同的 Java 运行时环境(JRE)关于功能会有显着的差异么?
计算机言语的基准测验游戏动态衡量方针,是出自于现在比较盛行的跨言语基准测验套件之一—— 计算机言语基准测验游戏(CLBG)的思路。
由 Doug Bagley 在 2000 年推出的,致力于比照一切编程言语的计算机项目。直至今日,该项目已经成为了计算机言语的基准测验的一个游戏,也是科学界比较盛行的跨言语基准测验。跟着新的基准测验诞生以及言语完结的增加而不断生长。创建者会体系地进行更新和收拾,来到达盯梢市场趋势的意图(增加新的言语,删去不再运用的言语,并更新要测验的基准测验算法列表)。GLBG 基准测验背面,有一个方针,它的内容与下面这个,来自 4chan 渠道用户提出的问题的答案根本共同:
我的问题是,假如在座有人有过简略的基准测验的经历,能告诉我应该测验哪些内容,以便对每种言语的总体功能有一个简略的了解吗?
为了答复这个问题,GLBG 团队提出了 10 种不同的算法问题。一切的问题以及相关的细节都能够在官网上看到。关于怎么完结这些算法也有较为苛刻的标准,算法终究的效果用什么去比照也是一个问题。这些信息确认之后,相关的问题就能够用指定的言语去完结,然后进行判别。
为了能够客观的点评效果,CLBG 基准测验组运用固定的脚本内容,对待一切的实验一致完结的方针。关于一切指定的言语,完结算法运用的测验值都是独立的。
基准程序的挑选
跟着完结内容的不断开发,计算机言语基准测验游戏现在由 10 个基准程序组成(补白:这个数字跟着时刻也会不断增加)。每一个都供给了一个彻底不同的问题,针对这个问题,运用不同的言语标准、言语特性和办法,企图运用最通用的办法来处理它们。(我不计划介绍 CLGB 每一个基准的内容,假如你有爱好的话,能够到他们的官网查询相关的细节)
咱们实验的首要意图仍是比较 Java 和 Kotlin,为了到达这一点,咱们选用一个取自 CLBG 基准库里边的、由 Java 完结的程序。然后运用 Kotlin 完结两个版别 —— 一个是单纯的转化版别,另一个是针对问题运用 Kotlin 的惯用版别。(鄙人一节会具体介绍)
依据这些假定,咱们依据两个因从来挑选实验中运用的基准:
从 CLBG 存储库中获得的最合适的 Java 程序有必要可转化为 Kotlin 言语。
程序有必要对尽或许多的数据进行操作。
《JVM 保管言语:是否真的说到做到》的论文作者提出了一种区别 CLBG 程序库的区别办法,首要的判别条件是,程序首要操作的是整数、浮点数、指针仍是字符串来区别。这些信息有助于咱们将基准进行分组。
考虑到以上悉数的要素,只要 6 个 GLBG 的基准测验是可行的。为了到达在代码修正量满足小的前提下,Java 代码有必要能够很容易转化成 Kotlin 言语版别这个条件,10 个基准测验中的 4 个被扫除掉了。
int - integer
fp - floating point
ptr - pointer
str - string
表 1: 挑选基准测验,其间包含关于大多数操作数据的信息
补白
在基准挑选之后 (如表 1 所示),终究的完好基准测验组件都不包含首要对字符串资源进行操作的程序。
代码完结
每一个基准测验都包含如下三个完结的版别:
Java 版别
Kotlin 转化的版别
Kotlin 惯用的完结版别
一切的代码都会用于实验——经过外部的 Python 脚本编译、履行和测验。里边没有增加任何有或许影响效果的完结代码。
Kotlin 里边分成了两个版别,首要是我想看一下不同场景下的 Kotlin 版别对效果的影响。单纯转化往后的代码或许跟 Java 的功能更为挨近,字节码的效果也更类似。不过从另一个方面考虑,运用 Kotlin 惯用的完结版别,更能够体现出经历丰富的 Kotlin 程序员代码的比照效果。
假如你对完结的细节感爱好的话,能够检出 Java vs Kotlin 的比照库房。
Java 完结
一切 Java 代码都直接取自计算机言语基准测验游戏库、大多数人运用的实践基准测验完结内容,并没有对代码进行任何的修正。
在咱们的基准测验存储库中,有多个版别的 Java 基准测验。依据 CLBG 网页上的排行榜,实验中运用的代码是功能最优的代码。
Kotlin 转化的完结版别
完结的代码是运用 Java 转 Kotlin 东西协助完结的,东西由 IntelliJ IDEA 供给。对原始的代码进行了一定量的修正,确保代码能够履行。一切的修正都是建立在能够让编译器履行代码的条件下的,所以都是有必要的修正。
一切 Kotlin 转化的完结代码都加入了一个特别的修正。为了运用命令行接口协助咱们编译代码,咱们把 main() 办法提取到了原始文件的外面。
Kotlin 惯用的完结版别
关于 Kotlin 惯用版别的完结,一切修正的内容介绍,都依据以下的几点:
惯用程度 —— 为了罗列 Kotlin 常用的习气列表,这儿运用的链接是 Kotlin 官方文档。
代码的转化状况 —— 其间包含当时 Kotlin 言语引荐的编码风格。该页面也运用了 Kotlin 文档的一部分。
IDEA(编译器)默许的代码查看规矩。
Kotlin 的惯用版别是依据转化后的版别来处理的。
补白
六个基准测验中有五个运用线程并行作业 (Java 和 Kotlin 完结),Kotlin 完结中没有一个运用协程,由于运用协程或许会显着影响功能效果
言语版别及硬件
这儿有必要记载一下履行基准测验时的软件和硬件环境。
履行实验的硬件信息我放在表 2 里边了。之所以挑选 Linux Ubuntu 体系,是由于它被引荐用于 CLBG 丈量脚本的操作体系。
表 3 给出了用于履行基准内容的 Java 和 Kotlin 版别。这两个版别是现在可用的最新版别。
补白
一切的基准测验都是在 Oracle HotSpot VM 上履行的。
动态方针
那么,我应该运用那些方针来进行比照呢?在这儿我决议运用大多数最常用的方针来进行比照:
履行时刻
内存运用状况
CPU 运用状况
每个程序都被履行和丈量了 500 次。
基准测验方针也依据计算机言语基准测验游戏中运用的方针。一切程序都运用专用 CLBG 脚本履行和丈量。
在 Kotlin 和 Java 基准测验组件开发过程中实验了多种丈量的办法,而且这些内容都存在了代码库房里边。开端,运用了 Java/Kotlin 代码和 System 类中的 currentTimeMilis() 或 nanoTime() 等办法来丈量时刻,可是在后期的作业中抛弃了这个主意。由于效果差异很大,后来我决议抛弃这种丈量办法。丈量其他负载方针 (如 CPU 和内存) 也不是很简略,由于环境的原因会受各种要素的影响(深化研讨基准测验办法是一个很长的论题,在此我不想做过多论述)。
在测验过一切的办法往后,咱们决议在这种状况下,运用官方的 CLBG 丈量脚本,由于这个办法最客观。该办法也能够协助咱们,将一切的 Kotlin 丈量定论放在 CLBG 给出的、不同言语的基准评价效果傍边。
有关 Python 脚本和怎么丈量每个参数的具体信息,请参阅 CLBG 页面。
结 果
项目库房中,记载了每个基准测验的一切丈量效果。
履行时刻
下图显现了每个基准测验与每个完结的履行时刻。括号中的字母表明大多数被运用的数据类型:
内存运用状况
下图显现了每个基准测验与每个完结的内存耗费状况。括号中的字母表明大多数被运用的数据类型:
CPU 占用
下表显现了每个基准测验与每个完结中,每个 CPU 核上的 CPU 运用状况:
结 论
下表给出了每个基准测验效果,内存耗费的中位数和履行时刻中位数的比照效果。括号中的字母代表大多数被运用的数据类型:
首要值得注意的是,kotlin 惯用的完结办法,没有在任何丈量的效果中取胜。在本例中,履行时刻的中位数要高于其他的完结。内存耗费也是如此:惯用的完结办法在某些场景下会处于第二的方位,可是从来没有在给定的基准测验中获得榜首的效果。因而,咱们能够得出定论,关于期望在内存办理和最快履行时刻方面获得最佳效果的人来说,运用引荐的技能编写办法,写出来的 Kotlin 代码或许不是最好的。
依据以上的效果,咱们还能够假定,Java 的完结办法,一般更长于办理内存优化和程序履行。在履行时刻上,在 6 个基准测验中 Java 的完结办法有 4 个是最优的。与此同时也在 6 个基准测验中有 4 个到达了最好的内存运用。kotlin 转化的版别,在 Mandelbrot 和 Fannkuch Redux 的测验中,履行时刻更短,在 Fasta 和二叉树基准测验中内存耗费更低。
在二叉树基准测验中,最高与最低的中位数上,履行时刻上的差异是清楚明了的。最佳履行时刻 (Java 版别) 和最差履行时刻 (Kotlin 惯用的版别) 之间的差值为 6.76%。在一些基准测验中,Java、kotlin 转化版别和 Kotlin 惯用完结三个版别,到达了十分类似的效果。在谱范数和曼德尔布罗特中,最高和最低中位数履行时刻的差异小于 1%。
在 Fannkuch Redux 基准测验中,能够看到相当大的内存运用差异。如前所述,Fannkuch Redux 是一个首要处理整数的程序。Java 和 Kotlin 办理整数的办法有本质上的差异。在 Java 中,没有小数部分的数字大多保存为根本数据类型,而 Kotlin 中一切这些数字都有必要封装为整数目标。
在图 3 中,咱们能够看到,与其他完结比较,kotlin 转化的完结版别,内存耗费的中位数要低得多。内存运用中位数之间的差异乃至大于 100MB。本文的研讨并没有对这个效果给出明晰的解说,由于即便静态字节码剖析效果也没有显现出任何有利于 kotlin 转化版别的依据,能供给这么显着的差异。
另一方面,咱们还有终究一个丈量方针 —— CPU 负载。正如上一节所说到的,在特定的丈量值之间没有显着的差异。考虑到使命是随机分配给 CPU 内核的,在大多数状况下,完结之间的差异不超越 3%。由于计算机硬件和软件的不同,在不同的履行级别上,差异也是不一样的。用不同言语完结的基准测验之间的差异或许会大得多。在官方的 CLBG 基准测验页面上,能够查看到相关的 CPU 负载效果的状况,它们之间的差异更大。
凭仗这些信息,咱们能够假定,这两种言语都以类似的办法去加载 CPU。由于在上面的言语和完结办法的比照上,CPU 负载没有显着下降。
问题的答案是什么呢?
在这篇文章开端的时分,我提出了我的研讨问题。写到这儿,应该依据已获得的效果来收拾一下答案了。
问题:在相同的基准测验内容下,不同的 Java 运行时环境(JRE)关于功能会有显着的差异么?
从履行时刻的视点来看,最高和最低的中位数,在运行时刻之间的最大差异能够在 6.7%(支撑 Java 完结二叉树基准测验) 到 1.2%(支撑 kotlin 转化的完结 Fannkuch Redux 基准测验) 之间。Kotlin 的惯用完结版别没有在一个基准测验中到达最佳的履行时刻。Java 代码在 6 个基准测验中有 4 个到达了最佳时刻的中位数,其他的基准测验在 kotlin 转化的代码中履行得最好。
在六个基准测验中,Java 在其间的四个测验中,内存耗费最低。另两个是 kotlin 转化代码的体现更好。这两种完结的最佳内存耗费效果不会与这些代码的最佳履行时刻效果堆叠。内存办理能够在不同的基准测验中,跟着完结言语的不同而改变。在 Fannkuch Redux 中,Java 完结完结了更好的内存运用,乃至到达 13%,但另一方面,与 Java 效果比较,kotlin 转化的二叉树功能提高了 9.7%。
CPU 负载测验效果中,这两种言语在 CPU 负载方面没有显着差异。一切的基准完结都完结了十分类似的效果。
接下来的使命
丈量的方针是动态的。我不是一个计算和数据剖析方面的专家,所以在这些效果中或许还包含了很多的其他定论,可是我并没有逐个提出。假如有人对此感爱好—— 我期望能协助我从我的数据中,剖分出更多的内容。
别的,假如您在我的比照中发现了任何的过错,比如说:过错的完结、中位数的评价等等 —— 请在谈论区, Twitter 或官方的 Kotlin slack (Jakub Aniola) 中联络我。
鄙人一部分中,我将介绍“Java vs Kotlin”静态剖析的办法和效果。我以为下次咱们应该从不同的视点来看看这两种言语之间的差异,从 JVM 字节码的视角。
https://medium.com/@bards95/comparative-evaluation-of-selected-constructs-in-java-and-kotlin-part-1-dynamic-metrics-2592820ce80
点个在看少个 bug