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
.