2012-04-03 16 views
7

Esiste un modo rapido per creare un'istanza DateTime e impostare i minuti \ secondi \ millis su 0? In questo momento sto utilizzando il seguente codice:Problema di prestazioni con 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; 
} 

Ma quando si invoca circa 200.000 volte, dateTime.with *** (0); ci vuole un sacco di tempo Probabilmente c'è una soluzione più corretta?

risposta

8

Forse così?

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

Perché è più veloce?

  • mia soluzione utilizza l'aritmetica veloce intero (trascurabile) e 1 unix timestamp normalizzazione (costoso) nel costruttore DateTime
  • la soluzione utilizza 1 unix timestamp normalizzazione (costoso) nel costruttore DateTime e altre 3 normalizzazioni (costoso), ogni volta che si trova una parte data da 0
  • Altre soluzioni possono avere bisogno di meno linee di codice, ma quando si guarda a fonti JodaTime, essi richiedono anche più di 3 normalizzazioni (costoso)

Quindi, probabilmente non puoi battere il modulo. Come altri hanno sottolineato, questo potrebbe portare a risultati errati in casi d'angolo molto remoti in cui le ore non contano 60 secondi (es. A causa di secondi bisestili), anche se non riesco a vedere come, poiché il timestamp unix può sempre essere troncato zero, per ottenere l'inizio di un'ora di calendario (esempi benvenuti).

+0

Non c'è un numero fisso di secondi in un minuto, quindi in alcune occasioni questo sarebbe sbagliato. – Dunes

+0

L'aritmetica, in particolare il calcolo della Modulo, è più veloce del metodo 3 set? Inoltre, date.getTime(), lo hai fatto due volte, quindi 2 getter e un calcolo, è davvero più veloce di 3 setter? E dal momento che sta fissando le ore a 0, non dovrebbe essere anche, 3600000 * 24 – Churk

+0

@Churk: Non preoccuparti di queste cose secondarie. Ciò che è costoso nel metodo 'with' è l'aritmetica data-ora completa. Nota, l'OP non sta impostando le ore a '0' –

5

appena provato il codice qui sotto - è sembra come metodo 1 (il vostro) dura circa 320ms sul mio pc, vs Metodo 2 (la mia) 390ms , vs method 3 (Lukas's) 15ms, vs method 4 (MutableDateTime) 310ms ... Ora il modulo potrebbe (?) portare a risultati errati.

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
ho fatta 10 milioni di corse dopo un warm up giro di 100.000:

Il modulo il metodo vince di un grande margine, quindi sembra sicuro che funzionerà molto meglio nella maggior parte delle situazioni.

+2

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

+1

@artbristol Sì, so per quale motivo ho sottolineato ** come **;) – assylias

+0

Il parametro di input è per 'java.util.Date', non' DateTime' – artbristol

1

Come utilizzare il metodo withTime() della classe DateTime, in modo che sia necessario effettuare una sola chiamata?

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

In modo univoco ** sembra ** correre più lentamente ... – assylias

+0

@assylias Ah si, vedo che è uno dei tuoi casi di test. Questo è davvero strano, specialmente se si guarda al codice sorgente di Joda Time per vedere cosa fanno esattamente tutti quei metodi 'with ...()'. – Jesper

+0

O forse a causa del modo in cui lo collaudo, che è un po 'semplicistico! – assylias

3

Procedimento dateTime.hourOfDay().roundFloorCopy() dovrebbe completano minuto, secondo e milli a zero in una singola chiamata di metodo (ed è il metodo consigliato se le prestazioni non è un problema). Tuttavia, è improbabile che sia più veloce di chiamare direttamente modulo sul millis.

Problemi correlati