Skip to content

Commit

Permalink
Convert Instant+Duration to use a Real for $.tai..
Browse files Browse the repository at this point in the history
This lets us implement a bunch of their functionality with integer math
based on nqp::time returning integer nanoseconds since the epoch instead
of floating point seconds. However, many of the outward-facing outputs
(e.g., `.Str()`) convert to Num/Rat for backwards compatibility.

`my $a; my $s := now; $a := now for ^100_000; say now - $s;` used to
take ~0.9s, but now takes ~0.03s.
  • Loading branch information
MasterDuke17 committed Mar 30, 2021
1 parent acaef29 commit c1f341c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 44 deletions.
32 changes: 22 additions & 10 deletions src/core.c/Duration.pm6
@@ -1,30 +1,42 @@
my class Duration is Cool does Real {
has Rat $.tai is default(0.0);
has Real $.tai is default(0);
# A linear count of seconds.

multi method new(Duration: Rat:D \tai --> Duration:D) {
nqp::p6bindattrinvres(
nqp::create(Duration),Duration,'$!tai',nqp::decont(tai)
nqp::create(Duration),Duration,'$!tai',nqp::decont(tai) * 1000000000
)
}
multi method new(Duration: \value --> Duration:D) {
nqp::istype((my \tai := value.Rat),Failure)
?? tai.throw
!! nqp::p6bindattrinvres(nqp::create(Duration),Duration,'$!tai',tai)
!! nqp::p6bindattrinvres(nqp::create(Duration),Duration,'$!tai',tai * 1000000000)
}

method Bridge(Duration: --> Num:D) { self.defined ?? $!tai.Num !! self.Real::Bridge }
method Num (Duration:D: --> Num:D) { $!tai.Num }
method Rat (Duration:D: --> Rat:D) { $!tai }
method narrow(Duration:D: ) { $!tai.narrow }
method tai(Duration:D: --> Rat:D) {
$!tai / 1000000000
}

method from-posix-nanos(Duration:U: Int:D $nanos --> Duration:D) {
nqp::p6bindattrinvres(nqp::create(Duration),Duration,'$!tai',$nanos)
}

method to-nanos(--> Int:D) {
$!tai.Int
}

method Bridge(Duration: --> Num:D) { self.defined ?? self.tai.Num !! self.Real::Bridge }
method Num (Duration:D: --> Num:D) { self.tai.Num }
method Rat (Duration:D: --> Rat:D) { self.tai }
method narrow(Duration:D: ) { self.tai.narrow }

multi method Str(Duration:D: --> Str:D) { ~$.tai }
multi method Str(Duration:D: --> Str:D) { ~self.tai }

multi method raku(Duration:D: --> Str:D) { "Duration.new({$.tai.raku})" }
multi method raku(Duration:D: --> Str:D) { "Duration.new({($!tai / 1000000000).raku})" }
}

multi sub prefix:<->(Duration:D $a --> Duration:D) {
Duration.new: -$a.tai;
Duration.from-posix-nanos: -$a.to-nanos;
}

multi sub infix:<+>(Duration:D $a, Real $b --> Duration:D) {
Expand Down
78 changes: 44 additions & 34 deletions src/core.c/Instant.pm6
Expand Up @@ -3,45 +3,57 @@ my class DateTime { ... }
my class Duration {... }

my class Instant is Cool does Real {
has Rat $.tai;
# A linear count of seconds since 1970-01-01T00:00:00Z, plus
has Real $.tai is default(0);
# A linear count of nanoseconds since 1970-01-01T00:00:00Z, plus
# Rakudo::Internals.initial-offset. Thus, $.tai matches TAI from 1970
# to the present.

method new(*@) { X::Cannot::New.new(class => self).throw }

method tai(--> Rat:D) {
$!tai / 1000000000
}

method from-posix-nanos(Instant:U: Int:D $nanos --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',$nanos)
}

method to-nanos(--> Int:D) {
$!tai.Int
}

proto method from-posix(|) {*}
multi method from-posix($posix --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
Rakudo::Internals.tai-from-posix($posix,0).Rat)
(Rakudo::Internals.tai-from-posix($posix,0) * 1000000000).Int)
}
multi method from-posix($posix, Bool $prefer-leap-second --> Instant:D) {
# $posix is in general not expected to be an integer.
# If $prefer-leap-second is true, 915148800 is interpreted to
# mean 1998-12-31T23:59:60Z rather than 1999-01-01T00:00:00Z.
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
Rakudo::Internals.tai-from-posix($posix,$prefer-leap-second).Rat)
(Rakudo::Internals.tai-from-posix($posix,$prefer-leap-second) * 1000000000).Int)
}

method to-posix(--> List:D) {
# The inverse of .from-posix, except that the second return
# value is true if *and only if* this Instant is in a leap
# second.
Rakudo::Internals.posix-and-leap-from-tai($!tai)
Rakudo::Internals.posix-and-leap-from-tai($!tai / 1000000000)
}

