Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8901

2019年の後半。いま、高速で生産性の高い開発言語はなに?!旬の言語 C++, C#, Javascript, Python で比較してみた!

$
0
0

はじめに

みなさんこんにちは。ハーツテクノロジーの James です。この記事は業務の中で得られた知見から書かれています。

この記事の目的は、開発言語別の実行速度と、おおざっぱな行数(ステップ数)を把握するのが目的です。以下の4種類の言語で、同じアルゴリズムを実装し、その実行速度とコード行数を測ります。今回、調べたかった開発言語は以下の5つです。

  • C++ / Visual Studio 2019
  • C++ / MinGW(v8.1.0)
  • Javascript / node.js(v10.16.3)
  • C# / Visual Studio 2019
  • Python / Python(v3.7.5)

C++, C#, Javascript, Python の実行速度の計測

計測に使うアルゴリズムには「ライプニッツの公式を使って円周率を求める」を選びました。短いコードですが、それなりにCPUを酷使するので目的は達成できると考えています。

結果的に、コードは3パターン作成し、計測しました。

  • A, わたし(James)が書いた、小数点以下100桁で計算するコード
  • B, 先輩の山崎さんがリファクタリングした、小数点以下20桁で計算するコード
  • C, 同僚のOさんが書いたシンプルにdoubleのみで計算するコードをさらに山崎さんがリファクタリングしたコード

どれも、ライプニッツの公式を使って円周率を計算するコードです。

パターンA わたし(James)が書いた、小数点以下100桁で計算するコード

コードの解説

小数点以下100桁までを、1e8 回繰り返し計算します。

小数点以下100桁まで計算のためpi(π)とval(加減する値(1-1/3+1/5-1/7+1/9-⋯))を使います。小数点以下の桁数は設定できます。

  • 例)1/3(pi も val と同じ大きさ100桁のint)
int val[][0][1][2][3][4][98][99][100]
03333333

1/3と同じ感じの無限小数をvalで保存して、一度は加算(+)一度は減算(-)と繰り返します。piとvalを加減算と同時に一番後から数の上げと下りを計算します。

  • int配列使うの理由

double 型は小数点以下15桁の精度まで計算と表記ができますが、今回はもっと多い桁数を計算して表記したかったので int 配列を作成しました。

コード

A-1. C++(計算時間: VS2019-278.973秒, MinGW-165.098秒)

Leibniz_J.cpp
#include <iostream>
#include <time.h>
#include <cstring>
usingnamespacestd;#define MAX 101 //小数点以下
intpi[MAX]={0};intval[MAX]={0};voidval_def(int*q)//calculate val, ‘4/q’を小数点MAXまで計算{intstart=4;for(inti=0;i<MAX;i++){val[i]=start/*q;start=(start%*q)*10;}}voidcal()//calculate pi{intp=-1,q=1,num=1;while(num<=1e8)//pi = 4 * (1/1 - 1/3 + 1/5 -  ~ 1/(1e8*2-1)){val_def(&q);p*=-1;for(inti=MAX-1;i>=0;i--){if(p==-1&&pi[i]<val[i])//pi[i]-val[i]時val[i]がpiより高い{pi[i-1]--;//piの高桁からpi[i]+10pi[i]+=10;pi[i]+=p*val[i];}elsepi[i]+=p*val[i];if(pi[i]>=10)//(pi[i] >= 10)時に高桁に数を増加{pi[i-1]+=pi[i]/10;pi[i]%=10;}}q+=2;num++;memset(val,0,MAX);}}voidprint_pi(){printf("%d.",pi[0]);for(inti=1;i<MAX;i++){printf("%d",pi[i]);if(i%10==0)printf("  ");if(i%50==0)printf("\n  ");}}intmain(void){doubleresult=0;printf("start\n");clock_tbegin,end;begin=clock();cal();end=clock();print_pi();//use if you want to see 0 ~ MAX PI valueresult+=(double)(end-begin);printf("\n");printf(" %f sec(not include print time.)",result/CLOCKS_PER_SEC);}

c++ 1e8.PNG

A-2. C#(計算時間: 458.992秒)

