2012-04-03 15 views
7

Czy istnieje jakiś szybki sposób na utworzenie instancji DateTime i ustawienie minut \ sekund \ milis na 0? W tej chwili używam następujący kod:Problem z wydajnością z joda-time DateTime.with *()

private DateTime createDateTime(java.util.Date date, org.joda.time.Chronology chronology) { 
    DateTime dateTime = new DateTime(date, chronology); 
    dateTime = dateTime.withMinuteOfHour(0); 
    dateTime = dateTime.withSecondOfMinute(0); 
    dateTime = dateTime.withMillisOfSecond(0); 
    return dateTime; 
} 

Ale kiedy wywołuje około 200,000 razy, dateTime.with *** (0); zabiera dużo czasu. Prawdopodobnie jest bardziej poprawne rozwiązanie?

Odpowiedz

8

Może tak?

// Truncate minutes/seconds/milliseconds from the date's timestamp 
long truncatedTimestamp = date.getTime() - date.getTime() % 3600000; 
DateTime dateTime = new DateTime(truncatedTimestamp, chronology);  

Dlaczego to jest szybsze?

  • Moje rozwiązanie wykorzystuje szybki arytmetyki liczb całkowitych (nieistotny) i 1 unix timestamp normalizacja (drogie) w konstruktorze DateTime
  • Twoje rozwiązanie wykorzystuje 1 unix timestamp normalizacja (drogie) w konstruktorze DateTime i 3 więcej normalizacji wykorzystano (drogie), za każdym razem ustawić datę pewną część do 0
  • Inne rozwiązania mogą wymagać mniej linii kodu, ale jeśli spojrzeć na źródła JodaTime, wymagają one nawet ponad 3 normalizacji wykorzystano (drogie)

Dlatego prawdopodobnie nie można pokonać modulo. Jak zauważyli inni, może to prowadzić do niepoprawnych wyników w bardzo odległych przypadkach narożnych, gdzie godziny nie liczyć 60 sekund (np. Z powodu sekund przestępnych), chociaż nie widzę jak, ponieważ znacznik czasowy unix zawsze może być obcięty do zero, aby uzyskać początek godziny kalendarzowej (przykłady mile widziane).

+0

Nie ma ustalonej liczby sekund na minutę, więc w niektórych przypadkach byłoby to nieprawidłowe. – Dunes

+0

Czy arytmetyka, a zwłaszcza obliczenia Modulo, są szybsze od 3 metody ustawiania? Również, date.getTime(), zrobiłeś to dwa razy, więc 2 pobierające i obliczenia, czy to naprawdę jest szybsze niż 3 setery? A ponieważ on ustala godziny na 0, czy nie powinno tak być, 3600000 * 24 – Churk

+0

@Curkur: Nie martw się o te drobne rzeczy. To, co jest drogie w metodzie 'with', to pełna arytmetyczna data i godzina. Zauważ, że OP nie ustawia godzin na '0' –

5

prostu próbowałem poniższy kod - to wygląda jak metoda 1 (Twoja) trwa około 320ms na moim komputerze, vs metoda 2 (kopalnia) 390ms , vs metoda 3 (Lukasa) 15 ms, a metoda 4 (MutableDateTime) 310ms ... Teraz modulo może (?) prowadzić do nieprawidłowych wyników.

public class Test { 

    private static int NUM_RUN; 

    public static void main(String[] args) { 

     Date date = new Date(); 
     List<Runnable> list = new ArrayList<>(); 

     list.add(method3Withs(date)); 
     list.add(method1With(date)); 
     list.add(methodModulo(date)); 
     list.add(methodMutable(date)); 

     NUM_RUN = 100_000; 
     for (Runnable r : list) { 
      long start = System.nanoTime(); 
      r.run(); 
      long end = System.nanoTime(); 
      System.out.println((end - start)/1000000); 
     } 

     NUM_RUN = 10_000_000; 
     for (Runnable r : list) { 
      long start = System.nanoTime(); 
      r.run(); 
      long end = System.nanoTime(); 
      System.out.println((end - start)/1000000); 
     } 
    } 

    private static Runnable method3Withs(final Date date) { 
     return new Runnable() { 

      @Override 
      public void run() { 
       DateTime d2 = null; 
       for (int i = 0; i < NUM_RUN; i++) { 
        d2 = new DateTime(date); 
        d2 = d2.withMinuteOfHour(0); 
        d2 = d2.withSecondOfMinute(0); 
        d2 = d2.withMillisOfSecond(0); 
       } 
       System.out.println(d2); 
      } 
     }; 
    } 

    private static Runnable method1With(final Date date) { 
     return new Runnable() { 

      @Override 
      public void run() { 
       DateTime d2 = null; 
       for (int i = 0; i < NUM_RUN; i++) { 
        d2 = new DateTime(date); 
        d2 = d2.withTime(d2.getHourOfDay(), 0, 0, 0); 
       } 
       System.out.println(d2); 
      } 
     }; 
    } 
    private static Runnable methodModulo(final Date date) { 
     return new Runnable() { 

      @Override 
      public void run() { 
       DateTime d2 = null; 
       for (int i = 0; i < NUM_RUN; i++) { 
        long truncatedTimestamp = date.getTime() - date.getTime() % 3600000; 
        d2 = new DateTime(truncatedTimestamp); 
       } 
       System.out.println(d2); 
      } 
     }; 
    } 

    private static Runnable methodMutable(final Date date) { 
     return new Runnable() { 

      @Override 
      public void run() { 
       MutableDateTime m = null; 
       for (int i = 0; i < NUM_RUN; i++) { 
        m = new MutableDateTime(date); 
        m.setMinuteOfHour(0); 
        m.setSecondOfMinute(0); 
        m.setMillisOfSecond(0); 
       } 
       System.out.println(m); 
      } 
     }; 
    } 
} 

EDIT
zrobiłem to 10 milionów biegnie po rozgrzewki rundzie 100000:

modulo metoda wygrywa dużą margines, więc wydaje się, że bezpiecznie jest myśleć, że w większości sytuacji będzie działał znacznie lepiej.

+2

eek microbenchmark! http://stackoverflow.com/q/504103/116509 – artbristol

+1

@artbristol Tak wiem, dlaczego podkreśliłem ** wygląda jak **;) – assylias

+0

Parametr wejściowy jest dla 'java.util.Date', a nie' DateTime' – artbristol

1

Jak korzystać z metody withTime() klasy DateTime, aby wykonać tylko jedno połączenie?

private DateTime createDateTime(java.util.Date date, org.joda.time.Chronology chronology) { 
    DateTime dateTime = new DateTime(date, chronology); 
    return dateTime.withTime(dateTime.getHourOfDay(), 0, 0, 0); 
} 
+0

Nieintuicyjnie ** wydaje się ** działać wolniej ... – assylias

+0

@assylias Ah tak, widzę, że to jeden z twoich przypadków testowych. To naprawdę dziwne, zwłaszcza jeśli spojrzysz na kod źródłowy Joda Time, aby zobaczyć, co dokładnie robią te metody "z ...()". – Jesper

+0

A może ze względu na sposób, w jaki testuję to, co jest nieco uproszczone! – assylias

3

Sposób dateTime.hourOfDay().roundFloorCopy() należy zaokrąglić minuty, sekundy i Milli do zera w jednym wywołaniu metody (i jest zalecanym sposobem, jeśli wydajność nie jest problemem). Jednak jest mało prawdopodobne, aby był szybszy niż wywoływanie modulo bezpośrednio na millis.

Powiązane problemy