Разница в наносекундной точности между JVM и C ++ - PullRequest
0 голосов
/ 27 апреля 2018

При измерении времени на JVM с помощью System.nanoTime() вы получаете более высокую точность, чем с std::chrono::high_resolution_clock. Как это может быть и есть ли кроссплатформенный способ получить ту же точность в C ++, что и в JVM.

Примеры:

Котлин (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 я получаю аналогичные результаты.

Edit:

Хорошо, исходный C ++ был вычислен с использованием MinGW и g ++ после перехода на MSVC. Я получил такие же результаты с JVM (444,88 нс).

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Ваш пример Java (Kotlin) не измеряет гранулярность наносекунды; в основном он измеряет, сколько времени занимает сборка списка объектов Long. (или разверните кучу, или выделите объекты и заголовки объектов - если вы выполните тест только один раз, он может попытаться собрать мусор, но он не будет успешным, пока работает цикл)

Java работает довольно быстро с выделением памяти, обычно быстрее, чем стандартные библиотеки выделения памяти для C / C ++.

Для C ++ возможно, что, по крайней мере, значительный процент воспринимаемой точности наносекундных часов поступает от вызова push_back 10 миллионов раз для вектора, который включает в себя ряд перераспределений.

Лучшим тестом был бы (Котлин, но то же самое можно сделать для 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")
}

Примечание: это дает мне довольно последовательную точность 32-35 нс в Java, что намного лучше, чем 45-200 нс, которые дал мне ваш исходный код.

Что касается вашего кода C ++, ваш исходный код, работающий на моем MacBookPro, дает мне 68-78 нс (при запуске в цикле, который запускает его 10 раз)

Я также удалил ненужный вектор из вашего кода, а затем он дает результат 50-51 нс, что дает достойное указание на то, что реальная гранулярность составляет 50 нс.

JVM работает несколько лучше, чем 32-35 нс (на 38% лучше, чем 50 нс), но разница не настолько велика, как вы упомянули.

Пожалуйста, попробуйте еще раз и опубликуйте вывод с кодом, который не сохраняет результаты в списке без необходимости, поскольку это сильно влияет на результаты.

#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();
    }
}
0 голосов
/ 27 апреля 2018

Разрешение в c ++ зависит от платформы, но вы можете добиться большей точности, используя вызовы для конкретной платформы (например, clock_gettime из time.h в Linux)

...