Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Collabsphere 2023

    Bob Balaban  August 22 2023 10:08:27 AM
    Greetings, Geeks!

    Just letting you know that I will be attending, and speaking, at next week's CollabSphere 2023, in Chicago.
    The conference begins on Tuesday, Aug. 29, and ends on Thursday the 31st.

    My session (representing HCL) is titled "DEV105 - Introducing the New VoltScript Extensions", on the Thursday. Here's the description from the conference site:

    LotusScript Extensions are at the heart of LotusScript, from the first LSX (lsxbe) to ones developed by business partners. And VoltScript Extensions (VSEs) are at the heart of VoltScript too.
    The classes may be different, and there's no NotesSession as a parent for so many. But while some are new, some will feel familiar.
    Get the lowdown on them and the tool used to scaffold them. Hmmm, I wonder how that was done for LSXs, could they have resurrected the ?

    Many other HCL employees are doing sessions too. Get the full agenda here. Registration and hotel space are still open!

    Hope to see you there.

    Geek ya later!

    This article is ©Copyright 2023 by Looseleaf Software, all rights reserved.

    Greasing the wheels of democracy

    Bob Balaban  January 9 2023 07:22:12 PM
    Greetings, Geeks!

    I feel a little bad about not having posted in a long time (ok, a couple of years), so here's my update:

    I've been semi-retired for 2 1/2 years, having left my full-time job at Veracode. I've been working as a part-time contractor for HCL, often thinking how strange life is -- after a 10-year hiatius from things IBM/Notes/Domino, I'm back working on it again.

    I'm having a great time, working only part-time is perfect for me, and I'm enjoying the interesting work, and the great people at HCL.

    So that's all good. But I also wanted to mention that for several years I've been a volunteer poll worker for the town I live in, Lexington, Massachusetts. Lexington, as some of you will recall from grade-school history lessons, was the locaton of the first battle of the
    American Revolution. Paul Revere, "one if by land, two if by sea", etc, etc. April, 1775. The redcoats came marching out what is now Massachusetts Avenue from Boston, on their way to Concord, where they were supposed to raid a "rebel" arms cache. On their way, they were met
    by a group of farmer/militiamen, called the "Minutemen", on Lexington green. There was a skirmish, a few people were wounded and killed, and the troops continued on to Concord. In Concord, there was another battle, the troops burned a house that contained some muskets and powder,
    and then turned around to march back to their camp in Boston, about 20 miles away.

    On their return journey, they passed again through Lexington, where they again fought with the Minutemen. From there, they continued down what is now Massachusetts Avenue back to Boston, being shot at from behind trees and stone walls all along the way. Heh heh.

    My wife and I have been volunteer workers at the election polls here in town for several years now. It's a long day, doing setup starting at 6am, managing the polls from opening at 7am until 8pm, then processing the ballots and other paperwork until about 9pm each time.

    After a few years of being an "inspector": checking people in and handing out ballots, showing people how to feed their ballots into the tabulating machine, telling people that they're not allowed to wear MAGA hats inside the polling place (a local elementary school gym), etc, we got
    promoted to "management": my wife Irene is now a "warden" (in charge of our precinct polling place), and I'm a "clerk", responsible for the tabulator machine, and signing off on some of the paperwork. Irene is officially my boss on election days. According to Massachusetts law,
    she is empowered to tell the police officer who always works at the polls with us to arrest people, if they are obstructing the voting process or otherwise posing a danger to voters or staff. I think that's so cool, even though she's never had a reason to exercise that particular authority.

    Most voters are perfectly nice and happy to be exercising their democratic right to vote. We poll staffers are strictly non-partisan in our jobs there, and are happy to help anyone cast their ballot. Voters in Massachusetts are not required to present an ID of any kind in order to
    vote. If they offer one, we generally tell them it isn't required, and just ask instead for their name and address, which we verify before handing them a ballot. One young woman berated me one time when I told her I didn't need to see an ID. "That's SO insecure!" she said. I
    replied, "That's the law in Massachusetts, and we follow the law". She voted anyway.

    Sometimes people thank me for working at the polls, which is nice. I usually just say, "Thank you for voting", because it's important that people vote, even though not enough people do it.

    So, VOTE! It's important.

    Geek ya later!

    This article and the attached presentation are ©Copyright 2023 by Looseleaf Software, all rights reserved. You may link to this page, but may not copy without prior approval.


    Presentation: "How to Make Software Projects Fail"

    Bob Balaban  July 31 2019 03:26:24 AM
    Greetings geeks!

    I recently found this old slide deck from a presentation I did at DNUG in 2010.
    (Comment for the humor-impaired: it's sarcastic and meant to be funy.)

    Feel free to add your own tips in a comment.

    Geek ya later!

    Follow me on Twitter @LooseleafLLC
    This article and the attached presentation are ©Copyright 2019 by Looseleaf Software, all rights reserved. You may link to this page, but may not copy without prior approval.


    "Serverless-Boston" meetup news

    Bob Balaban  December 21 2018 05:58:07 PM
    Greetings, geeks!

    I am pleased (and a little proud) to announce that the meetup of which I am co-organizer, Serverless-Boston, was awarded "Super Group" status by meetup.com. Why? My best guess is that in the 2+ years since Wayne Scarano and I started it, we have grown the membership to over 1100, held meetings nearly every month over that entire stretch, and garnered very positive reviews for the content of our meetings.

    We owe thanks to the group membership, who routinely turn out (in all kinds of weather) with enthusiasm, to our many and varied host companies (IBM, Microsoft, Google, Amazon, Pivotal, Oracle, Veracode, Agero and many more. Some have hosted us multiple times), and especially to our excellent speakers.

    We're taking a break for January, and resuming as usual in February at Oracle. Check the meetup page for details, and feel free to join! Serverless is hot tech, and we're there to educate and inform our members in an objective and vendor-agnostic way.

    Geek ya later!

    Follow me on Twitter @LooseleafLLC
    This article ©Copyright 2018 by Looseleaf Software, all rights reserved. You may link to this page, but may not copy without prior approval.


    Check out my presentation from "Serverless-Boston @Veracode" last week

    Bob Balaban  June 25 2018 08:57:39 PM
    Greetings Geeks!

    I've been co-organizer (along with Wayne Scarano) of the Serverless-Boston meet-up for almost 2 years now. Last week I was also the main speaker, talking about a current project I'm working on at Veracode. Check out the slides here, And here's a link to our meetup page.

    Fun stuff, great event, thanks to Veracode for hosting.

    Geek ya later!

    Check out Scott Gray’s blog!

    Bob Balaban  May 2 2018 03:37:19 PM
    Greetings geeks!

    Go check out Scott Gray's blog. He is a highly respected colleague of mine (I hear him laughing...). He's working on a couple of books that should be *very* interesting.

    Enjoy!

    Geek ya later

    There are good things about IBM, this is not one of them

    Bob Balaban  March 31 2018 03:05:58 PM
    Greetings geeks!

    I've had a long association with IBM, going back to my pre-teen years. I have an uncle who worked for IBM in the 1950s and 60s, and he would bring me in to the office on weekends to play with the keypunch and card sorting machines. I became an IBM employee in 1995 when the company I was then working at (Lotus Development Corp./Iris Associates) was bought by Lou Gerstner's IBM in a hostile takeover (hostile in the sense that Lotus senior management did not want to be bought).

    I left Lotus/Iris/IBM in 1997 to write a book and start my own consulting business (Looseleaf Software). I continued a pretty tight association with Lotus/IBM for another 8 years. Looseleaf was an IBM business partner, and most of my consulting work revolved around various IBM technologies. I attended every Lotusphere, and was a speaker at most of them, as well as at other IBM-centric user groups and trade journal conferences. I made a pretty good living, and I worked with lots of people who I liked and admired (and still do). I thought the tech was great, and fun to work with.

    In 2005 I again became an IBM employee, working in the division-that-used-to-be-Lotus. I resigned (again) in 2008 to move on to other companies and other types of work. While I would not claim to be a victim of ageism, one of the things that disenchanted me about IBM in 2007-2008 was the growing drumbeat for re-focusing the software development work we were doing around younger target customers, and around the supposed need to "bring in younger developers". It was never an announced policy, but it was in the air. I'd be in architecture/design meetings and hear senior people say things like, "Our customer developer base is aging out into retirement, and our own developers are too old to relate to a younger customer demographic." It made me pretty angry, though I never felt as though I could (or should) complain about it.

    To be clear: nobody ever told me explicitly that I was too old to work there. I wasn't laid off ("Resource Actioned" in IBM lingo. They don't use "laid off" because that might imply that they have some intention of bringing you back, which they don't). I was told by one development manager that reducing headcount in the US and hiring people in China made "huge business sense -- they're half the price, and most of them studied at American universities!"

    That's all background to this recent article researched by Mother Jones and Pro Publica: Investigation Confirms IBM Layoffs Targeted Older Workers

    My (informed) opinion: IBM behaves disgracefully towards its employees. The widespread mantra of "our employees are our most valuable asset" is true, but they don't really believe it. They (senior IBM management) believe in financial engineering and boosting the stock price at all costs (including reducing employee headcount and shifting jobs to Asia).

    Is it illegal? I don't know. I do know that it ain't right.

    Geek ya later!

    Follow me on Twitter @LooseleafLLC
    This article ©Copyright 2018 by Looseleaf Software, all rights reserved. You may link to this page, but may not copy without prior approval.

      10 years! Go figure...

      Bob Balaban  April 19 2017 04:10:51 PM
      Greetings, Geeks!

      This month marks just over 10 years' existence for this blog. When I started it (after some rather forceful nudging by friends like Rocky Oliver and my then manager at IBM), I had no idea it would carry on for so long.

      I don't create posts here very often, but I hope that you find the content here useful, or at least interesting.

      HUGE shoutout to Rob Novak and the team at SNAPPS for hosting the blog for me. And yes, although I moved out of near-Notes orbit a few years ago, I still maintain the blog using Notes, and SNAPPS hosts it on a Domino server somewhere in the heartland.

      Thanks to all who have participated over the years. I'll keep the conversation going if you will.

      Enjoy!
      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2017 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      Upcoming meetings "Serverless-Boston @"

      Bob Balaban  February 14 2017 07:17:36 PM
      Greetings Geeks!

      We have scheduled some very interesting meet-ups over the next few months:
      • March 16, @AWS, Cambridge
      • April 5, @IBM, Cambridge
      • May 10, @Microsoft, Cambridge

      Our meeting last week @Google (Cambridge) was a great success, we had about 30 attendees and heard a great presentation/demo by Bret McGowen, Developer Evangelist from Google on Firebase and Google Functions.

      See all the details here, or on our meetup web site: https://www.meetup.com/Serverless-Boston/

      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2017 by Looseleaf Software LLC, all rights reserved. You may link to this page, but you may not copy without prior approval.

      Slides from Serverless-Boston meet-up 2 Nov.

      Bob Balaban  November 5 2016 09:23:16 AM
      Greetings, Geeks!

      We had a great meetup on the 2nd (first one for this group) in Boston. Wayne Scarano did a talk on serverless concepts and architecture. I did a demo and walkthrough of an AWS IoT use case (click a button, get an email), utilizing AWS IoT, AWS Lambda, AWS DynamoDb and AWS Simple Notification Service.

      View the slides here: http://sga.com/perspective/

      Enjoy!
      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2016 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      Announcing first 2 "Serverless-Boston" meet-ups

      Bob Balaban  November 1 2016 01:15:15 PM
      Greetings, Geeks!

      My longtime friend Wayne Scarano and I co-founded the "Serverless-Boston" meet-up, to educate ourselves (and others) about microservices, function-oriented ("serverless") architectures on public cloud infrastructures, and other related topics.

      Info here: http://www.meetup.com/Serverless-Boston

      We have 2 meetings scheduled so far. The kickoff is tomorrow (see meetup link for location and agenda), and the next one will be December 7, both in Boston (Fort Point area, thanks to WeWork for providing the space!)

      From the agenda for tomorrow's meeting:
      Serverless is a misnomer because there are servers. We will discuss what Serverless is, how it is part of an evolving abstraction, and what's on the horizon - InterCloud. It's important to have a shared understanding of what Serverless is, so we offer a draft baseline definition as a reference point for future discussions.

      I will be showing an IoT demo: AWS button sending MQTT messages to a Lambda function, which then uses DynamoDb to record hits and SNS to send email. Should be fun.

      Hope to see you there.

      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2016 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      Cool Tech: How to Evaluate Application Performance Monitoring Tools

      Bob Balaban  December 7 2015 09:08:58 PM
      Greetings Geeks!

      I've been working with Application Performance Monitoring tech recently, and I thought I'd share some thoughts with you on how to evaluate different products by conducting a "proof of concept" (POC) trial.

      Lots of tips and hints here. Enjoy!

      Deploying an APM Tool.pdf

      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2015 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      TokenBucket 2.0!

      Bob Balaban  February 7 2015 05:07:16 PM
      Greetings, Geeks!

      I went back and looked at the code I posted here several months ago for doing time-based access counting. I realized that it could be improved and cleaned up just a bit, so I'm sharing a revised version with you here.


      import java.util.Date;
      import java.util.LinkedList;

      public class TimeBasedAccessList {

           // This is the list of time values. New entries are always
           // added at the "tail", so the "head" is always the oldest value.
           // This collection is a little bit more efficient than using (for
           // example) a TreeSet, which will sort new entries into the tree.
           private LinkedList timeSet = new LinkedList();
           private int nCalls;                        // count of allowed calls
           private long timeWindow;        // in millisec
           
           public TimeBasedAccessList(int N, long M) {
                   nCalls = N;
                   timeWindow = M;
           }

           public synchronized boolean isCallOK() {
                   long now = new Date().getTime();
                   long earliest = now - this.timeWindow;
                   
                   // remove entries older than "earliest"
                   // can't use "for" loop with removals inside
                   while (!this.timeSet.isEmpty()) {
                           Long L = this.timeSet.peekFirst();         // "lowest" valued element is earliest timestamp
                           if (L.longValue() <= earliest) {
                                   this.timeSet.remove(L);
                           }
                           else {
                                   // no more entries out of window
                                   break;
                           }
                   } // end for
                   
                   // see how many entries there are
                   if (this.timeSet.size() >= this.nCalls) {
                           return false;
                   }
                   
                   // add new entry at the end of the list
                   this.timeSet.add(new Long(now));
                   return true;
           } // end isCallOK
           
      } // end class

      I really only made 3 changes:
      1) removed "volatile" declaration on the member timeSet. Since this member variable is only ever set one time (at object construction time), there's no point in making it volatile.

      2) I changed the data structure used to store the list of date/time values from a TreeSet to a somewhat simpler LinkedList, which in this application is a bit more efficient. The reason is that with a TreeSet, every time you add a new element, the TreeSet evaluates the "ordinary value" of that element to see where it should sit in the (sorted) tree. We don't really need that functionality here, because we know for sure that newer elements (later time values) are going to be larger than any earlier ones (because time marches on, naturally). So we just add new elements to the end of the list, and examine and/or remove older elements (smaller time values) from the front or head of the list.

      3) Use the LinkedList specific method peekFirst() to look at (but not remove) the element at the head of the list. Note that the remove() and add() methods don't change, they exist in both classes.

      So, there you have it. Enjoy, and Geek ya Later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2015 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      Token bucket!

      Bob Balaban  June 8 2014 05:41:25 AM
      Greetings, geeks!

      I came across this problem on a project I'm doing: given a RESTful API on a web application server, how do you "throttle" calls so that a given authenticated user can only make a certain number of calls (N) per time interval (W), where both N and W are configurable. The solution I came up with is basically a “Token Bucket” with a sliding time window. What surprised me when I got something working was how easy it really was to implement.

      The context in which this bit of code operates is:
       - You know who the caller is (they're already authenticated)
       - The application can maintain a "user context" that you can retrieve on each HTTP "call" or invocation

      The algorithm is as follows:
      Given a per-user TreeSet (a built-in Java collection class that implements a sorted set) containing long values representing java.util.Date time values (milliseconds). The actual set will contain Long values, not long, but that’s transparent to the algorithm. The set contains a list of times the user has invoked the API, within the last interval W

        - When an invocation arrives at the server, set NOW = long value of “now”, from java.util.Date.

       - For the current user’s list of previous call times, do:
           - Inspect the oldest entry, if older than (NOW – W), remove it
           - Until all old entries are removed, or the list is empty.

       - If the number of entries remaining in the set + 1 is > N, return error
       - Else, add NOW to the set and continue

      The application server context is per-user, and you cache a TreeSet instance for each authenticated client in the context. Here's a link to the file:
      TimeBasedAccessList.java

      As it's only 44 lines, here's all the code...

      import java.util.Date;
      import java.util.TreeSet;

      public class TimeBasedAccessList {

            private volatile TreeSet timeSet = new TreeSet();
            private int nCalls;                        // count of allowed calls
            private long timeWindow;        // in millisec
           
            public TimeBasedAccessList(int N, long M) {
                    nCalls = N;
                    timeWindow = M;
            }

            public synchronized boolean isCallOK() {
                    long now = new Date().getTime();
                    long earliest = now - this.timeWindow;
                   
                    // remove entries older than "earliest"
                    // can't use "for" loop with removals inside
                    while (!this.timeSet.isEmpty()) {
                            Long L = this.timeSet.first();         // "lowest" valued element is earliest timestamp
                            if (L.longValue() <= earliest) {
                                    this.timeSet.remove(L);
                            }
                            else {
                                    // no more entries out of window
                                    break;
                            }
                    } // end for
                   
                    // see how many entries there are
                    if (this.timeSet.size() >= this.nCalls) {
                            return false;
                    }
                   
                    // add new entry
                    this.timeSet.add(new Long(now));
                    return true;
            } // end isCallOK
           
      } // end class
      Not much, is it? There's meant to be an instance of the class for each authenticated user. The constructor captures the size of the time window and the number of allowed calls in that time. All the interesting stuff happens in the isCallOK() method. It's possible on a web server that two calls from the same user could come in at the same time, and be dispatched on separate threads. Therefore the isCallOK() method needs to be synchronized to be thread-safe.

      In my particular project, I'm using a Tomcat app server, and the consumer of this class is a Tomcat filter. Filters are cool, you designate one to the server in your application (.WAR file), and Tomcat invokes your filter code before any servlets or JSPs that you have configured. The filter can examine the request data, modify things, do server-wide logging, whatever. In my case, I'm using this particular filter as a gateway: if the user is making too many calls, the filter just returns an HTTP error code (I picked 503 - Unavailable, which usually means "I'm busy, try again later"). If not, then the filter passes control on to either another filter, or to the designated endpoint for the caller's request..

      A few comments on the details of the code...

      The TreeSet object (built-in class in the Java Collections, um, collection) can only store Objects, not scalars, like "long", so we have to convert between "long" and "Long" (the Object version). The only slightly tricky bit of this was looping over the TreeSet and removing entries outside the specified time window. Originally I coded it as a "for" loop: for (Long L : this.timeSet), etc. The problem was that it threw an exception. The reason is documented, though a little obscure. When you do a "for" loop over a collection object, the JVM actually builds the looping functionality using an Iterator instance that it gets from the collection object. That part's fine, but when you use an Iterator (either explicitly by coding it, or invisibly, as with a for loop), you're supposed to make any changes to the contents of the collection (such as removing items) via the Iterator, not directly to the collection object itself. That's because if you don't do it through the Iterator, the Iterator's knowledge of what's in the collection gets out of sync with the collection's actual contents.

      I could have fixed the loop by explicitly getting the Iterator instance and using it to, um, iterate, as well as to remove old entries. But instead I chose to code it by iteratively calling TreeSet.first(), which returns the smallest valued (and in this case, therefore the earliest) entry, or null if the set is empty. The loop exits when the first entry is found that is inside the specified time window, or there are no entries left.

      So, the TreeSet class is the hero here, because it automatically sorts the entries for me. I looked around for a Queue class where I could add new entries to one end, and remove old entries from the other, because that would eliminate the need for sorting overhead. I didn't find one, though, and I was too lazy to write one, so I went with TreeSet. The efficiency/speed of this code is still very good, testing whether the next server invocation for a given user is ok to proceed (calling the isCallOK() method) only consumes a few milliseconds.

      So, there you have it. Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2014 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

      Geek-o-Terica 17: Accuracy vs. Precision, The Time Drift Chronicles

      Bob Balaban  September 8 2013 07:09:08 AM
      Greetings, geeks!

      Ever need a software timer to see how long it took to do something? This is a common task, right? And the basic technique is simple: capture the current time, do your thing, preferably for a while so that you average out a large number of operations to eliminate random perturbations, capture the current time again, and report on the difference between the two times.

      Sounds easy, right? But there are complications. Such as: What is the relative granularity of the time span you are measuring, vs. the granularity of the timer? Here’s a brief anecdote to illustrate what I mean.

      In a previous job I was asked to do benchmarking of the data flowing through a piece of software (it happened to be a server backup system, but that’s not the important point). The test I devised involved copying about 3 terabytes (TB) – that’s 3000 gigabytes – from one place on the network to another, and seeing how long it took. The software I was using to do the data movement had built-in timers and logging, so I didn’t have to do anything special: just set up 3TB of random stuff somewhere, point the software at it and provide a destination location. The log would then contain the elapsed time.

      I knew it would take a while (more than a day, in fact, for reasons I won’t bother to explain here), so I kicked off the test, wrote down the date and time, and went home early (I love those kinds of tests…)

      When I got in the next morning, the test was still running. I hung around for another couple of hours until it was done, wrote down the end date/time, and went looking for the execution log. I noticed two odd things right away: 1) My computed elapsed time, based on writing down the start and end times, was something like 16.5 hours, but the elapsed time in the log was closer to 18 hours; 2) The start and end time recorded in the execution log were in microseconds.

      So the logged results were very precise, but not very accurate. So here’s the first point I want to make about accuracy and precision:

      Point 1 About Accuracy and Precision: For 99% of the real-world “timing” applications you will ever encounter, it makes no sense for the granularity of the event itself to be out of proportion to the granularity of the timer. If the thing you’re timing (copy 3TB of data over a network) takes hours (or days), then what’s the point of timing it in microseconds? Is it going to matter if the resulting elapsed time is 1 day and 2 microseconds, vs. 1 day and 3 microseconds? Very doubtful, as you’re looking at a difference in the 11th decimal place, or thereabouts, which is extremely unlikely to be significant, especially given Point 2.

      Point 2 About Accuracy and Precision: Software timers drift. That is, the “current time” measured in milliseconds, microseconds or nanoseconds is an estimate, not an absolute. The finer-grained (more “precise”) the measurement, the more likely it is to drift from “real” (clock) time as you measure longer and longer intervals. This explains why the log for my experiment was off by a few hours: had the software used millisecond-, or even better, second-level granularity, the results would have been much closer to wall clock time.

      I got to thinking about how I might demonstrate this issue in a simple Java program. Java has two different built-in “timer” capabilities. The java.util.Date class will give you its date/time value in milliseconds, measured since the “reference date” (usually January 1, 1970, 12AM is time 0). So you can create a Date instance as your start time, create one as your end time and subtract the values returned by Date.getTime(). That’s your elapsed time in milliseconds.

      There’s also a nanosecond-level timer in Java (1000 nanoseconds == 1 microsecond), System.nanoTime(). The documentation for this call isn’t precise about when time 0 is for this timer, but I suspect it’s related either to computer boot time, or JVM start time (it would take too many bits to measure the current time as number of nanoseconds since January 1, 1970).

      I wanted to see if the two timer techniques tracked each other over some number of minutes, so I wrote this Java program: NanoTime.java

      The basic idea is to sample the timers (millisec. and nanosec.) every 10 seconds (make the thread sleep in between) or so, and see if they stay about the same relative to each other, or if the difference between them gets bigger as time passes. So, I get the “start” time in both milliseconds and nanoseconds, then loop for N minutes, sleeping for 10 seconds after every iteration of the loop. Inside the loop I get the elapsed time from “start” in both milliseconds and nanoseconds. The “drift” value is computed by simply dividing the nanosecond value by 1000000 (convert to millisec.)  and subtract from the millisecond elapsed time.

      I know the 2 values will never be the same. For one thing, getting the current timer values requires two Java calls. By the time we’ve made the second call, some time has already passed since the first call. But that’s not the important thing. The important thing is to monitor the change in value of “drift” as the program continues to run. If both timers are equally accurate, then the value of “drift” should not vary very much over, say, 10 or 15 minutes.

      In fact, however, it does (on my laptop). Here’s a selection from the program output:

      At time  17:53:22  elapsed millisec = 0 elapsec nanosec = 3124  Drift in millisec = 0
      At time  17:53:32  elapsed millisec = 10120 elapsec nanosec 10119357918  Drift in millisec = 1
      At time  17:53:42  elapsed millisec = 20121 elapsec nanosec 20119601550  Drift in millisec = 2
      At time  17:53:52  elapsed millisec = 30121 elapsec nanosec 30119829118  Drift in millisec = 2
      At time  17:54:02  elapsed millisec = 40122 elapsec nanosec 40120027683  Drift in millisec = 2
      At time  17:54:12  elapsed millisec = 50123 elapsec nanosec 50120249897  Drift in millisec = 3
      At time  17:54:22  elapsed millisec = 60123 elapsec nanosec 60120394024  Drift in millisec = 3

      SNIP…

      At time  18:07:02  elapsed millisec = 820167 elapsec nanosec = 820136832058  Drift in millisec = 31
      At time  18:07:12  elapsed millisec = 830167 elapsec nanosec = 830137065428  Drift in millisec = 30
      At time  18:07:22  elapsed millisec = 840168 elapsec nanosec = 840137282287  Drift in millisec = 31

      SNIP…

      At time  18:23:42  elapsed millisec = 1820224 elapsec nanosec = 1820158420728  Drift in millisec = 66
      At time  18:23:52  elapsed millisec = 1830224 elapsec nanosec = 1830158597874  Drift in millisec = 66
      At time  18:24:02  elapsed millisec = 1840225 elapsec nanosec = 1840158851769  Drift in millisec = 67
      At time  18:24:12  elapsed millisec = 1850226 elapsec nanosec = 1850159074430  Drift in millisec = 67
      Done!

      The “Drift” number is the milliseconds number minus the nanoseconds number (scaled to milliseconds, of course). So the drift values behavior is very interesting. It shows that the elapsed time derived in milliseconds advances faster than the elapsed time derived in nanoseconds. Had the drift remained more or less constant over time, we’d chalk up the difference between the two values to program execution time: it takes a finite amount of time to execute a line of code, and the two time samples can’t occur at exactly the same moment. Plus, of course, other things are going on in the Java VM (garbage collection, perhaps other background thread execution), and on the computer itself (many processes time-sharing the CPUs…). But the fact that the drift value keeps increasing over time indicates a real difference in the sampling techniques.

      I ran the program again on another laptop, and got some very interesting results:

      At  12:37:47  elapsed millisec = 0 elapsec nanosec = 2165  Drift in millisec = 0
      At  12:37:57  elapsed millisec = 10080 elapsec nanosec = 10080167661  Drift in millisec = 0
      At  12:38:07  elapsed millisec = 20080 elapsec nanosec = 20080718639  Drift in millisec = 0
      At  12:38:17  elapsed millisec = 30080 elapsec nanosec = 30081314176  Drift in millisec = -1
      At  12:38:27  elapsed millisec = 40080 elapsec nanosec = 40081868017  Drift in millisec = -1
      At  12:38:37  elapsed millisec = 50080 elapsec nanosec = 50082447490  Drift in millisec = -2

      SNIP…

      At  12:43:47  elapsed millisec = 360080 elapsec nanosec = 360100194438  Drift in millisec = -20
      At  12:43:57  elapsed millisec = 370080 elapsec nanosec = 370100772026  Drift in millisec = -20
      At  12:44:07  elapsed millisec = 380080 elapsec nanosec = 380101345283  Drift in millisec = -21
      At  12:44:17  elapsed millisec = 390080 elapsec nanosec = 390101918540  Drift in millisec = -21
      At  12:44:27  elapsed millisec = 400080 elapsec nanosec = 400102496756  Drift in millisec = -22
      At  12:44:37  elapsed millisec = 410080 elapsec nanosec = 410103064147  Drift in millisec = -23

      SNIP…

      At  12:51:27  elapsed millisec = 820079 elapsec nanosec = 820126346016  Drift in millisec = -47
      At  12:51:37  elapsed millisec = 830079 elapsec nanosec = 830126909636  Drift in millisec = -47
      At  12:51:47  elapsed millisec = 840079 elapsec nanosec = 840127478912  Drift in millisec = -48
      At  12:51:57  elapsed millisec = 850079 elapsec nanosec = 850128065229  Drift in millisec = -49
      At  12:52:07  elapsed millisec = 860079 elapsec nanosec = 860128631991  Drift in millisec = -49
      At  12:52:17  elapsed millisec = 870079 elapsec nanosec = 870129212163  Drift in millisec = -50
      At  12:52:27  elapsed millisec = 880079 elapsec nanosec = 880129787934  Drift in millisec = -50
      At  12:52:37  elapsed millisec = 890079 elapsec nanosec = 890130352950  Drift in millisec = -51
      At  12:52:47  elapsed millisec = 900079 elapsec nanosec = 900130939617  Drift in millisec = -51
      At  12:52:57  elapsed millisec = 910079 elapsec nanosec = 910131482563  Drift in millisec = -52
      At  12:53:07  elapsed millisec = 920079 elapsec nanosec = 920132072303  Drift in millisec = -53
      At  12:53:17  elapsed millisec = 930079 elapsec nanosec = 930132644303  Drift in millisec = -53
      At  12:53:27  elapsed millisec = 940079 elapsec nanosec = 940133205897  Drift in millisec = -54
      At  12:53:37  elapsed millisec = 950079 elapsec nanosec = 950133785300  Drift in millisec = -54
      Done!

      Cool, huh? Similar behavior in that the two values diverge at a more or less constant rate over time. BUT, in the opposite direction! In this case, the nanosecond value advances faster than the millisecond value. Go figure.

      One additional interesting question might be: which value is “right”, or “more accurate”? I don’t have an answer to that question, and within the world of this test program, there’s absolutely no way to tell. What you’d need to do is measure the results of the two sampling techniques against an object outside measurement, a 3rd clock that doesn’t depend on the computer running the Java program. After all, a “gap” of 50-60 milliseconds over a time interval of 30 minutes is a pretty small variation, approximately .003%.

      Geek ya later!

      Follow me on Twitter @LooseleafLLC
      This article ©Copyright 2013 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.