JVM和C ++之间的纳秒精度差异

时间:2018-04-27 13:59:14

标签: java c++ kotlin jvm chrono

使用System.nanoTime()在JVM上测量时间时,您获得的精度高于std::chrono::high_resolution_clock。怎么可能并且有一种跨平台的方式来获得与JVM相同的C ++精度。

示例:

Kotlin(JVM):

fun main(args: Array<String>) {
    for (i in 0..10)
        test() // warmup

    println("Average resolution: ${test()}ns")
}

fun test(): Double {
    val timeList = mutableListOf<Long>()

    for (i in 0 until 10_000_000) {
        val time = System.nanoTime()
        if (timeList.isEmpty() || time != timeList.last())
            timeList.add(time)
    }

    return timeList
            .mapIndexed { i, l -> if (i > 0) l - timeList[i - 1] else null }
            .filterNotNull()
            .average()
}

输出:Average resolution: 433.37ns

C ++:

#include <iostream>
#include <chrono>
#include <numeric>
#include <vector>

int main() {
    using namespace std;
    using namespace chrono;

    vector<long long int> time_list;

    for(int i = 0; i < 10'000'000; ++i) {
        auto time = duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count();
        if(time_list.empty() || time != time_list[time_list.size() - 1])
            time_list.push_back(time);
    }

    adjacent_difference(time_list.begin(), time_list.end(), time_list.begin());
    auto result = accumulate(time_list.begin() + 1, time_list.end(), 0.0) / (time_list.size() - 1);

    printf("Average resolution: %.2fns", result);

    return 0;
}

输出:Average resolution: 15625657.89ns编辑:(MinGW g ++) 编辑:输出:Average resolution: 444.88ns(MSVC)

这是在Windows上完成的,但在Linux上我得到了类似的结果。

编辑:

原来的C ++是在切换到MSVC后用MinGW和g ++计算的,我得到了JVM(444.88ns)的平均成绩。

2 个答案:

答案 0 :(得分:1)

您的Java(Kotlin)示例不是测量纳秒粒度;它主要是测量垃圾收集Long对象列表所需的时间。 (或者扩展堆,或者分配对象和对象头 - 如果你只运行一次测试,它可能会尝试垃圾收集,但只要循环运行它就不会成功)

Java的内存分配速度非常快,通常比C / C ++的标准内存分配器库快。

对于C ++,至少有可能纳秒时钟的感知精度的很大一部分来自向量上的push_back 1000次调用,这涉及许多重新分配。

更好的测试是(Kotlin,但C ++也可以这样做) - 不需要记住列表中的时间戳来计算它们之间的平均差异。

fun main(args: Array<String>) {
    for (i in 0 until 10) {
        runTest();
    }
}

fun runTest() {
    var lastTime = System.nanoTime()
    var count = 0;
    var total = 0L;
    for (i in 0 until 50_000_000) {
        val time = System.nanoTime()
        if (time > lastTime) {
            count++;
            total += time - lastTime;
            lastTime = time;
        }
    }

    val result = total / count;

    println("Average resolution: ${result}ns")
}

注意:这使我在Java中具有相当一致的32-35ns精度,远远优于原始代码给我的45-200 ns。

至于你的C ++代码,我在MacBookPro上运行的原始代码给了我68-78ns(当在一个运行它10次的循环中运行时)

我还从您的代码中删除了不必要的向量,然后它给出了50-51ns的结果,这给出了一个体面的指示,即真正的粒度是50ns。

JVM确实比32-35ns(比50ns好38%)要好一些,但是差距并不像你提到的那么大。

请再试一次,并使用不会不必要地将结果存储在列表中的代码发布输出,因为这会极大地影响结果。

#include <iostream>
#include <chrono>
#include <numeric>
#include <vector>


int main1() {
    using namespace std;
    using namespace chrono;

    vector<long long int> time_list;

    long long total = 0;
    int count = 0;
    auto lastTime = duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count();
    for(int i = 0; i < 50000000; ++i) {
        auto time = duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count();
        if (time > lastTime) {
            count++;
            total += time - lastTime;
            lastTime = time; 
        }
    }

    long long result = total / count;

    printf("Average resolution: %.2lld ns\n", result);

    return 0;
}

int main() {
    for (int i = 0; i < 10; i++) {
        main1();
    }
}

答案 1 :(得分:0)

c ++中的分辨率取决于平台,但您可以使用特定于平台的调用(例如,来自Linux上的clock_gettime的{​​{1}})获得更高的准确性