起因

我的一个项目使用 Kotlin 编写,他是一个多维数据库应用程序,所以会非常频繁的操作 int 数组,其中有段程序就需要进行 几亿次的数组清除动作,类似这样的代码:

Arrays.fill(target, 0);

这个Arrays.fill其实就是jdk自带的一个实现,非常简陋,就是一个for循环填充数据。

所以我想改进他,将常见的数组长度编写成单个的实现,比如清除8个长度的方法如下:

fun clear8(target: IntArray) {
if(target.size < 8){
throw IndexOutOfBoundsException()
}
target[0] = 0
target[1] = 0
target[2] = 0
target[3] = 0
target[4] = 0
target[5] = 0
target[6] = 0
target[7] = 0
}

不要怀疑你的眼睛,这样的写法通常是有效的。好的编译器会优化我写的代码,当然,更好的编译器会优化一个简单数组的for循环,这是后话。

那我们就测试一下吧。

import java.util.*
import kotlin.system.measureNanoTime fun main() {
test3()
} private fun test3() {
val size = 8
val time2 = measureNanoTime {
val target = IntArray(size)
for (i in 0 until 10_0000_0000) {
IntArrays.clear8(target)
}
}
println("fill$size $time2") val time1 = measureNanoTime {
val target = IntArray(size)
for (i in 0 until 10_0000_0000) {
Arrays.fill(target, 0)
}
}
println("Arrays.fill$size $time1")
println()
} internal object IntArrays {
fun clear8(target: IntArray) {
if(target.size < 8){
throw IndexOutOfBoundsException()
}
target[0] = 0
target[1] = 0
target[2] = 0
target[3] = 0
target[4] = 0
target[5] = 0
target[6] = 0
target[7] = 0
}
}

测试结果:

fill8                    55,408,200
Arrays.fill8    2,262,171,100

可以看出,使用展开的方式,比java自带的2.2秒,性能提高了40倍!!

与Java的性能对比

我感叹kotlin的编译器真的很强,但仔细一想,不对啊, Kotlin 就是基于 JVM 的,功劳应该是 java 的虚拟机运行时很厉害,所以如果这个程序如果转化为java直接编写是不是更快,至少性能一致吧。说干就干。

//IntArrays.java
import java.util.Arrays; final class IntArrays {
static void clear8(int[] target) {
/* if (target.length < 8){
throw new IndexOutOfBoundsException();
}*/
target[0] = 0;
target[1] = 0;
target[2] = 0;
target[3] = 0;
target[4] = 0;
target[5] = 0;
target[6] = 0;
target[7] = 0;
}
} // IntArraysDemoJava.java
import java.util.Arrays; public final class IntArraysDemoJava {
public static void main(String[] var0) {
test1();
} private static void test1() {
long count = 1000000000;
long start = System.nanoTime();
final int[] target = new int[8]; for(int i = 0; i < count; i++) {
IntArrays.clear8(target);
}
long time2 = System.nanoTime() - start;
System.out.println("fill8 " + time2); start = System.nanoTime();
for(int i = 0; i < count; i++) {
Arrays.fill(target, 0);
} long time1 = System.nanoTime() - start;
System.out.println("Arrays.fill8 " + time1);
System.out.println();
}
}

Java的实现

测试结果如下:

fill8                   2,018,500,800
Arrays.fill8        2,234,306,500

天啊,在java下这种优化几乎没有效果,java我没有找到什么 release编译参数的概念,最多只有debug  = false,我是在gradle中包含

compileJava {
options.debug = false
}

那么就是说,Kotlin生成的字节码要好于 Java生成的字节码?

Java               Kotlin
ALOAD 0 ALOAD 1
ICONST_0 ICONST_0
ICONST_0 ICONST_0
IASTORE ASTORE ALOAD 0 ALOAD 1
ICONST_1 ICONST_1
ICONST_0 ICONST_0
IASTORE IASTORE

字节码稍微不同,你要是问我为什么?  我母鸡啊。。。。。。

与C# 的对比

作为一个 .net 的死忠粉,这个时候就会想着是不是 c# 更快一些,更何况 .net core 3做了大量的性能优化,

