c 比java快_Java可能比C++快吗?为什么?

这是一段用C++写的计算十万以内的回文素数算法。

#include

using namespace std;

int main()

{

int input_num=100000;

int pp_count=0;

for(int each=2; each<=input_num; each++)

{

int factorization_lst=0;

for(int factor=1; factor<=each; factor++)

if(each%factor==0&&!(factor>each/factor))

factorization_lst++;

if(factorization_lst==1)

{

int antitone=0,each_cpy=each;

while(each_cpy)

{

antitone=antitone*10+each_cpy%10;

each_cpy/=10;

}

if(antitone==each)

{

pp_count++;

cout<

}

}

}

return 0;

}

稍微做一下修改的Java版,加了计时相关的部分。

public class main {

public static void main(String[] args) {

long start = System.currentTimeMillis();

int input_num = 100000;

int pp_count = 0;

for (int each = 2; each <= input_num; each++) {

int factorization_lst = 0;

for (int factor = 1; factor <= each; factor++)

if (each % factor == 0 && !(factor > each / factor))

factorization_lst++;

if (factorization_lst == 1) {

int antitone = 0, each_cpy = each;

while (each_cpy != 0) {

antitone = antitone * 10 + each_cpy % 10;

each_cpy /= 10;

}

if (antitone == each) {

pp_count++;

System.out.println(pp_count + ":" + each);

}

}

}

System.out.println(System.currentTimeMillis() - start);

}

}

执行结果:

f62fd917d7f2b0493a7728b13ad4a3d4.png

11fd138f56dbaeee6c1bbbef8b5de050.png

同样的算法,C++用了230s,Java只用了124s。这是为什么呢,不是说C++的速度更快吗?

注:运行环境是树莓派3B的官方raspbian(在我的笔记本上运行过,但仅相差一秒不明显,java17s),C++和Java分别用的默认仓库的codeblocks和eclipse(都不是最新版本,eclipse的版本是2012年的3.8.1,codeblocks是2016年的16.01),gcc已经默认开启了-O2优化选项,但还是如此相差悬殊。已经看过类似于这样的解释文章。但还是不太明白。我的代码只有一个main,没有内联函数。Java编译器难道不也是只分指令集的吗,怎么能够编译出更加优化的字节码呢?而且这段代码,Java还能怎么优化呢?

追加:

按照@Untitled(sf没有艾特的功能吗)的提示,做下一个实验证明JIT对Java执行速度的影响。这次使用命令行直接编译,绕过IDE的影响。个人感觉两分钟仅输出百来行的话IO操作对速度的影响可忽略不计。

(由于这次图片屡次上传失败因此只贴出shell相关操作,加上C++编译结果)

pi@raspberrypi:~/workspace/testjava/src $ javac main.java

pi@raspberrypi:~/workspace/testjava/src $ java main

1:2

# 省略计算输出

113:98689

110494

# 110秒,比在eclipse中执行的速度还快,接下来禁用JIT

pi@raspberrypi:~/workspace/testjava/src $ java -Xint main

1:2

# 省略计算输出

113:98689

797514

# 797秒,明显慢于使用JIT的

pi@raspberrypi:~/workspace/testjava/src $

# C++编译

pi@raspberrypi:~/cpplearn $ g++ -o main main.cpp

pi@raspberrypi:~/cpplearn $ time ./main

1:2

# 省略计算输出

113:98689

real 4m5.606s

user 4m5.581s

sys 0m0.000s

#245秒,接下来启用-O2选项

pi@raspberrypi:~/cpplearn $ g++ -O2 -o main main.cpp

pi@raspberrypi:~/cpplearn $ time ./main

1:2

# 省略计算输出

113:98689

real 3m50.631s

user 3m50.384s

sys 0m0.010s

# 230秒,快了一点,和在codeblocks编译的速度差不多

pi@raspberrypi:~/cpplearn $

JIT确实是大幅度提升了Java的执行速度。(从797到110)

看了一下JIT的相关资料(1,2),感觉就算是这样,也不过就是不经过JVM直接执行了Java代码,这和C++的编译原理不是一样的吗?最多只是持平,怎么还会快这么多呢?

其实我不懂怎么反汇编,所以也不知道这怎么回事。我的循环也不是空的。可能的话,我想知道Java的JIT是怎么加快执行这段代码的速度的。

追加:

经过几次实验,发现在x86/x64架构中无论是在Windows还是Linux,实体机还是虚拟机,C++的速度在总体上都比Java更胜一筹。arm的设备我除了树莓派,剩下的只有Android手机了。我准备在一台诺基亚7(骁龙630,4GB,原生Android 8.0,无root,已经尽可能关掉所有后台应用,在我看来是相当稳定的测试环境。)上面进行测试。用来测试的软件有两个在手机上运行的IDEAIDE (用来编译Java代码)和CIDE (用来编译C++代码,编译器为aarch64的gcc7.2)。

由于在CIDE无法显示程序执行时间,因此这次在C++代码也加入了计时。

#include

#include

using namespace std;

int main()

{

clock_t start = clock();

int input_num = 100000;

int pp_count = 0;

for (int each = 2; each <= input_num; each++)

{

int factorization_lst = 0;

for (int factor = 1; factor <= each; factor++)

if (each % factor == 0 && !(factor > each / factor))

factorization_lst++;

if (factorization_lst == 1)

{

int antitone = 0, each_cpy = each;

while (each_cpy)

{

antitone = antitone * 10 + each_cpy % 10;

each_cpy /= 10;

}

if (antitone == each)

{

pp_count++;

cout << pp_count << ':' << each << endl;

}

}

}

cout << 1000*(clock() - start) / CLOCKS_PER_SEC;

return 0;

}

优化选项改成使用-O3(默认为-Os)

40179aba003f443deb1e3bf66e7b2a49.png

执行结果:(这已经是我挑选出来所用时间最短的了)

3566a1bbcca4dce568944c187d8eaa3b.png

C++用了43s

226af30c68caf82803aac3d8f357a3f0.png

Java用了37s

.....

(已经经过多次测试)

追加:

听从Untitled的建议使用clang编译(Raspbian默认没有安装,还得自己apt install clang一下)

速度有了质的飞跃。(但还没越过Java)

不使用优化选项:3m22s(202s)

使用-O2选项:3m05s(185s)(使用-O3与-O2的执行速度是差不多的)

4306a4f69bff973c5386788919631ef7.png

顺带一提,我再次执行java版时去掉计时的那两行代码,

//long start = System.currentTimeMillis();

//System.out.println(System.currentTimeMillis() - start);

然后使用time命令计时,结果时间延长了零点几秒...

追加:

今晚身体不适,但还是抽出一点时间写了Android上的测试应用。(源,下载)

在编写过程中,我已经尽量保证了公平。

因为今晚急着早点休息,暂时未进行充分的测试(但大体上C++比Java快很多)。大家可以自行下载测试一下,晚些时候我再发布一下详细测试结果。

主要代码:

MainActivity.java

package ryuunoakaihitomi.javacppperfcomparison;

import android.annotation.SuppressLint;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.WindowManager;

import android.widget.Toast;

import java.util.Timer;

import java.util.TimerTask;

public class MainActivity extends Activity {

public static final String TAG = "JCPC";

static {

System.loadLibrary("native-lib");

}

@SuppressWarnings("ConstantConditions")

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

setContentView(R.layout.activity_main);

getActionBar().setTitle("logcat -s JCPC");

Log.i(TAG, "Finding palindromic primes within 100,000.(Waiting for 3s)");

new Timer().schedule(new TimerTask() {

@Override

public void run() {

new Thread(new Runnable() {

@Override

public void run() {

final long jTime = pcTimer(true);

final long cTime = pcTimer(false);

runOnUiThread(new Runnable() {

@SuppressLint("DefaultLocale")

@Override

public void run() {

Toast.makeText(getApplicationContext(), String.format("java:%d\ncpp:%d", jTime, cTime), Toast.LENGTH_LONG).show();

finish();

}

});

}

}).start();

}

}, 3000);

}

public native void cpp();