multi method Str(Instant:D: --> Str:D) {
'Instant:' ~ $!tai
'Instant:' ~ self.tai
}
multi method raku(Instant:D: --> Str:D) {
my ($posix,$flag) = self.to-posix;
'Instant.from-posix(' ~ $posix.raku ~ ($flag ?? ',True)' !! ')')
}
method Bridge(Instant: --> Num:D) { self.defined ?? $!tai.Bridge !! self.Real::Bridge }
method Num (Instant:D: --> Num:D) { $!tai.Num }
method Rat (Instant:D: --> Rat:D) { $!tai }
method Int (Instant:D: --> Int:D) { $!tai.Int }
method narrow(Instant:D: ) { $!tai.narrow }
method Bridge(Instant: --> Num:D) { self.defined ?? self.tai.Bridge !! self.Real::Bridge }
method Num (Instant:D: --> Num:D) { self.tai.Num }
method Rat (Instant:D: --> Rat:D) { self.tai }
method Int (Instant:D: --> Int:D) { self.tai.Int }
method narrow(Instant:D: ) { self.tai.narrow }

method Date(Instant:D: --> Date:D) { Date.new(self) }
method DateTime(Instant:D: --> DateTime:D) { DateTime.new(self) }
Expand All @@ -55,77 +67,75 @@ my class Instant is Cool does Real {
}

multi sub infixcmp»(Instant:D $a, Instant:D $b) {
$a.tai <=> $b.tai }
$a.to-nanos <=> $b.to-nanos }

multi sub infix:«<=>»(Instant:D $a, Instant:D $b) {
$a.tai <=> $b.tai
$a.to-nanos <=> $b.to-nanos
}

multi sub infix==»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai == $b.tai
$a.to-nanos == $b.to-nanos
}

multi sub infix!=»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai != $b.tai
$a.to-nanos != $b.to-nanos
}

multi sub infix<»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai < $b.tai
$a.to-nanos < $b.to-nanos
}

multi sub infix>»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai > $b.tai
$a.to-nanos > $b.to-nanos
}

multi sub infix<=»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai <= $b.tai
$a.to-nanos <= $b.to-nanos
}

multi sub infix>=»(Instant:D $a, Instant:D $b --> Bool:D) {
$a.tai >= $b.tai
$a.to-nanos >= $b.to-nanos
}

multi sub infix:<+>(Instant:D $a, Instant:D $b) {
die "Adding two Instant values has no meaning.
Did you mean to subtract? Perhaps you need to convert to .Numeric first?"
}
multi sub infix:<+>(Instant:D $a, Real:D $b --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
$a.tai + $b.Rat)
Instant.from-posix-nanos($a.to-nanos + ($b * 1000000000).Int)
}
multi sub infix:<+>(Real:D $a, Instant:D $b --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
$a.Rat + $b.tai)
Instant.from-posix-nanos(($a * 1000000000).Int + $b.to-nanos)
}
multi sub infix:<+>(Instant:D $a, Duration:D $b --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
$a.tai + $b.tai)
Instant.from-posix-nanos($a.to-nanos + $b.to-nanos)
}
multi sub infix:<+>(Duration:D $a, Instant:D $b --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
$a.tai + $b.tai)
Instant.from-posix-nanos($a.to-nanos + $b.to-nanos)
}

multi sub infix:<->(Instant:D $a, Instant:D $b --> Duration:D) {
Duration.new: $a.tai - $b.tai;
Duration.from-posix-nanos($a.to-nanos - $b.to-nanos);
}
multi sub infix:<->(Instant:D $a, Real:D $b --> Instant:D) {
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
$a.tai - $b.Rat)
Instant.from-posix-nanos($a.to-nanos - ($b * 1000000000).Int)
}

sub term:<time>(--> Int:D) { nqp::p6box_i(nqp::div_i(nqp::time(),1000000000)) }
sub term:<time>(--> Int:D) { nqp::time() div 1000000000 }
# 37 is $initial-offset from Rakudo::Internals + # of years
# that have had leap seconds so far. Will need to be incremented
# when new leap seconds occur.
my int constant \tai-offset-nanos = 37 * 1000000000;
sub term:<now>(--> Instant:D) {
# FIXME: During a leap second, the returned value is one
# second greater than it should be.
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
Rakudo::Internals.tai-from-posix(nqp::div_n(nqp::time(),1000000000e0),0).Rat)
Instant.from-posix-nanos(nqp::add_i(nqp::time,tai-offset-nanos))
}

Rakudo::Internals.REGISTER-DYNAMIC: '$*INIT-INSTANT', {
PROCESS::<$INIT-INSTANT> :=
nqp::p6bindattrinvres(nqp::create(Instant),Instant,'$!tai',
Rakudo::Internals.tai-from-posix(Rakudo::Internals.INITTIME,0).Rat)
(Rakudo::Internals.tai-from-posix(Rakudo::Internals.INITTIME,0) * 1000000000).Int)
}

# vim: expandtab shiftwidth=4

0 comments on commit c1f341c

Please sign in to comment.