class Program {
static void Main(string[] args) {
Test3.test1();
}
} class Test3
{
public static void test1()
{
long count = 1000000000;
var watch = System.Diagnostics.Stopwatch.StartNew();
int[] target = new int[8]; for (int i = 0; i < count; i++)
{
Clear8(target);
}
watch.Stop();
Console.WriteLine("fill8 " + watch.Elapsed); watch.Restart();
for (int i = 0; i < count; i++)
{
Array.Clear(target, 0,8);
} watch.Stop();
Console.WriteLine("Array.Clear8 " + watch.Elapsed);
Console.WriteLine();
} static void Clear8(int[] target)
{
/* if (target.Length < 8)
{
throw new IndexOutOfRangeException();
}*/
target[0] = 0;
target[1] = 0;
target[2] = 0;
target[3] = 0;
target[4] = 0;
target[5] = 0;
target[6] = 0;
target[7] = 0;
}
}

测试成绩:

fill8                     00:00:02.7462676
Array.Clear8      00:00:08.4920514

和Java比起来还要慢,甚至系统自带的Array.clear更加慢,这怎么能让我忍,于是一通的 Span.Fill(0),结果更不理想。

和Nim对比的性能

兴趣提起来了,那就使用C语言实现一个....... 没写出来,我笨......,那就使用 Rust 实现一个,还是没有实现出来,按照教程一步步写,还是没有搞定..........

最后折腾出来一个 Nim 环境,嗯,还是这个简单。

import times, strutils

proc clear8*[int](target: var seq[int]) =
target[0] = 0
target[1] = 0
target[2] = 0
target[3] = 0
target[4] = 0
target[5] = 0
target[6] = 0
target[7] = 0 proc clear*[int](target: var seq[int]) =
for i in 0..<target.len:
target[i] = 0 proc test3() =
const size = 8
var start = epochTime()
var target = newseq[int](size)
for i in 0..<10_0000_0000:
target.clear8() let elapsedStr = (epochTime() - start).formatFloat(format = ffDecimal, precision = 3)
echo "fill8 ", elapsedStr start = epochTime()
for i in 0..<10_0000_0000:
target.clear() let elapsedStr2 = (epochTime() - start).formatFloat(format = ffDecimal, precision = 3)
echo "Arrays.fill ", elapsedStr2 test3()

Nim

测试成绩,注意要加 --release 参数。

fill8 3.499
Arrays.fill 5.825

失望,及其失望。

备注

所有测试是在我的台式机上进行的,配置如下:

AMD Ryzen 5 3600 6 Core 3.59 Ghz

8 GB RAM

Windows 10 64 专业版

所有测试都使用release编译。

