Почему мой System.nanoTime () поврежден?

Самостоятельно и другой разработчик на моем времени, недавно перемещенном от машины Core 2 Duo на работе к новому Core 2 Quad 9505; оба рабочих Windows XP SP3, 32-разрядные с JDK 1.6.0_18.

После выполнения так, несколько наших автоматизированных модульных тестов на некоторое агрегирование синхронизации/статистики/метрик кодируют быстро запущенный сбой, из-за того, что, кажется, смешные значения, возвращающиеся из System.nanoTime ().

Тестовый код, который показывает это поведение, надежно, на моей машине:

import static org.junit.Assert.assertThat;

import org.hamcrest.Matchers;
import org.junit.Test;

public class NanoTest {

  @Test
  public void testNanoTime() throws InterruptedException {
    final long sleepMillis = 5000;

    long nanosBefore = System.nanoTime();
    long millisBefore = System.currentTimeMillis();

    Thread.sleep(sleepMillis);

    long nanosTaken = System.nanoTime() - nanosBefore;
    long millisTaken = System.currentTimeMillis() - millisBefore;

    System.out.println("nanosTaken="+nanosTaken);
    System.out.println("millisTaken="+millisTaken);

    // Check it slept within 10% of requested time
    assertThat((double)millisTaken, Matchers.closeTo(sleepMillis, sleepMillis * 0.1));
    assertThat((double)nanosTaken, Matchers.closeTo(sleepMillis * 1000000, sleepMillis * 1000000 * 0.1));
  }

}

Типичный вывод:

millisTaken=5001
nanosTaken=2243785148

Выполнение его 100x приводит к нано результатам между 33% и 60% фактического времени сна; обычно приблизительно 40% все же.

Я понимаю слабые места в точности таймеров в Windows и имею связанные с чтением потоки, любят, System.nanoTime () последовательный через потоки?, однако мое понимание - то, что System.nanoTime () предназначается для точно цели, мы используем его:-, измеряющий прошедшее время; более точно, чем currentTimeMillis ().

Кто-либо знает, почему это возвращает такие сумасшедшие результаты? Это, вероятно, будет проблемой аппаратной архитектуры (единственная главная вещь, которая изменилась, ЦП/материнская плата на этой машине)? Проблема с Windows HAL с моими текущими аппаратными средствами? Проблема JDK? Я должен отказаться от нановремени ()? Я должен зарегистрировать ошибку где-нибудь или какие-либо предложения о том, как я мог заняться расследованиями далее?

ОБНОВЛЕНИЕ 19/07 03:15 UTC: После попытки тестового сценария finnw ниже я сделал еще некоторый поиск с помощью Google, столкнувшись с записями, такими как bugid:6440250. Это также напомнило мне о некотором другом странном поведении, что я заметил поздно в пятницу, куда ping возвращались отрицательные. Таким образом, я добавил/usepmtimer к своему boot.ini, и теперь все тесты ведут себя как ожидалось., и мои ping нормальны также.

Я немного смущен тем, почему это было все еще проблемой хотя; от моего чтения я думал, что TSC по сравнению с проблемами PMT были в основном разрешены в Windows XP SP3. Это могло быть, потому что моя машина была первоначально SP2, и была исправлена к SP3, а не установлена первоначально как SP3? Я теперь также задаюсь вопросом, должен ли я устанавливать патчи как тот в MS KB896256. Возможно, я должен поднять это с корпоративной настольной командой сборки?

11
задан Community 23 May 2017 в 10:30
поделиться

4 ответа

Проблема была решена (с некоторыми открытыми подозрениями относительно пригодности nanoTime () в многоядерных системах!) Путем добавления / usepmtimer в конец моего C: \ boot.ini строка; заставляет Windows использовать таймер управления питанием, а не TSC. Это открытый вопрос, почему мне нужно было это сделать, учитывая, что я использую XP SP3, поскольку я понял, что это было по умолчанию, однако, возможно, это было связано с тем, как моя машина была исправлена ​​до SP3.

7
ответ дан 3 December 2019 в 09:40
поделиться

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

Вы можете попробовать использовать собственные вызовы для принудительного запуска потока на определенном ядре.

Кроме того, чтобы исключить эффекты управления питанием, попробуйте вращать в цикле в качестве альтернативы sleep () :

import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.W32API;

public class AffinityTest {

    private static void testNanoTime(boolean sameCore, boolean spin)
    throws InterruptedException {
        W32API.HANDLE hThread = kernel.GetCurrentThread();
        final long sleepMillis = 5000;

        kernel.SetThreadAffinityMask(hThread, new NativeLong(1L));
        Thread.yield();
        long nanosBefore = System.nanoTime();
        long millisBefore = System.currentTimeMillis();

        kernel.SetThreadAffinityMask(hThread, new NativeLong(sameCore? 1L: 2L));
        if (spin) {
            Thread.yield();
            while (System.currentTimeMillis() - millisBefore < sleepMillis)
                ;
        } else {
            Thread.sleep(sleepMillis);
        }

        long nanosTaken = System.nanoTime() - nanosBefore;
        long millisTaken = System.currentTimeMillis() - millisBefore;

        System.out.println("nanosTaken="+nanosTaken);
        System.out.println("millisTaken="+millisTaken);
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Sleeping, different cores");
        testNanoTime(false, false);
        System.out.println("\nSleeping, same core");
        testNanoTime(true, false);
        System.out.println("\nSpinning, different cores");
        testNanoTime(false, true);
        System.out.println("\nSpinning, same core");
        testNanoTime(true, true);
    }

    private static final Kernel32Ex kernel =
        (Kernel32Ex) Native.loadLibrary(Kernel32Ex.class);

}

interface Kernel32Ex extends Kernel32 {
    NativeLong SetThreadAffinityMask(HANDLE hThread, NativeLong dwAffinityMask);
}

Если вы получаете очень разные результаты в зависимости от выбора ядра (например, 5000 мс на одном и том же core, но 2200 мс на разных ядрах), что наводит на мысль, что проблема заключается в естественном изменении таймера между ядрами.

Если вы получаете очень разные результаты от сна и от вращения, это, скорее всего, из-за управления питанием, замедляющего тактовую частоту.

Если none из четырех результатов близки к 5000 мс, то это может быть ошибкой.

1
ответ дан 3 December 2019 в 09:40
поделиться

Вы, вероятно, захотите прочитать ответы на этот другой вопрос о переполнении стека: Является ли System.nanoTime () полностью бесполезно? .

Таким образом, похоже, что nanoTime полагается на таймеры операционной системы, на которые может повлиять наличие нескольких ядер ЦП. Таким образом, nanoTime может оказаться бесполезным для определенных комбинаций ОС и ЦП, и следует проявлять осторожность при использовании его в переносимом коде Java, который вы собираетесь запускать на нескольких целевых платформах. Кажется, что в сети много жалоб по этому поводу, но нет единого мнения о значимой альтернативе.

1
ответ дан 3 December 2019 в 09:40
поделиться

В моей системе (64-разрядная Windows 7, Core i7 980X):

nanosTaken=4999902563
millisTaken=5001

System.nanoTime () использует специфичные для ОС вызовы, поэтому я ожидаю, что вы видите ошибку в комбинации Windows и процессора.

2
ответ дан 3 December 2019 в 09:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: