2

I am trying to write a high data rate UDP streaming interface simulator/tester in Java 8 to a realtime machine that has a very accurate time processor card. Every message has a time field in it and this field is in microseconds resolution. The interface relies on the high resolution time processor for packet ordering. The interface relies on the high precision time card which I don't have and need to simulate out of the equation. I figured I could get away with using something like this:

TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

It does work but after running for extended periods of time I found UDP bites me because I send a couple hundred packets out of order with the same exact time stamp and the other side of the interface can't tell that the packets it received were out of order. The interface is tolerant of this to an extent but this isn't really an issue on the real system with the high precision clocks.

To mitigate this I have added a sense of synthetic microseconds to my currentTimeMillis() as follows:

class TimeFactory {
  private long prev;
  private long incr;

  public long now() {
    final long now = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
    long synthNow = now;
    if(now == prev) {
      if(incr < 999) {
        incr += 1;
      }

      synthNow += incr;
    } else {
      incr = 0;
    }

    prev = now;
    return synthNow;
  }
}

Has anyone ever dealt with synthetic time like this? Is there any other way to tighten this code up or even a better way to handle this (using nanoTime somehow)? If I ever did send more then 999 packets would it be safe to increment into the milliseconds range (ie: increment + 1000 or more)? It looks like I am getting around ~10-15ms difference between currentTimeMillis() calls but I'm sure this is very system dependent.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
umhelp
  • 175
  • 8
  • Are you using JIT or just straight Java? Java runs off a bytecode machine: it is as fast as a bytecode machine can be but there are limitations. – cup May 31 '14 at 17:13
  • Are your packets being buffered? That would explain having a bunch with the same timestamp – etherous May 31 '14 at 17:35
  • @cup we are using the standard Oracle 1.8 JRE/JDK we don't specifically turn JIT off so I would imagine it is enabled. – umhelp May 31 '14 at 17:49
  • @etherous I have a multiple threads creating Messages and a DatagramPacket can contain multiple Messages these get pushed to a queue and a single thread taking from the queue and sending. This is for simplicity at the moment, eventually it will be extended to an async system. – umhelp May 31 '14 at 18:19

3 Answers3

2

In case anyone is interested here is what I ended up with to work around the lack of a high resolution system clock. It will give me a synthetic microseconds counter that increments until either System.currentTimeMillis() returns an updated value or you have called this 999 times. In practice I have only seen a maximum of ~500 increments. It doesn't look like I will have worry about spilling into the millisecond range.

I'm still open to other more realistic result alternatives.

public class SyntheticMicrosClock extends Clock {
    private final ZoneId zone;
    private long prev;
    private long incr;

    SyntheticMicrosClock (ZoneId zone) {
        this.zone = zone;
    }
    @Override
    public ZoneId getZone() {
        return zone;
    }
    @Override
    public Clock withZone(ZoneId zone) {
        if (zone.equals(this.zone)) {  // intentional NPE
            return this;
        }
        return new SyntheticMicrosClock(zone);
    }
    public long micros() {
      final long now = TimeUnit.MILLISECONDS.toMicros(millis());
      long synthNow = now;
      if(now == prev) {
        if(incr < 999) {
          incr += 1;
        }

        synthNow += incr;
      } else {
        incr = 0;
      }

      prev = now;
      return synthNow;
    }
    @Override
    public long millis() {
        return System.currentTimeMillis();
    }
    @Override
    public Instant instant() {
        return Instant.ofEpochSecond(0, micros());
    }
}

To use it I inject my synthetic Clock where I need it. Ex:

Clock synthClock = Inject or new SynthClock(ZoneOffset.UTC);
Instant.now(synthClock);
umhelp
  • 175
  • 8
1

Do you need a timestamp or just a high resolution increasing number that it time based? If so, you might be able to use System.nanoTime.

There were issues with this call in early JVM's/OS' but they seem to have been addressed (see first answer here).

Of course there's that odd chance that it might loop around on you. Don't know what kind of flexibility you have with your protocol, but there should be ways to deal with that.

Community
  • 1
  • 1
BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
1

Building on what @Bill suggested, you have 200+ years of resolution with nanoTime, so why not store nanoTime on init, currentTimeMillis on init, then add the difference of nanoTime and initNanoTime to initCurrentTimeMillis to get an augmented, high-precision timestamp? Once you detect clock skew between this augmented clock and the real one over 100ms, or so, you can reinit.

David Ehrmann
  • 7,366
  • 2
  • 31
  • 40