Observing leap seconds
I came across a blog post by Emil Mikulic on the leap second introduced by the IERS on 1 July 2012. Inspired by Emil Mikulic’s blog post, I created the following short programme.
/*
timekeeping.c --- An implementation inspired by http://unix4lyfe.org/leap-second/
Copyright © 2013, Trond Endrestøl <Trond.Endrestol@ximalas.info>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <errno.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char **argv)
{
struct timespec tv1, tv2;
long double v1, v2;
while (1) {
if (clock_gettime(CLOCK_REALTIME, &tv1) == -1) {
fprintf(stderr,
"%s:%s:%d: clock_gettime(CLOCK_REALTIME, ...) = %s (%d)",
argv[0], __FILE__, __LINE__, strerror(errno), errno);
return EXIT_FAILURE;
} // if
if (clock_gettime(CLOCK_MONOTONIC, &tv2) == -1) {
fprintf(stderr,
"%s:%s:%d: clock_gettime(CLOCK_MONOTONIC, ...) = %s (%d)",
argv[0], __FILE__, __LINE__, strerror(errno), errno);
return EXIT_FAILURE;
} // if
v1 = tv1.tv_sec + (tv1.tv_nsec * 1.0E-9); // CLOCK_REALTIME
v2 = tv2.tv_sec + (tv2.tv_nsec * 1.0E-9); // CLOCK_MONOTONIC
printf("%-*.*Lf %-*.*Lf\n", // field widths and precision gathered from arguments
LDBL_DIG + 1, LDBL_DIG, v1,
LDBL_DIG + 1, LDBL_DIG, v2);
fflush(stdout);
usleep(100000); // sleep 0.1s
} // while
return EXIT_SUCCESS;
} // main()
// timekeeping.c
I’d better run this programme the next time a leap second is introduced, which won’t happen this December.
I compiled the above programme using clang 3.3 on FreeBSD/amd64 9.2-STABLE, and it’s very instructive to review the optimized assembly code generated by using the -O3 and -save-temps options.
.file "timekeeping.i"
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LCPI0_0:
.quad 4472406533629990549 # double 1.0E-9
.text
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0: # %entry
pushq %rbp
.Ltmp3:
.cfi_def_cfa_offset 16
.Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
.Ltmp5:
.cfi_def_cfa_register %rbp
pushq %r15
pushq %r14
pushq %rbx
subq $88, %rsp
.Ltmp6:
.cfi_offset %rbx, -40
.Ltmp7:
.cfi_offset %r14, -32
.Ltmp8:
.cfi_offset %r15, -24
movq %rsi, %r14
leaq -40(%rbp), %rsi
xorl %edi, %edi
callq clock_gettime
cmpl $-1, %eax
je .LBB0_5
# BB#1:
leaq -56(%rbp), %r15
leaq -40(%rbp), %rbx
.align 16, 0x90
.LBB0_2: # %if.end
# =>This Inner Loop Header: Depth=1
movl $4, %edi
movq %r15, %rsi
callq clock_gettime
cmpl $-1, %eax
je .LBB0_3
# BB#4: # %if.end13
# in Loop: Header=BB0_2 Depth=1
cvtsi2sdq -48(%rbp), %xmm0
movsd .LCPI0_0(%rip), %xmm2
mulsd %xmm2, %xmm0
cvtsi2sdq -56(%rbp), %xmm1
addsd %xmm0, %xmm1
movsd %xmm1, -64(%rbp)
cvtsi2sdq -40(%rbp), %xmm0
cvtsi2sdq -32(%rbp), %xmm1
fldl -64(%rbp)
fstpt 16(%rsp)
mulsd %xmm2, %xmm1
addsd %xmm0, %xmm1
movsd %xmm1, -72(%rbp)
fldl -72(%rbp)
fstpt (%rsp)
movl $.L.str3, %edi
movl $19, %esi
movl $18, %edx
movl $19, %ecx
movl $18, %r8d
xorb %al, %al
callq printf
movq __stdoutp(%rip), %rdi
callq fflush
movl $100000, %edi # imm = 0x186A0
callq usleep
xorl %edi, %edi
movq %rbx, %rsi
callq clock_gettime
cmpl $-1, %eax
jne .LBB0_2
.LBB0_5: # %if.then
movq (%r14), %r14
movq __stderrp(%rip), %r15
callq __error
movl (%rax), %edi
callq strerror
movq %rax, %rbx
callq __error
movl (%rax), %eax
movl %eax, (%rsp)
movq %r15, %rdi
movl $.L.str, %esi
movq %r14, %rdx
movl $.L.str1, %ecx
movl $45, %r8d
jmp .LBB0_6
.LBB0_3: # %if.then7
movq (%r14), %r14
movq __stderrp(%rip), %r15
callq __error
movl (%rax), %edi
callq strerror
movq %rax, %rbx
callq __error
movl (%rax), %eax
movl %eax, (%rsp)
movq %r15, %rdi
movl $.L.str2, %esi
movq %r14, %rdx
movl $.L.str1, %ecx
movl $52, %r8d
.LBB0_6: # %if.then
movq %rbx, %r9
xorb %al, %al
callq fprintf
movl $1, %eax
addq $88, %rsp
popq %rbx
popq %r14
popq %r15
popq %rbp
ret
.Ltmp9:
.size main, .Ltmp9-main
.cfi_endproc
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "%s:%s:%d: clock_gettime(CLOCK_REALTIME, ...) = %s (%d)"
.size .L.str, 55
.type .L.str1,@object # @.str1
.L.str1:
.asciz "timekeeping.c"
.size .L.str1, 14
.type .L.str2,@object # @.str2
.L.str2:
.asciz "%s:%s:%d: clock_gettime(CLOCK_MONOTONIC, ...) = %s (%d)"
.size .L.str2, 56
.type .L.str3,@object # @.str3
.L.str3:
.asciz "%-*.*Lf %-*.*Lf\n"
.size .L.str3, 17
.section ".note.GNU-stack","",@progbits
Notice how the error handling of the two clock_gettime calls have been changed to separate code paths with only different arguments for each of the calls to fprintf prior the jump to the common group of instructions finishing the calls to fprintf. Also, the main loop has been optimized somewhat, although the call to get CLOCK_REALTIME is duplicated, once prior to the loop and once again after the call to usleep.