2010-07-18 36 views
10

Bản thân tôi và một nhà phát triển khác trên thời gian của tôi gần đây đã chuyển từ máy tính Core 2 Duo sang Core 2 Quad 9505 mới; cả hai đều chạy Windows XP SP3 32 bit với JDK 1.6.0_18.Tại sao System.nanoTime() của tôi bị hỏng?

Khi làm như vậy, một vài thử nghiệm đơn vị tự động của chúng tôi cho một số mã thời gian/thống kê/số liệu tổng hợp bắt đầu thất bại, do những giá trị vô lý trở lại từ System.nanoTime().

mã kiểm tra cho thấy hành vi này, đáng tin cậy, trên máy tính của tôi là:

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)); 
    } 

} 

sản lượng tiêu biểu:

millisTaken=5001 
nanosTaken=2243785148 

Chạy nó sản lượng 100x kết quả nano giữa 33% và 60% so với thực tế giờ ngủ; thường là khoảng 40%.

Tôi hiểu điểm yếu về độ chính xác của bộ tính giờ trong Windows và đã đọc các chủ đề có liên quan như Is System.nanoTime() consistent across threads?, tuy nhiên, hiểu biết của tôi là System.nanoTime() nhằm mục đích chính xác mục đích chúng tôi đang sử dụng: - đo thời gian trôi qua; chính xác hơn currentTimeMillis().

Có ai biết tại sao nó trả về kết quả điên rồ như vậy không? Đây có phải là một vấn đề kiến ​​trúc phần cứng (điều duy nhất đã thay đổi là CPU/Bo mạch chủ trên máy này)? Một vấn đề với Windows HAL với phần cứng hiện tại của tôi? Một vấn đề JDK? Tôi có nên từ bỏ nanoTime() không? Tôi có nên đăng nhập một lỗi ở đâu đó hoặc bất kỳ đề xuất nào về cách tôi có thể điều tra thêm không?

CẬP NHẬT 19/07 03:15 UTC: Sau khi thử trường hợp thử nghiệm của finnw bên dưới, tôi đã thực hiện một số Googling khác, đi qua các mục như bugid:6440250. Nó cũng nhắc nhở tôi về một số hành vi kỳ lạ khác mà tôi nhận thấy vào cuối ngày thứ Sáu, nơi ping đã trở lại tiêu cực. Vì vậy, tôi đã thêm /usepmtimer vào boot.ini của tôi và bây giờ tất cả các thử nghiệm hoạt động như mong đợi., Và ping của tôi cũng bình thường.

Tôi hơi bối rối vì sao đây vẫn là vấn đề; từ đọc sách của tôi, tôi nghĩ rằng TSC vs PMT vấn đề đã được giải quyết phần lớn trong Windows XP SP3. Có thể vì máy của tôi ban đầu là SP2 và được vá thành SP3 thay vì được cài đặt ban đầu là SP3? Tôi bây giờ cũng tự hỏi liệu tôi có nên cài đặt các bản vá lỗi như một cái ở số MS KB896256 hay không. Có lẽ tôi nên thực hiện điều này với nhóm xây dựng máy tính để bàn của công ty?

+0

Bạn có nhận được một máy hoàn toàn mới hoặc máy hiện tại của bạn đã được nâng cấp có giữ cài đặt Windows cũ không? –

+0

Máy hoàn toàn mới; xây dựng lại trên một tiêu chuẩn công ty xây dựng. – Chad

+0

hoạt động tốt cho tôi trong Windows 7 64 bit mới nhất JDK 6. – TofuBeer

Trả lời

5

Vấn đề đã được giải quyết (với một số nghi ngờ cởi mở về sự phù hợp của nanoTime() trên các hệ thống đa lõi!) Bằng cách thêm /usepmtimer đến hết C của tôi: \ boot.ini chuỗi; buộc Windows phải sử dụng bộ hẹn giờ Quản lý nguồn thay vì TSC. Đó là một câu hỏi mở là tại sao tôi cần phải làm điều này cho tôi là trên XP SP3, như tôi hiểu rằng đây là mặc định, tuy nhiên có lẽ đó là do cách thức mà máy của tôi đã được vá đến SP3.

+0

Wow - Tôi vui vì tôi đã tìm thấy bài đăng này - đã có một trang web khách hàng nơi ScheduledExecutorService đã hoàn toàn thoát khỏi đường ray (thời gian còn lại cho đến khi tác vụ được lên lịch tiếp theo ngẫu nhiên đi sai hướng). –

+0

Vui vì nó đã giúp ai đó! Tôi đã mất rất nhiều thời gian để làm điều này :) Tôi cũng tưởng tượng rằng XP ngày càng ít được sử dụng hơn bây giờ vì nó đã được EOLed (đặc biệt là bởi các nhà phát triển) khả năng chẩn đoán nhanh chóng những vấn đề mơ hồ trên bộ khách hàng cũ sẽ giảm dần .... – Chad

1

Bạn có thể muốn đọc câu trả lời cho câu hỏi tràn ngăn xếp khác này: Is System.nanoTime() completely useless?.

Tóm lại, có vẻ như nanoTime phụ thuộc vào bộ hẹn giờ của hệ điều hành có thể bị ảnh hưởng bởi sự hiện diện của nhiều CPU lõi. Như vậy, nanoTime có thể không hữu ích đối với các kết hợp nhất định của hệ điều hành và CPU, và cần lưu ý khi sử dụng nó trong mã Java di động mà bạn dự định chạy trên nhiều nền tảng đích. Dường như có rất nhiều phàn nàn trên trang web về chủ đề này, nhưng không có nhiều sự đồng thuận về một sự thay thế có ý nghĩa.

+2

Đây không phải là bản tóm tắt chính xác. System.nanoTime phụ thuộc vào bộ định thời hệ điều hành cụ thể. Đã có một hoặc hai lỗi trong quá khứ, ví dụ: trên chip Athlon 64 trong Windows, tuy nhiên trên hầu hết các hệ thống bạn có thể dựa vào nanoTime để hoạt động khá tốt. Tôi sử dụng nó cho hoạt hình và thời gian trong các trò chơi đa lõi và chưa bao giờ gặp bất kỳ vấn đề gì. – mikera

+0

Cảm ơn bạn đã làm rõ mikera. Tôi đã cập nhật câu trả lời của mình (hy vọng) cải thiện độ chính xác. – Tom

+0

Cảm ơn Tom. Như tôi đã đề cập trong câu hỏi được cập nhật ở trên, tôi đã xoay xở trở lại hành vi "bình thường" bằng cách buộc sử dụng PMT. Tôi đoán tôi vẫn còn lo lắng về việc liệu điều này sẽ hành xử theo cách chúng ta mong đợi trên nhiều lõi mặc dù. Và có, mà không có một thay thế có ý nghĩa (viết tắt của "quay trở lại currentTimeMillis") thật khó để biết cách tốt nhất để tiến hành! – Chad

2

Trên hệ thống của tôi (Windows 7 64-Bit, Core i7 980X):

nanosTaken=4999902563 
millisTaken=5001 

System.nanoTime() sử dụng các cuộc gọi hệ điều hành cụ thể, vì vậy tôi hy vọng rằng bạn đang nhìn thấy một lỗi trong Windows của bạn/bộ xử lý kết hợp.

+0

Cảm ơn mikera, có vẻ như Windows đang sử dụng kiểu hẹn giờ không hoạt động chính xác trên Core 2 Quad của tôi. Việc buộc nó sử dụng Bộ hẹn giờ quản lý nguồn đã khiến nó hoạt động bình thường trở lại; nhưng tôi không hiểu tại sao tôi phải làm điều này! – Chad

1

Rất khó để biết đây là lỗi hay chỉ là sự thay đổi thời gian bình thường giữa các lõi.

Thử nghiệm bạn có thể thử là sử dụng cuộc gọi gốc để buộc luồng chạy trên một lõi cụ thể.

Ngoài ra, để loại trừ ảnh hưởng quản lý điện năng, hãy thử quay trong một vòng lặp như một thay thế cho 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); 
} 

Nếu bạn nhận được kết quả rất khác nhau tùy thuộc vào lựa chọn lõi (ví dụ 5000ms trên lõi tương tự nhưng 2200ms trên các lõi khác nhau) sẽ gợi ý rằng vấn đề chỉ là sự thay đổi bộ đếm thời gian tự nhiên giữa các lõi.

Nếu bạn nhận được kết quả rất khác so với ngủ so với xoay, có nhiều khả năng là do quản lý nguồn sẽ làm chậm đồng hồ.

Nếu không có trong số bốn kết quả gần 5000 mili giây thì đó có thể là lỗi.

+0

Cảm ơn finnw, điều này là thú vị. kết quả của tôi là: Ngủ, lõi khác nhau nanosTaken = 2049217124 millisTaken = 4985 ngủ, cùng một lõi nanosTaken = 1808868148 millisTaken = 4985 kéo sợi, lõi khác nhau nanosTaken = 5015172794 millisTaken = 5000 kéo sợi, cùng một lõi nanosTaken = 5015295288 millisTaken = 5000 Bạn có nghĩ điều này ngụ ý điều gì đó bị hỏng với quản lý năng lượng trên máy của tôi không? – Chad

+0

Sau khi thực hiện một số đọc thêm, được kích hoạt bởi thử nghiệm của bạn, tôi đã thử khởi động lại máy tính của mình bằng/usepmtimer trong tệp boot.ini. Bây giờ thử nghiệm của bạn, (và thử nghiệm ban đầu của tôi) hoạt động bình thường. Tôi đã chỉnh sửa câu hỏi của mình cho phù hợp. Tôi có nên làm điều này không? – Chad

+0

Nó không nhất thiết phải "hỏng" nhưng rõ ràng là TSC không phù hợp với thời gian chính xác cao trên máy của bạn và sử dụng bộ đếm thời gian PM cho kết quả tốt hơn. Tôi nghĩ/usepmtimer là mặc định trên XP SP3 nhưng kết quả của bạn được đề xuất khác. – finnw

Các vấn đề liên quan