long pcTimer(boolean isJava) {

long lStart = System.currentTimeMillis();

if (isJava)

Java.kernel();

else

cpp();

long lTime = System.currentTimeMillis() - lStart;

Log.i(TAG, "total time:" + lTime);

return lTime;

}

}

Java.java

package ryuunoakaihitomi.javacppperfcomparison;

public class Java {

static {

System.loadLibrary("native-lib");

}

static void kernel() {

int iInputNumber = 100000;

int iPalprimeCount = 0;

for (int iEach = 2; iEach <= iInputNumber; iEach++) {

int iFactorizationList = 0;

for (int iFactor = 1; iFactor <= iEach; iFactor++)

if (iEach % iFactor == 0 && !(iFactor > iEach / iFactor))

iFactorizationList++;

if (iFactorizationList == 1) {

int iAntitone = 0, iEachCopy = iEach;

while (iEachCopy != 0) {

iAntitone = iAntitone * 10 + iEachCopy % 10;

iEachCopy /= 10;

}

if (iAntitone == iEach) {

iPalprimeCount++;

ResultPrint(iPalprimeCount, iEach);

}

}

}

}

public static native void ResultPrint(int c, int e);

}

native-lib.cpp

#include

#include

#include

using namespace std;

void kernel();

void kernel_log(string, int, int);

extern "C" JNIEXPORT void

JNICALL

Java_ryuunoakaihitomi_javacppperfcomparison_MainActivity_cpp(

JNIEnv *,

jobject /* this */) {

kernel();

}

void kernel() {

int input_num = 100000;

int pp_count = 0;

for (int each = 2; each <= input_num; each++) {

int factorization_lst = 0;

for (int factor = 1; factor <= each; factor++)

/*Expression can be simplified to 'factor <= each / factor' less... (Ctrl+F1)

This inspection finds the part of the code that can be simplified, e.g. constant conditions, identical if branches, pointless boolean expressions, etc.*/

if (each % factor == 0 && factor <= each / factor)

factorization_lst++;

if (factorization_lst == 1) {

int antitone = 0, each_cpy = each;

while (each_cpy) {

antitone = antitone * 10 + each_cpy % 10;

each_cpy /= 10;

}

if (antitone == each) {

pp_count++;

kernel_log("c", pp_count, each);

}

}

}

}

void kernel_log(string t, int c, int e) {

__android_log_print(ANDROID_LOG_DEBUG, "JCPC", "%s %d:%d", t.c_str(), c, e);

}

extern "C"

JNIEXPORT void JNICALL

Java_ryuunoakaihitomi_javacppperfcomparison_Java_ResultPrint(JNIEnv *, jobject, jint c,

jint e) {

kernel_log("j", c, e);

}

追加:

准备环境:

测试之前已经完全运行过一次

禁用Xposed,暂时冻结了占用后台的应用,电量至少在30%保证稳定供电

实验三次取各自的最小值,实验结果:

说明:表格前四列的值均来自于android.os.Build中对应名称的常量

MODEL

MANUFACTURER

DISPLAY

SDK_INT

Java耗时

C++耗时

GT-I9300

samsung

lineage_i9300-userdebug 7.1.2 NJH47F 0f9e26b899

25

192169

171928

Redmi 4A

Xiaomi

NJH47F

25

66009

31907

m2

Meizu

Flyme 6.3.0.0A

22

37722

34654

A2

softwinner

升级版四核2G运存

19

239865

202402

Redmi Note 3

Xiaomi

OPM1.171019.018

27

22299

18105

TA-1041

HMD Global

00CN_1_34E

26

37310

20234

HTC 802t

htc

LRX22G release-keys

21

48211

125279

可以看出,绝大多数的arm Android设备运行C++的速度快过Java。但是最后这一行的结果超出了预料。

cef5451071a7947b5acf694d6c7a904d.png

这个设备的CPU是骁龙600。(好奇怪......)

另:我前两天买了一个香橙派zero plus,用的全志H5。C++45s,java70s。

我的所有arm设备已经测试完成,我能不能得到以下结论。

在一小部分的arm指令集架构设备中,Java的运行速度会快于C++。

想知道原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值