起因
私のプロジェクトの1つは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 ターゲット[ 5] = 0 ターゲット[ 6] = 0 ターゲット[ 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 to 10_0000_0000 ){ IntArrays.clear8(target) } } println( "fill $ size $ time2" ) val time1 = measureNanoTime { val target = IntArray(size) for(i in 010_0000_0000まで){ Arrays.fill(target、 0 ) } } println( "Arrays.fill $ size $ time1" ) println() } 内部オブジェクト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アレイ
.fill8 2,262,171,100
展開メソッドを使用すると、パフォーマンスは、Javaに付属している2.2秒の40倍であることがわかります。!
Javaとのパフォーマンス比較
Kotlinのコンパイラーは本当に強力だと嘆いていますが、慎重に検討してください。正しくありません。KotlinはJVMに基づいているため、Javaの仮想マシンランタイムは非常に強力である必要があります。このプログラムをJavaに変換する場合は、直接記述する方が適切です。高速で、少なくとも一貫したパフォーマンス。やるだけ。
// IntArrays.java import java.util.Arrays; 最終 クラス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 ; 長いスタート= System.nanoTime(); final int [] target = new int [8 ]; for(inti = 0; i <カウント; i ++ ){ IntArrays.clear8(target); } long time2 = System.nanoTime()- 開始; System.out.println( "fill8" + time2); start = System.nanoTime(); for(int i = 0; i <count; i ++ ){ Arrays.fill(target、 0 ); } long time1 = System.nanoTime()- 開始; System.out.println( "Arrays.fill8" + time1); System.out.println(); } }
テスト結果は次のとおりです。
fill8 2,018,500,800アレイ
.fill8 2,234,306,500
この種の最適化はJavaではほとんど効果がありません。リリースコンパイルパラメータの概念は見つかりませんでした。せいぜい、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の熱狂的なファンとして、今回は.netコア3が多くのパフォーマンス最適化を行ったことは言うまでもなく、c#の方が速いかどうかを考えます。
クラスProgram { static void Main(string [] args){ Test3.test1(); } } クラス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(); 以下のために(INT iが= 0 ; I <数; I ++ ) { Array.Clear(ターゲット、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 ; ターゲット[ 7 ] = 0; } }
テスト結果:
fill8 00:00:02.7462676
Array.Clear8 00:00:08.4920514
Javaと比較すると、速度はさらに遅く、システムに付属するArray.clearもさらに遅くなります。これにより、どうすれば耐えられるのか、Span.Fill(0)を使用すると、結果はさらに不十分になります。
Nimと比較したパフォーマンス
興味が言及され、それを達成するためにC言語を使用します...書かれていません、私は愚かです...そして、Rustを使用して実装するか、実現されていません。まだ完了していません...
最後に、Nim環境を捨てる、ええと、それはまだ簡単です。
インポート時間、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() letlapsedStr =(epochTime()-start).formatFloat(format = ffDecimal 、精度= 3 ) echo " fill8 " 、expededStr start = epochTime() for i in 0 .. < 10_0000_0000: target.clear() letlapsedStr2=(epochTime()-start).formatFloat(format = ffDecimal、precision = 3 ) echo " Arrays.fill " 、lapsedStr2 test3 ()
テスト結果については、-releaseパラメータの追加に注意してください。
fill8 3.499
Arrays.fill 5.825
失望、そしてその失望。
備考
すべてのテストはデスクトップコンピューターで行われ、構成は次のとおりです。
AMD Ryzen 5 3600 6コア3.59 Ghz
8 GBのRAM
Windows 10 64 Professional Edition
すべてのテストはreleaseを使用してコンパイルされています。