bokutake

bokutake 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

bokutake 关注了标签 · 2019-02-03

关注 2437

bokutake 赞了问题 · 2018-04-10

Java可能比C++快吗?为什么?

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

#include <iostream>
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<<pp_count<<':'<<each<<endl;
            }
        }
    }
    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);
    }
}

执行结果:
Codeblocks
eclipse
同样的算法,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的执行速度。(从797110)

看了一下JIT的相关资料(1,2),感觉就算是这样,也不过就是不经过JVM直接执行了Java代码,这和C++的编译原理不是一样的吗?最多只是持平,怎么还会快这么多呢
其实我不懂怎么反汇编,所以也不知道这怎么回事。我的循环也不是空的。可能的话,我想知道Java的JIT是怎么加快执行这段代码的速度的。


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

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

#include <iostream>
#include <ctime>
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)

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

C++用了43s

Java用了37s

.....
(已经经过多次测试)


追加:
听从Untitled的建议使用clang编译(Raspbian默认没有安装,还得自己apt install clang一下)
速度有了质的飞跃。(但还没越过Java)
不使用优化选项:3m22s(202s)
使用-O2选项:3m05s(185s)(使用-O3与-O2的执行速度是差不多的)

顺带一提,我再次执行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 <jni.h>
#include <android/log.h>
#include <string>

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中对应名称的常量

MODELMANUFACTURERDISPLAYSDK_INTJava耗时C++耗时
GT-I9300samsunglineage_i9300-userdebug 7.1.2 NJH47F 0f9e26b89925192169171928
Redmi 4AXiaomiNJH47F256600931907
m2MeizuFlyme 6.3.0.0A223772234654
A2softwinner升级版四核2G运存19239865202402
Redmi Note 3XiaomiOPM1.171019.018272229918105
TA-1041HMD Global00CN_1_34E263731020234
HTC 802thtcLRX22G release-keys2148211125279

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

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

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

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

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

想知道原因。

关注 14 回答 8

bokutake 回答了问题 · 2018-04-02

解决std::vector<std::string>是否需要手动内存释放?

vector这么做是为了避免频繁分配内存和拷贝。因为vector设计接近数组,要求数据连续存放。如果删除元素就释放内存,以后再添加可能会连续可用空间不够,需要移动到其他的内存区域。因为string内部的字符串数据在堆里,并没有直接放在vector中,而且从vector中删除时会释放掉数据。所以你不必担心vector没有释放的空间,一般都是很小的。除非你的vector的数据量变化幅度极大,一般是不用收缩vector的。

关注 3 回答 3

bokutake 赞了回答 · 2018-03-29

document.body.appendChild()会重新渲染整棵DOM树吗?

本来我觉得既然是appendChild的话是添加为body节点的最后一个子元素应该不会重新渲染整个Dom树,但是就在这个页面中打开控制台试了一下,好像跟我想象的不太一样。

clipboard.png

控制台中干了这么件事
然后在Performance中记录了大概六秒

clipboard.png
可以算出6634/50=132.68
也就是说这段时间里我的interval向body添加了大概132次div,花费在Rendering的时间一共1433.7毫秒(回流?)painting花费了376.2毫秒(重绘?)
但是这应该看不出来是否重新渲染了整个DOM,所以我又接着看

clipboard.png

找了其中一帧吧,在红线标出的这一帧中,Main中有几个事件块,分别是(timerfired)- recalculate style - Layout - update layer tree - paint - composite(综合)layers
其中Layout 和 paint 里查看详情,Layer Root上写的是#document

clipboard.png

就此明了,如果我的操作和理解没有问题的话,document.body.appendChild()会重新渲染整棵DOM树

=================================华丽的转折=============================================
提交回答后感觉越想越不对劲,于是又花时间多试了试,发现几次测试后就算换成了其他的节点上增加还是删除节点,分析中的layer root 都是#document,果然刚才的回答太草率了。
图片描述

从图上可以看到layout root没有变化,只是nodes that need layout 不一样。
然后。。。
图片描述

图片描述
将回答区域的el一个一个移除,查看分析后貌似重绘的只有自己和自己的子节点
不甘心,然后又刷新整个页面看了下
图片描述
页面加载时候的第一次回流应该是需要处理所有节点的吧Nodes that need layout(1059 of 1059)
然后我又按照刚才回答的方法继续在body上appendChild,
查看分析发现

clipboard.png

nodes that need layout 需要layout的nodes少了(换了个页面,所以总元素变成了641)

但愿这次分是是对的,别再说错了影响大家,如果有大手看到了有问题求指正
所以通过这次的折腾我最后的结论是不会重新渲染整个DOM(Chrome浏览器),至少是对于添加到最后的子元素应该不会影响到兄弟节点吧,嗯,应该是这样,忧伤。