一个清除数组的方法在 Kotlin、Java、C#和Nim上的性能测试的更多相关文章

  1. java中远程http文件上传及file2multipartfile

    工作中有时会遇到各种需求,你得变着法儿去解决,当然重要的是在什么场景中去完成. 比如Strut2中file类型如何转换成multipartfile类型,找了几天,发现一个变通的方法记录如下(虽然最后没 ...

  2. atitit.技术选型方法总结为什么java就是比.net有前途

    atitit.技术选型方法总结为什么java就是比.net有前途 #----按照不同的需要有不铜的法... 一般有开发效率,稳定性上的需要.. 作者 老哇的爪子 Attilax 艾龙,  EMAIL: ...

  3. Ant执行一个含有main方法的class文件

    目前需要使用ant来执行一个含有main方法的class文件,并且需要通过命令来行传两个参数(start和end)到main方法. <target name="gsp" de ...

  4. 一个Web Project引用多个Java Project在Eclipse下的配置--转载

    项目结构: 项目由一个Web Project和多个Java Project构成,Web Project需要引用其它Java Project的类和Jar包.开发时用Eclipse3.5和Tomcat调试 ...

  5. Bean-Query 一个把对象转换为Map的Java工具库

    刚开源了一个经过完整測试的Java工具类. 地址例如以下: https://github.com/Jimmy-Shi/bean-query 使用说明例如以下: Bean-query Click Her ...

  6. Java-Runoob-高级教程-实例-方法:14. Java 实例 – Varargs 可变参数使用

    ylbtech-Java-Runoob-高级教程-实例-方法:14. Java 实例 – Varargs 可变参数使用 1.返回顶部 1. Java 实例 - Varargs 可变参数使用  Java ...

  7. Java-Runoob-高级教程-实例-方法:07. Java 实例 – instanceOf 关键字用法

    ylbtech-Java-Runoob-高级教程-实例-方法:07. Java 实例 – instanceOf 关键字用法 1.返回顶部 1. Java 实例 - instanceof 关键字用法   ...

  8. Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖

    ylbtech-Java-Runoob-高级教程-实例-方法:06. Java 实例 – 方法覆盖 1.返回顶部 1. Java 实例 - 方法覆盖  Java 实例 前面章节中我们已经学习了 Jav ...

  9. Java-Runoob-高级教程-实例-方法:05. Java 实例 – 阶乘

    ylbtech-Java-Runoob-高级教程-实例-方法:05. Java 实例 – 阶乘 1.返回顶部 1. Java 实例 - 阶乘  Java 实例 一个正整数的阶乘(英语:factoria ...

  10. Java-Runoob-高级教程-实例-方法:04. Java 实例 – 斐波那契数列

    ylbtech-Java-Runoob-高级教程-实例-方法:04. Java 实例 – 斐波那契数列 1.返回顶部 1. Java 实例 - 斐波那契数列  Java 实例 斐波那契数列指的是这样一 ...

随机推荐

  1. 玩转云端|天翼云边缘安全加速平台AccessOne实用窍门之保障热门产品发售服务安全稳定

    本文分享自天翼云开发者社区<玩转云端|天翼云边缘安全加速平台AccessOne实用窍门之保障热门产品发售服务安全稳定>,作者:天翼云社区官方账号 随着社会经济的发展和人民生活水平的提高,越 ...

  2. 天翼云云电脑:IAAS基础设施带来的计算革新

    本文分享自天翼云开发者社区<天翼云云电脑:IAAS基础设施带来的计算革新>,作者:不知不觉 在当今这个数字化快速发展的时代,云计算作为一种新兴的信息技术,已经逐渐成为企业和个人日常运营的重 ...

  3. 对外提供API,通过appId、appSecret、sign秘钥对接口做鉴权

    一.背景 在接口开发过程中,我们通常不能暴露一个接口给第三方随便调用,要对第三方发来参数进行校验,看是不是具有访问权限. 名词介绍: 1.appId: 应用id,用户自定义命名,如:*-access- ...

  4. RabbitMQ(九)——消息持久化

    RabbitMQ系列 RabbitMQ(一)--简介 RabbitMQ(二)--模式类型 RabbitMQ(三)--简单模式 RabbitMQ(四)--工作队列模式 RabbitMQ(五)--发布订阅 ...

  5. ABB机器人IO板DSQC651维修检查方法

    ABB机器人作为工业自动化的重要设备,其稳定性和可靠性对于生产线的持续运行至关重要.然而,在实际使用中,由于各种原因,可能会出现ABB机器人IO板DSQC651故障,影响机器人的正常运行. 一.ABB ...

  6. MDK Debug时No target connected,STM32 ST-LINK Utility连接不上单片机的解决办法“Can not connect to target!”

    芯片下载程序成功,再次下载时出现,以下错误. 点击确认后,如下提示. 或提示如下. 不管怎么设置都侦测不到芯片. 使用STM32 ST-LINK Utility连接单片机时提示下边错误 "C ...

  7. 【效能提升】测试人员提bug,应该提供哪些信息以便排查问题?

    背景 我们在运维企业级应用时,会遇到很多Bug. 有时候,测试人员或业务方反馈bug,描述得不够详细,我们基于他的描述很难清晰地了解情况,以解决bug. 一般情况下,我们会跟他询问更多的详情,才能知悉 ...

  8. 让 LLM 来评判 | 设计你自己的评估 prompt

    设计你自己的评估 prompt 这是 让 LLM 来评判 系列文章的第三篇,敬请关注系列文章: 基础概念 选择 LLM 评估模型 设计你自己的评估 prompt 评估你的评估结果 奖励模型相关内容 技 ...

  9. 机器学习 | 强化学习(1) | 马尔科夫决策过程(MDP)概论

    最近在搞强化学习(Reinforcement Learning),打算把之前写的笔记整理一下 本文基于大卫 希尔维(David Silver)教授的强化学习概论课程,视频中所采用的样例学生马尔科夫链( ...

  10. 算子 segment_contours_xld

    算子segment_contours_xld 功能:将 XLD 轮廓分割为线段.圆弧或椭圆弧. 签名  segment_contours_xld(Contours : ContoursSplit : ...