Leibniz_J.cs
usingSystem;namespaceleibniz{classfunc{staticintMAX=101;//小数点以下 100までint[]pi=newint[MAX];int[]val=newint[MAX];publicvoidval_def(intq){intstart=4;for(inti=0;i<MAX;i++){val[i]=start/q;start=(start%q)*10;}}publicvoidcal(){intp=-1,q=1,num=1;while(num<=1e8)//pi = 4 * (1/1 - 1/3 + 1/5 -  ~ 1/(1e8*2-1)){val_def(q);p*=-1;for(inti=MAX-1;i>=0;i--){if(p==-1&&pi[i]<val[i]){pi[i-1]--;pi[i]+=10;pi[i]+=p*val[i];}elsepi[i]+=p*val[i];if(pi[i]>=10){pi[i-1]+=pi[i]/10;pi[i]%=10;}}q+=2;num++;Array.Clear(val,0,MAX);}}publicvoidprint(){Console.Write(pi[0]+".");for(inti=1;i<MAX;i++){Console.Write(pi[i]);if(i%10==0)Console.Write("  ");if(i%50==0)Console.Write("\n  ");}}}classProgram{staticvoidMain(string[]args){doubleresult=0;DateTimebegin,end;begin=DateTime.Now;func_func=newfunc();_func.cal();end=DateTime.Now;result+=end.Subtract(begin).TotalSeconds;_func.print();//use if you want to see 0 ~ MAX PI valueConsole.WriteLine("\n "+end.Subtract(begin).TotalSeconds+" sec");}}}

c#1e8.PNG

A-3. JavaScript(計算時間: 456.066秒)

Leibniz_J.js
letMAX=101;////小数点以下 100までletpi=Array.apply(null,newArray(MAX)).map(Number.prototype.valueOf,0);letval=Array.apply(null,newArray(MAX)).map(Number.prototype.valueOf,0);letbegin=newDate().getTime();letresult=0;functionval_def(q){letstart=4;for(leti=0;i<MAX;i++){val[i]=Math.floor(start/q);start=(start%q)*10;}}functioncal(){letp=-1,q=1,num=1;while(num<=1e8){//pi = 4 * (1/1 - 1/3 + 1/5 -  ~ 1/(1e8*2-1))val_def(q);p*=-1;for(leti=MAX-1;i>=0;i--){if(p==-1&&pi[i]<val[i]){pi[i-1]--;pi[i]+=10;pi[i]+=p*val[i];}elsepi[i]+=p*val[i];if(pi[i]>=10){pi[i-1]+=parseInt(pi[i]/10);pi[i]%=10;}}q+=2;num++;val=[];}}functionprint(){process.stdout.write(`${pi[0]}.`);for(leti=1;i<MAX;i++){process.stdout.write(`${pi[i]}`);if(i%10==0)process.stdout.write("");if(i%50==0)process.stdout.write("\n");}}cal();letend=newDate().getTime();print();//use if you want to see 0 ~ MAX PI valueresult=(end-begin)/1000;console.log(`\n${(end-begin)/1000} sec`);

js 1e8.PNG

A-4. Python(計算時間: 1148.174秒)

Leibniz_J.py
importtimeMAX=11#遅いため小数点以下 10まで
pi=[0]*MAXval=[0]*MAXdefcal():globalpiglobalvalp=-1q=1num=1whilenum<=1e8:#pi = 4 * (1/1 - 1/3 + 1/5 -  ~ 1/(1e8*2-1))
val_def(q)val=list(reversed(val))p*=-1forindex,iinenumerate(pi):ifp==-1andpi[index]<val[index]:pi[index+1]-=1pi[index]+=10pi[index]+=p*val[index]else:pi[index]+=p*val[index]ifi>=10:pi[index+1]+=pi[index]//10pi[index]%=10q+=2num+=1val=[0]*MAXpi=list(reversed(pi));defval_def(q):globalvalstart=4forindex,iinenumerate(val):val[index]=start//qstart=(start%q)*10defprint_pi():globalpiprint("%d."%pi[0],end="")forindex,iinenumerate(pi):ifindex==0:continueprint(i,end="");if(index%10)==0:print(" ",end="")if(index%50)==0:print("  ")print("")if__name__=='__main__':print("start")begin=time.time()cal()end=time.time()print_pi()print(end-begin," sec")

python 1e8.PNG

パターンB, 先輩の「山崎さん」がリファクタリングした、小数点以下20桁で計算するコード

コードの解説

小数点以下20桁までを、分母が 1e9 になるまで繰り返し計算します。

コード

B-1. C++(計算時間: VS2019-216.595秒, MinGW-134.391秒)

Leibniz_Y.cpp
#include <stdio.h>  // printf()
#include <time.h>   // clock()
#define MAX 21 //小数点以下 20桁
intpi[MAX]={0};voidpi_add(longlong_q,int_a=1){longlongstart=4;// _q は 1e9 まで来る想定なので long 32bit では足りないfor(inti=0;i<MAX;i++){pi[i]+=(start/_q)*_a;start=(start%_q)*10;}}intmain(){printf("start\n");autobegin=clock();// 実行時間を計測for(longlongq=1;q<=1e9;)// pi = 4/1 - 4/3 + 4/5 - 4/7 + ... 4/1e9{pi_add(q);// 加算q+=2;pi_add(q,-1);// 減算q+=2;}// 計算のあと、各桁を 0 - 9 の範囲に調整するfor(inti=MAX-1;i>0;i--)// 0以下の値は、上の桁から拝借してくる{for(;pi[i]<0;){pi[i-1]-=1;pi[i]+=10;}}for(inti=MAX-1;i>0;i--)// 10以上の値は、上の桁に送る{if(pi[i]>=10){pi[i-1]+=pi[i]/10;pi[i]%=10;}}// 表示printf("%d.",pi[0]);for(inti=1;i<MAX;i++){printf("%d",pi[i]);if(i%10==0)printf("  ");}printf("\n");printf(" %f sec.",(double)(clock()-begin)/CLOCKS_PER_SEC);// 実行時間を表示return0;}

1029 cpp o2.PNG

B-2. C#(計算時間: 287.459秒)

Leibniz_Y.cs
usingSystem;namespaceleibniz{classProgram{staticintMAX=21;staticint[]pi=newint[MAX];staticvoidpi_add(long_q,int_a=1){longstart=4;for(inti=0;i<MAX;i++){pi[i]+=(int)(start/_q)*_a;start=(start%_q)*10;}}staticvoidMain(string[]args){Console.WriteLine("start");DateTimebegin=DateTime.Now;for(longq=1;q<=1e9;)// pi = 4/1 - 4/3 + 4/5 - 4/7 + ... 4/1e9{pi_add(q);q+=2;pi_add(q,-1);q+=2;}for(inti=MAX-1;i>0;i--)// 0以下の値は、上の桁から拝借してくる{for(;pi[i]<0;){pi[i-1]-=1;pi[i]+=10;}}for(inti=MAX-1;i>0;i--)// 10以上の値は、上の桁に送る{if(pi[i]>=10){pi[i-1]+=pi[i]/10;pi[i]%=10;}}Console.Write(pi[0]+".");for(inti=1;i<MAX;i++){Console.Write(pi[i]);if(i%10==0)Console.Write("  ");if(i%50==0)Console.Write("\n  ");}Console.WriteLine("\n{0} sec.",DateTime.Now.Subtract(begin).TotalMilliseconds/1000);//時間表示}}}

1028 c# 1e9.PNG

B-3. JavaScript(計算時間: 123.56秒)

Leibniz_Y.js
console.log('start')constbegin=Date.now()// 実行時間を計測constMAX=21// 小数点以下 20桁varpi=Array(MAX).fill(0)functionpi_add(_q,_a=1)// 加算{letstart=4for(leti=0;i<MAX;i++){pi[i]+=Math.floor(start/_q)*_astart=(start%_q)*10}}for(letq=1;q<=1e9;)// pi = 4/1 - 4/3 + 4/5 - 4/7 + ... 4/1e9{pi_add(q)// 加算q+=2pi_add(q,-1)// 減算q+=2}// 計算のあと、各桁を 0 - 9 の範囲に調整するfor(leti=MAX-1;i>0;i--)// 0以下の値は、上の桁から拝借してくる{for(;pi[i]<0;){pi[i-1]-=1pi[i]+=10}}for(leti=MAX-1;i>0;i--)// 10以上の値は、上の桁に送る{if(pi[i]>=10){pi[i-1]+=parseInt(pi[i]/10)pi[i]%=10}}// 表示varr=`${pi[0]}.`for(leti=1;i<MAX;i++){r+=`${pi[i]}`+((i%10==0)?'':'')}console.log(r)console.log(`${(Date.now()-begin)/1000} sec.`)// 実行時間を表示

1028 js 1e9.PNG

B-4. Python(計算時間: 3722.021秒)

Leibniz_Y.py
importtimeprint("start")begin=time.time()# 実行時間を計測
MAX=21# 小数点以下 20桁
pi=[0]*MAXdefpi_add(q,a=1):start=4foriinrange(0,MAX):pi[i]+=start//q*astart=(start%q)*10q=1whileq<=1e9:# pi = 4/1 - 4/3 + 4/5 - 4/7 + ... 4/1e9
pi_add(q)# 加算
q+=2pi_add(q,-1)# 減算
q+=2# 計算のあと、各桁を 0 - 9 の範囲に調整する
foriinrange(MAX-1,0,-1):# 0以下の値は、上の桁から拝借してくる
whilepi[i]<0:pi[i-1]-=1pi[i]+=10foriinrange(MAX-1,0,-1):# 10以上の値は、上の桁に送る
ifpi[i]>=10:pi[i-1]+=pi[i]//10pi[i]%=10# 表示
print("%d."%pi[0],end="")foriinrange(1,MAX):print(pi[i],end="")if(i%10)==0:print("  ",end="")print("")print(time.time()-begin," sec.")# 実行時間を表示

1025 py 1e9.PNG

パターンC, 同僚の「Oさん」が書いたシンプルにdoubleのみで計算するコードをさらに山崎さんがリファクタリングしたコード

3回(小数点以下double(15)まで、1e9まで計算)

コード

C-1. C++(計算時間: VS2019-2.6秒, MinGW-0.608秒)

Leibniz_O.cpp
#include <stdio.h>  // printf()
#include <time.h>   // clock()
intmain(){printf("start\n");autobegin=clock();// 実行時間を計測doublel=0.0;for(doublen=1;n<1e9;)// l = 1/1 - 1/3 + 1/5 - 1/7 + ... 1/1e9{l+=1.0/n;n+=2;l-=1.0/n;n+=2;}printf("%1.16f\n",l*4);printf(" %f sec.",(double)(clock()-begin)/CLOCKS_PER_SEC);// 実行時間を表示return0;}

1029 cpp o2 short.PNG

C-2. C#(計算時間: 1.847秒)

Leibniz_O.cs
usingSystem;namespaceLeibniz{classProgram{publicstaticvoidMain(){System.Console.WriteLine("start");System.DateTimebegin=System.DateTime.Now;// 実行時間を計測doublel=0.0;for(doublen=1;n<1e9;)// l = 1/1 - 1/3 + 1/5 - 1/7 + ... 1/1e9{l+=1.0/n;n+=2;l-=1.0/n;n+=2;}System.Console.WriteLine(l*4);System.Console.WriteLine("{0} sec",System.DateTime.Now.Subtract(begin).TotalMilliseconds/1000);}}}

1028 cs short code.PNG

C-3. JavaScript(計算時間: 0.756秒)

Leibniz_O.js
console.log('start')constbegin=Date.now()// 実行時間を計測varl=0.0for(letn=1;n<1e9;)// l = 1/1 - 1/3 + 1/5 - 1/7 + ... 1/1e9{l+=1.0/nn+=2l-=1.0/nn+=2}console.log(l*4)console.log(`${(Date.now()-begin)/1000} sec.`)// 実行時間を表示

1029 js short.PNG

C-4. Python(計算時間: 121.914秒)

Leibniz_O.py
importtimeprint("start")begin=time.time()# 実行時間を計測
l=0.0n=1whilen<1e9:# l = 1/1 - 1/3 + 1/5 - 1/7 + ... 1/1e9
l+=1.0/nn+=2l-=1.0/nn+=2print(l*4)print(time.time()-begin," sec.")# 実行時間を表示

1028 py short code.PNG

結果

実行環境

  • CPU : Intel Core i7-7500U 2.70GHZ
  • RAM : 8 GB
  • SSD : 250 GB
  • OS : Windows 10 Pro

実行速度(秒)

codeVS2019/C++MinGW/C++node.js/JavascriptVS2019/C#Python
A278.973165.098456.066458.9921148.174
B216.595134.391123.56287.4593722.021
C2.60.6080.7561.847121.914

graph1.PNG

やはり C++ がよい成績を出しました。でも、2番目に Javascript が速いです。これは予想外。。。Python は3位のC#より3倍以上、最大で約100倍遅いです。

graph2.PNG

追加で、MinGW と Visual Studio の速度の違いも意味があると思います。どちらも「O2」最適化オプションで計測しました。

コード量(行数)

コード量はコメント業も空白行も含む純粋な行数です。好みの書き方に左右されるので、あくまでも参考値です。

codeC++JavascriptC#Python
A75567859
B59516341
C24152516

graph-length.PNG

コード量は C++, C# が長めで Python, Javascript 短めに思います。

3つとも Javascript と Python は長さ面で似ていましたが、速度面では反対の感じに結果がでました。

総評

この計測の前に g++(C++ compiler) で「-O2」オプションを使わずに計測しました。その計測のときにはJavascriptが速度面で1位でした。

でも最適化オプション「-O2」を使ったあとで結果が変わりました。

しかし速度だけでなくコード量を一緒に見れば Javascript が合理的な言語だと思いました。

追加テスト

ABC、3つの中で1つ(B,山崎さんのコード)だけ Javascriptが速い結果が出ました。それもなぜJavascriptが速いかが解決してないので簡単なテストをしました。
ループの速度が違うのでループを1回は1e5反復,2回は1e6反復して5回1e9までテストしました。(オプションで -O2使う)

ループ速度

コード

  • C++
test.cpp
#include <iostream>
#include <time.h>
intmain(void){inti=0;doubleval=1e5;//ループ 回数while(i<5){doublea=0;//これのtypeを変えるintj=0;doubleresult=0.0;while(j<5){a=0;autobegin=clock();for(intq=1;q<=val;q++)//これのtypeを変える{a++;}result+=clock()-begin;j++;}printf("long long q ~ 10^%d, double a = %10.0f: %.3f sec \n",i+5,a,(result/5)/CLOCKS_PER_SEC);i++;val*=10;//ループ 回数増加(i=0:1e5, i=1:1e6, i=2:1e7, i=3:1e8, i=4:1e9)}return0;}
MinGW(double a)MinGW(int a)
double a-MinGW.PNGint a-MinGW.PNG
VS2019(double a)VS2019(int a)
double a.PNGint a.PNG
  • Javascript
test.js
leti=0letval=1e5//ループ 回数while(i<5){leta=0letj=0letresult=0.0while(j<5){a=0;letbegin=Date.now()for(letq=1;q<=val;q++){a++}result+=Date.now()-beginj++}console.log(`10^${i+5}: ${((result/5)/1000).toFixed(3)} sec, a = ${a}`)i++val*=10//ループ 回数増加(i=0:1e5, i=1:1e6, i=2:1e7, i=3:1e8, i=4:1e9)}

javascript.PNG

結果

10^9反復する場合だけ結果を整理します。(単位: 秒)

int qdouble qlong long q
MinGW - int a0.7911.1820.622
MinGW - double a1.1661.1921.188
VS2019 - int a4.1881.3744.332
VS2019 - double a4.8551.3655.582
Javascript - type関係ない1.019--

10^9.PNG

総合的にVS2019はループカウント(q)のデータタイプがdouble時には一番速かったけどそれも10^7からはJavascriptより遅くなりました。
でもMinGWはdoubleを使う時だけ(aかqかは関係ない)10^7からJavascriptより遅くなりましたが、doubleを使用しない時(aとq全体)にはJavascriptより全体の場合で速くなりました。
VS2019がループカウント(q)をdoubleで使う時だけMinGWと同じ実行時間が出ましたが、intとlong longを使う時にはVS2019がMinGWより3~4倍ぐらい遅くなりました。

まとめに

まだ結果について明快な解答がありません。でも個人的にJavascriptが結構いい言語ということを知りました。

このあと Python がどこまで発展するかまだわからないが、今は速度が必要で、資金など資源が不足ぎみなら C++ より Javascript を使うのもいいと思いました。

みなさまのご意見もお聞かせください。よろしくお願いいたします。


Viewing all articles
Browse latest Browse all 8901

Trending Articles