======================================PS======================================
emmmmm我觉得自己好啰嗦
不死心,然后又before在前边插,然后挑选body中间的子节点在子节点中插,发现结果大同小异,need layout 值都不高,把原因拽给浏览器优化的结果。
最后写了个计时器丧心病狂的将body隐藏显示隐藏显示,嘿嘿,全部重绘,累死浏览器,哼

clipboard.png

关注 7 回答 5

bokutake 回答了问题 · 2018-03-11

关于如何windows10上 64编译应用Qt

用msvc的就行了,有msvc说明是用VC编译的版本,也就是可执行的dll和头文件。不是C++源代码不需要你再编译的。如果你确定要用Python语言开发,去下PyQt5就好了。

关注 3 回答 2

bokutake 回答了问题 · 2018-01-26

C既然有指针为什么要发明数组?

数组并不是C发明的,C语言1972年才出现。比它早的50、60年代的高级语言BASIC、COBOL、Lisp、FORTRAN、Pascal都有数组的概念。这是高级语言的特性,毕竟同一类型的数据批量处理放在一起是十分基础的需求。相对于汇编语言的内存偏移访问,数组很方便的。
只不过C语言设计时故意把它和指针设计为很接近的使用方式,数组下标等效于指针加偏移。严格意义上数组的实现和指针是有一定区别的。比如全局数组是预分配的在DATA区或者记录在BSS区,局部数组则是栈中的。

关注 15 回答 10

bokutake 赞了回答 · 2018-01-26

C既然有指针为什么要发明数组?

可能有不一样的地方啊,效率和使用方式的难易等等,你这个问题更多的应该从"人"的角度去思考,你说既然哟汇编语言了,为什么还要搞个c语言,其实一切都是为了能方便使用、便于理解、提高效率,从而来降低"人"的成本

关注 15 回答 10

bokutake 赞了回答 · 2018-01-26

解决购物车计算总价,为什么有时候会出现小数?

原因:IEEE754标准
而且简单的乘除也会有问题

0.56*100  // 56.00000000000001

解决:同样建议bignumber.js

关注 11 回答 9

bokutake 赞了回答 · 2018-01-26

解决购物车计算总价,为什么有时候会出现小数?

虽然题主已经采纳了答案了,但是我还是要根据我以往的经验来分享一下
建议使用bignumber这个库。
题主的具体业务可以决定使用 * 10 ^n / 10 ^n的方式,但我就举一个例子,
将320.21小时转换成x天x小时形式,我们希望得到的结果是13天8.21小时

var input = 320.21;
var day = Math.floor(input / 24); // day == 13
var hour = (input - 24 * day) * 100 / 100; //bad
var hour2 = (input * 100 - 24 * day * 100) / 100; // bad
//结果会是类似这个结果:8.209999999999964

那么如果使用bignumber

var input = 320.21;
var bg = new BigNumber(input);
var day = Math.floor(input / 24); 
var hour = bg.minus(day * 24);
console.log(day + 'D' + hour + 'H');

jsfiddle

关注 11 回答 9

bokutake 赞了回答 · 2018-01-26

es6的const定义的常量不能更改该怎么理解?

跟堆与栈没有关系, 真正的区别在于简单数据类型引用数据类型的差异

假设有两个变量aobj,分别是Number和Object类型

var a = 2
var obj = {
    prop: 5
}

严格来说, JS并没有区分什么数据类型放在堆,什么数据类型放在栈(不过你可以这么想, 这里我们就假设简单数据类型是在,引用数据类型是在

整个内存的分配你可以这么假想(实际的比这复杂得多):

  1. 对于a, 就是栈上存一个a变量, a存储的值为2
  2. 对于obj,可以拆分成两部分, 一部分是堆中(也就是{prop: 5})这一部分, 另一部分在栈中(obj这一部分)。
  3. 这里我们不分析堆中的, 只管栈这一部分,那么obj可以看成是一个存储这地址的“简单数据类型”(因为一个地址占的字节数和简单数据类型差不多),obj的值为0x10000000这样
  4. 假设执行obj.prop = 6, 实际上是改变了{prop: 5}这一部分, 而obj的值并没有改变。依然是0x10000000, 数组的原理与之类似
  5. 当你执行obj = {key: 'foo'}, 是在堆中开辟了一个新的对象,然后把地址赋给了obj, 那obj的值肯定就变了, 那以前{prop: 5}这一部分去哪里了呢? 被垃圾回收给清除了

关注 4 回答 3

认证与成就

  • 获得 121 次点赞
  • 获得 7 枚徽章 获得 1 枚金徽章, 获得 2 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-04-28
个人主页被 2.9k 人浏览