• May the numbers speak

    From zbigniew2011@zbigniew2011@gmail.com (LIT) to comp.lang.forth on Wed Jun 11 13:54:53 2025
    From Newsgroup: comp.lang.forth

    Let's find out the difference between my
    and "Mark Twain" 's approach to the solution
    of "parsing 'time string' task". For simplicity
    I'll do everything using DX Forth.

    My solution is:

    VARIABLE C6
    VARIABLE C1

    : TIMESTRSCAN ( addr count -- d )
    1 C6 ! 1 C1 !
    >R >R 0 0 R> R>
    OVER + 1-
    DO
    I C@ DUP 58 =
    IF
    DROP
    C6 @ 60 * C6 !
    1 C1 !
    ELSE
    48 - C1 @ * C6 @ M* D+
    10 C1 !
    THEN
    -1 +LOOP
    ;

    (the only change for ANS-Forth were
    slightly different two VARIABLE lines,
    I have no idea what kind of problem Ed
    met with the loop)


    Mr. Fifo's proposal:

    : dtimescan
    over swap chars + >r >r 0. r> 0 begin
    over r@ <
    while
    over c@ [char] : =
    if
    swap >r s>d d+ 60 1 m*/ r> 0
    else
    10 * over c@ [char] 0 - +
    then
    swap char+ swap
    repeat r> drop nip s>d d+
    ;

    OK, now we need to time both solutions.
    So let's create the timing words:

    : TLOOP1
    TICKS PAD 8 30000 0 DO 2DUP TIMESTRSCAN 2DROP LOOP
    2DROP TICKS 2SWAP D- D. ;

    : TLOOP2
    TICKS PAD 8 30000 0 DO 2DUP dtimescan 2DROP LOOP
    2DROP TICKS 2SWAP D- D. ;

    Now let's take it away:

    S" 12:34:56" PAD SWAP CMOVE ok
    TLOOP1 61 ok
    TLOOP2 10 ok

    Ooops! Not too good. It seems variables are
    pretty "expensive" thing in DX Forth. So let's
    optimize the thing a little, quite basic way:


    : TIMESTRSCAN1 ( addr count -- d )
    1 C6 ! 1 C1 !
    >R >R 0 0 R> R>
    OVER + 1-
    DO
    I C@ DUP 58 =
    IF
    DROP
    [ C6 ] LITERAL @ 60 * [ C6 ] LITERAL !
    1 [ C1 ] LITERAL !
    ELSE
    48 - [ C1 ] LITERAL @ * [ C6 ] LITERAL @ M* D+
    10 [ C1 ] LITERAL !
    THEN
    -1 +LOOP
    ;

    : TLOOP3
    TICKS PAD 8 30000 0 DO 2DUP TIMESTRSCAN1 2DROP LOOP
    2DROP TICKS 2SWAP D- D. ;

    How does it do now?

    S" 12:34:56" PAD SWAP CMOVE ok
    TLOOP3 16 ok

    So according to the rule: "Once twp implementation
    techniques provide performance within (say)
    a factor of 1.5 or 2 of each other, I stop worrying.
    Short words are better than long ones." - I still
    dare to express the opinion my solution is better.

    Besides: only recently some "Master of Forth" explained:
    "you have to know that good software comes with MANY
    qualities, speed being only one of them. If I have to
    choose between correct and fast, there has to be a very
    convincing argument for the speed requirement.
    They didn't tell you that in your college? Oh dear. I'm
    so sorry for you! You must have had a miserable life."

    So taking the above into consideration, the
    slightly slower pace of my (optimized) solution
    doesn't really matter. No further optimization
    really necessary, in fact.

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Wed Jun 11 17:27:23 2025
    From Newsgroup: comp.lang.forth

    On 11-06-2025 15:54, LIT wrote:

    You show again how little you understand of Forth. The performance of my routine is heavily influenced by the price of M*/:

    So, let's run 'em on 4tH:

    Your routine (100K runs):
    real 0m5,652s
    user 0m5,652s
    sys 0m0,000s

    My routine (100K runs):
    real 0m8,448s
    user 0m8,444s
    sys 0m0,005s

    Yeah, that's worse. Now let's replace this very expensive M*/ by D*:
    real 0m1,935s
    user 0m1,935s
    sys 0m0,000s

    Oops - now it's a lot faster! Now, why did I use M*/? Because it was a
    Q&D conversion from the single word version - and I needed something to multiply a double by a single. Since D* isn't standard.. Catch my drift?

    Because - the reason I wrote it in the first place was to show that
    *EVEN* if you did a double version, you still don't need a variable.

    Remember what I said?
    On a 32-bit platform you don't need a double word version?
    Let alone on a 64-bit platform?

    Now, how does this single word version perform?
    real 0m0,034s
    user 0m0,030s
    sys 0m0,004s

    I can do a *HUNDRED* of your benches with my single word version!
    .. and still you wouldn't beat me ..
    <mike drop>

    Hans Bezemer


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From zbigniew2011@zbigniew2011@gmail.com (LIT) to comp.lang.forth on Wed Jun 11 15:42:30 2025
    From Newsgroup: comp.lang.forth

    I can do a *HUNDRED* of your benches with my single word version!
    ... and still you wouldn't beat me ..
    <mike drop>

    Hans Bezemer

    "you have to know that good software comes with MANY
    qualities, speed being only one of them. If I have to
    choose between correct and fast, there has to be a very
    convincing argument for the speed requirement.
    They didn't tell you that in your college? Oh dear. I'm
    so sorry for you! You must have had a miserable life."

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From zbigniew2011@zbigniew2011@gmail.com (LIT) to comp.lang.forth on Wed Jun 11 15:57:30 2025
    From Newsgroup: comp.lang.forth

    OK, let's move that simple optimization
    process a little bit further; what if we
    do the same even to these two variable calls
    at the very beginning, that are outside the loop?

    VARIABLE C6 ok
    VARIABLE C1 ok
    : TIMESTRSCAN2
    1 [ C6 ] LITERAL ! 1 [ C1 ] LITERAL !
    >R >R 0 0 R> R>
    OVER + 1-
    DO
    I C@ DUP 58 =
    IF
    DROP
    [ C6 ] LITERAL @ 60 * [ C6 ] LITERAL !
    1 [ C1 ] LITERAL !
    ELSE
    48 - [ C1 ] LITERAL @ * [ C6 ] LITERAL @ M* D+
    10 [ C1 ] LITERAL !
    THEN
    -1 +LOOP
    ; ok

    : TLOOP4 TICKS PAD 8 30000 0 DO 2DUP TIMESTRSCAN2 2DROP LOOP
    2DROP TICKS 2SWAP D- D. ; ok
    S" 12:34:56" PAD SWAP CMOVE ok
    TLOOP4 9 ok

    That's enough for today. :)

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From minforth@minforth@gmx.net (minforth) to comp.lang.forth on Wed Jun 11 16:29:44 2025
    From Newsgroup: comp.lang.forth

    From the other optimizers team :o)
    Extra simple and easy to maintain
    (for well-formed hh:mm:ss):

    # : hms ':' parse evaluate ':' parse evaluate bl parse evaluate ; ok
    # hms 12:59:21 ok
    12 59 21 #

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Wed Jun 11 18:55:44 2025
    From Newsgroup: comp.lang.forth

    On 11-06-2025 18:29, minforth wrote:
    From the other optimizers team :o)
    Extra simple and easy to maintain
    (for well-formed hh:mm:ss):

    # : hms ':' parse evaluate ':' parse evaluate bl parse evaluate ;  ok
    # hms 12:59:21  ok
    12 59 21 #

    --

    Can't replicate it in 4tH, but I'll take it! :)

    Hans Bezemer
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Wed Jun 11 19:12:29 2025
    From Newsgroup: comp.lang.forth

    On 11-06-2025 17:57, LIT wrote:
    OK, let's move that simple optimization
    process a little bit further; what if we
    do the same even to these two variable calls
    at the very beginning, that are outside the loop?

    VARIABLE C6  ok
    VARIABLE C1  ok
    : TIMESTRSCAN2
     1 [ C6 ] LITERAL !   1 [ C1 ] LITERAL !
    t;R >R 0 0 R> R>
     OVER + 1-
     DO
       I C@ DUP 58 =
       IF
         DROP
         [ C6 ] LITERAL @ 60 * [ C6 ] LITERAL !
         1 [ C1 ] LITERAL !
       ELSE
         48 - [ C1 ] LITERAL @ * [ C6 ] LITERAL @ M* D+
         10 [ C1 ] LITERAL !
       THEN
     -1 +LOOP
    ;  ok

    : TLOOP4 TICKS PAD 8 30000 0 DO 2DUP TIMESTRSCAN2 2DROP LOOP
    2DROP TICKS 2SWAP D- D. ;  ok
    S" 12:34:56" PAD SWAP CMOVE  ok
    TLOOP4 9  ok

    That's enough for today. :)

    4tH address translation is pretty efficient already, so it doesn't give
    you too much - if anything:

    real 0m5,677s
    user 0m5,677s
    sys 0m0,001s

    You know, the variable accesses are automatically optimized (which - I
    admit - worried me a bit :)

    485| branch 523 TIMESTRSCAN
    486| literal 1
    487| to 0 C6
    488| literal 1
    489| to 1 C1
    490| >r 0
    491| >r 0
    492| literal 0
    493| literal 0
    494| r> 0
    495| +literal -1
    496| r> 0
    497| over 0
    498| + 0
    499| do 522
    500| r@ 0
    501| c@ 0
    502| dup 0
    503| literal 58
    504| = 0
    505| 0branch 512
    506| drop 0
    507| value 0 C6
    508| *literal 60
    509| to 0 C6
    510| literal 1
    511| to 1 C1
    512| branch 520
    513| +literal -48
    514| value 1 C1
    515| * 0
    516| value 0 C6
    517| call 312 M*
    518| call 74 D+
    519| literal 10
    520| to 1 C1
    521| literal -1
    522| +loop 499
    523| exit 0

    You insistence on raw addresses makes it slightly *LESS* efficient in 4tH:

    524| branch 570 TIMESTRSCAN2
    525| literal 1
    526| literal 10
    527| ! 0
    528| literal 1
    529| literal 11
    530| ! 0
    ...

    Hans Bezemer


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Thu Jun 12 04:00:56 2025
    From Newsgroup: comp.lang.forth

    On 11/06/2025 11:54 pm, LIT wrote:
    Let's find out the difference between my
    and "Mark Twain" 's approach to the solution
    of "parsing 'time string' task". For simplicity
    I'll do everything using DX Forth.

    My solution is:

    VARIABLE C6
    VARIABLE C1

    : TIMESTRSCAN ( addr count -- d )
     1 C6 !  1 C1 !
    ;R >R 0 0 R> R>
     OVER + 1-
     DO
       I C@ DUP 58 =
       IF
         DROP
         C6 @ 60 * C6 !
         1 C1 !
       ELSE
         48 - C1 @ * C6 @ M* D+
         10 C1 !
       THEN
     -1 +LOOP
    ;

    Same using stack ops:

    : TIMESTRSCAN2 ( addr count -- d )
    >R >R 0 0 1 1 ( C6 C1) R> R>
    OVER + 1- DO
    I C@ DUP 58 = IF DROP
    DROP 60 * ( C6) 1 ( C1)
    ELSE
    48 - * OVER ( C6) >R UM* D+ R> 10 ( C1)
    THEN
    -1 +LOOP 2DROP ;

    12% faster and 20% smaller by my measurement.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Wed Jun 11 20:48:05 2025
    From Newsgroup: comp.lang.forth

    On 11-06-2025 20:00, dxf wrote:
    On 11/06/2025 11:54 pm, LIT wrote:
    Let's find out the difference between my
    and "Mark Twain" 's approach to the solution
    of "parsing 'time string' task". For simplicity
    I'll do everything using DX Forth.

    My solution is:

    VARIABLE C6
    VARIABLE C1

    : TIMESTRSCAN ( addr count -- d )
     1 C6 !  1 C1 !
    t;R >R 0 0 R> R>
     OVER + 1-
     DO
       I C@ DUP 58 =
       IF
         DROP
         C6 @ 60 * C6 !
         1 C1 !
       ELSE
         48 - C1 @ * C6 @ M* D+
         10 C1 !
       THEN
     -1 +LOOP
    ;

    Same using stack ops:

    : TIMESTRSCAN2 ( addr count -- d )
    >R >R 0 0 1 1 ( C6 C1) R> R>
    OVER + 1- DO
    I C@ DUP 58 = IF DROP
    DROP 60 * ( C6) 1 ( C1)
    ELSE
    48 - * OVER ( C6) >R UM* D+ R> 10 ( C1)
    THEN
    -1 +LOOP 2DROP ;

    12% faster and 20% smaller by my measurement.

    It's slightly faster than the original on 4tH:
    real 0m5,527s
    user 0m5,522s
    sys 0m0,004s

    Opcodes: his: 39, yours: 37

    Hans Bezemer



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Thu Jun 12 12:32:05 2025
    From Newsgroup: comp.lang.forth

    On 12/06/2025 4:48 am, Hans Bezemer wrote:
    On 11-06-2025 20:00, dxf wrote:
    On 11/06/2025 11:54 pm, LIT wrote:
    Let's find out the difference between my
    and "Mark Twain" 's approach to the solution
    of "parsing 'time string' task". For simplicity
    I'll do everything using DX Forth.

    My solution is:

    VARIABLE C6
    VARIABLE C1

    : TIMESTRSCAN ( addr count -- d )
      1 C6 !  1 C1 !
      >R >R 0 0 R> R>
      OVER + 1-
      DO
        I C@ DUP 58 =
        IF
          DROP
          C6 @ 60 * C6 !
          1 C1 !
        ELSE
          48 - C1 @ * C6 @ M* D+
          10 C1 !
        THEN
      -1 +LOOP
    ;

    Same using stack ops:

    : TIMESTRSCAN2 ( addr count -- d )
       >R >R  0 0  1 1 ( C6 C1)  R> R>
       OVER + 1- DO
         I C@ DUP 58 = IF  DROP
           DROP  60 * ( C6)  1 ( C1)
         ELSE
           48 -  *  OVER ( C6) >R  UM*  D+  R>  10 ( C1)
         THEN
       -1 +LOOP 2DROP ;

    12% faster and 20% smaller by my measurement.

    It's slightly faster than the original on 4tH:
    real    0m5,527s
    user    0m5,522s
    sys    0m0,004s

    Opcodes: his: 39, yours: 37

    To my mind the variables were, if anything, a distraction.
    A simple stack comment serving as a reminder achieved the
    same thing.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Thu Jun 12 14:34:57 2025
    From Newsgroup: comp.lang.forth

    On 12/06/2025 2:55 am, Hans Bezemer wrote:
    On 11-06-2025 18:29, minforth wrote:
     From the other optimizers team :o)
    Extra simple and easy to maintain
    (for well-formed hh:mm:ss):

    # : hms ':' parse evaluate ':' parse evaluate bl parse evaluate ;  ok
    # hms 12:59:21  ok
    12 59 21 #

    -- 

    Can't replicate it in 4tH, but I'll take it! :)

    I replicated it.

    App before optimization: 9 Kb

    App after optimization: 28 Kb

    It took a while to work out how to pass the string to the interpreter
    but eventually got there ...

    : >HMS ( a u -- sec min hr )
    blk @ >in @ 2>r source 2>r 'source 2! 0 >in !
    [char] : parse evaluate [char] : parse evaluate
    bl parse evaluate
    2r> 'source 2! 2r> >in ! blk !
    swap rot ;

    'Use the Forth Interpreter, Luke.' Yeah, right :)

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From sean@sean@conman.org to comp.lang.forth on Thu Jun 12 05:07:54 2025
    From Newsgroup: comp.lang.forth

    It was thus said that the Great Hans Bezemer <the.beez.speaks@gmail.com> once stated:

    It's slightly faster than the original on 4tH:
    real 0m5,527s
    user 0m5,522s
    sys 0m0,004s

    Opcodes: his: 39, yours: 37

    I think I am in a unique position to get an exact cycle count of the
    various attempts due to cycle emulation of the CPU my ANS Forth implemention targets (the 6809, an 8-bit CPU). I ran the three versions LIT presented,
    plus the version he said was Mr. Fifo's. I also ran dxf's version. Each version included the following word:

    : FOO S" 12:34:56" TIMESTRSCAN D. ;

    and I recorded the number CPU cycles the following line took to run:

    FOO BYE

    The results:

    dxf cycles=75453
    fifo cycles=85658
    LIT#1 cycles=78642
    LIT#2 cycles=78714
    LIT#3 cycles=78720

    I also ran miniforth's version, and timed this the line:

    HMS 12:34:56 .S BYE

    It was not fast:

    minforth cycles=834951

    Make of this what you will.

    -spc
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From mhx@mhx@iae.nl (mhx) to comp.lang.forth on Thu Jun 12 08:12:50 2025
    From Newsgroup: comp.lang.forth

    There may be several issues on a real computer which make
    these considerations immaterial:

    ANEW -timescan

    : dtimescan ( addr count -- d )
    over swap + >r >r
    0. r> 0
    begin
    over r@ <
    while
    over c@ ':'
    = if swap >r s>d d+ #60 1 m*/ r> 0
    else #10 * over c@ [char] 0 - +
    endif
    swap 1+ swap
    repeat
    -r nip s>d d+ ;

    : timestrscan ( addr count -- d )
    1 1 LOCALS| c6 c1 |
    0. 2SWAP
    OVER + 1- ?DO
    I C@ DUP ':' =
    IF DROP
    c6 #60 * TO c6
    1 TO C1
    ELSE '0' - C1 * C6 M* D+
    #10 TO C1
    ENDIF
    -1 +LOOP ;

    : TEST ( -- )
    CR ." \ dtimescan : " S" 12:34:56" TICKS-RESET dtimescan
    TICKS? UD. ." clock ticks elapsed, " UD.
    CR ." \ timestrscan : " S" 12:34:56" TICKS-RESET timestrscan
    TICKS? UD. ." clock ticks elapsed, " UD. ;

    TEST

    FORTH> in
    \ dtimescan : 1258 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296 ok
    FORTH> TEST TEST TEST
    \ dtimescan : 1258 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 420 clock ticks elapsed, 45296
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 420 clock ticks elapsed, 45296
    FORTH>

    A 'clock tick' is approximately 0.2ns.

    -marcel
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From minforth@minforth@gmx.net (minforth) to comp.lang.forth on Thu Jun 12 08:13:01 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 5:07:54 +0000, sean@conman.org wrote:
    I also ran miniforth's version, and timed this the line:

    HMS 12:34:56 .S BYE

    It was not fast:

    minforth cycles=834951

    Make of this what you will.

    But software development time about 1 minute including
    typing. :o) And no bugs in the first trial run.

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Thu Jun 12 02:59:56 2025
    From Newsgroup: comp.lang.forth

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Thu Jun 12 12:04:50 2025
    From Newsgroup: comp.lang.forth

    On 11-06-2025 18:29, minforth wrote:
    From the other optimizers team :o)
    Extra simple and easy to maintain
    (for well-formed hh:mm:ss):

    # : hms ':' parse evaluate ':' parse evaluate bl parse evaluate ;  ok
    # hms 12:59:21  ok
    12 59 21 #

    --

    Ok, two ways I tried to replicate it - first the "clean" way, by
    extracting the substrings:

    : till
    dup >r swap >r
    begin dup r@ < while dup c@ [char] : <> while char+ repeat
    r> r> -rot over char+
    ;

    : figure over over = if drop >zero ;then over - number ;
    : parse>secs figure >r figure >r figure r> r> swap 60 * + swap 3600 * + ;
    : timeparse1 bounds till till till drop drop parse>secs ;

    This is the dirty way - copy it to TIB and then parse it.

    : s>figure dup 0> if number ;then drop >zero ;
    : str>secs s>figure >r s>figure >r s>figure r> r> swap 60 * + swap 3600
    * + ;
    : timeparse2 tib place [char] : parse [char] : parse 0 parse str>secs ;

    My original "single" word is still the fastest "clean" one, 100K in 0.034s

    The "dirty" one is TWICE as fast, 0.017s

    The "clean" imitation is (despite everything) third by 0.042s

    Hans Bezemer
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From zbigniew2011@zbigniew2011@gmail.com (LIT) to comp.lang.forth on Thu Jun 12 10:08:31 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 9:59:56 +0000, Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    This one's particularly elegant, one has to admit.

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From mhx@mhx@iae.nl (mhx) to comp.lang.forth on Thu Jun 12 11:04:46 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 9:59:56 +0000, Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;

    It doesn't do the same thing. It isn't faster, which points out
    the '60 *' and 'M* D+' are not the limiting factors.

    : TEST ( -- )
    CR ." \ dtimescan : " S" 12:34:56" TICKS-RESET dtimescan
    TICKS? UD. ." clock ticks elapsed, " UD.
    CR ." \ timestrscan : " S" 12:34:56" TICKS-RESET timestrscan
    TICKS? UD. ." clock ticks elapsed, " UD.
    CR ." \ HMS : " S" 12:34:56" TICKS-RESET hms
    TICKS? UD. ." clock ticks elapsed, " ROT . SWAP . . ;

    FORTH> TEST TEST TEST TEST
    \ dtimescan : 1257 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 12 34 56
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 420 clock ticks elapsed, 45296
    \ HMS : 0 clock ticks elapsed, 12 34 56
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 12 34 56
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 420 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 12 34 56 ok

    FORTH> ' hms idis
    $01466000 : hms
    $0146600A pop rbx
    $0146600B pop rbx
    $0146600C mov $01463210 qword-offset, rbx
    $01466013 mov rbx, $01463210 qword-offset
    $0146601A movzx rbx, [rbx] byte
    $0146601D add $01463210 qword-offset, 1 b#
    $01466025 lea rbx, [rbx #-48 +] qword
    $01466029 lea rbx, [rbx rbx*4] qword
    $0146602D mov rdi, $01463210 qword-offset
    $01466034 movzx rdi, [rdi] byte
    $01466038 add $01463210 qword-offset, 1 b#
    $01466040 add $01463210 qword-offset, 1 b#
    $01466048 mov rax, $01463210 qword-offset
    $0146604F movzx rax, [rax] byte
    $01466052 add $01463210 qword-offset, 1 b#
    $0146605A lea rax, [rax #-48 +] qword
    $0146605E lea rax, [rax rax*4] qword
    $01466062 mov rdx, $01463210 qword-offset
    $01466069 movzx rdx, [rdx] byte
    $0146606C add $01463210 qword-offset, 1 b#
    $01466074 add $01463210 qword-offset, 1 b#
    $0146607C mov r9, $01463210 qword-offset
    $01466083 movzx r9, [r9 ] byte
    $01466087 add $01463210 qword-offset, 1 b#
    $0146608F lea r9, [r9 #-48 +] qword
    $01466093 lea r9, [r9 r9*4 ] qword
    $01466097 mov r10, $01463210 qword-offset
    $0146609E movzx r10, [r10] byte
    $014660A2 add $01463210 qword-offset, 1 b#
    $014660AA add $01463210 qword-offset, 1 b#
    $014660B2 lea rbx, [rdi rbx*2 #-48 +] qword
    $014660B7 push rbx
    $014660B8 lea rbx, [rdx rax*2 #-48 +] qword
    $014660BD push rbx
    $014660BE lea rbx, [r10 r9*2 #-48 +] qword
    $014660C3 push rbx
    $014660C4 ;

    -marcel
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Thu Jun 12 15:47:17 2025
    From Newsgroup: comp.lang.forth

    On 12-06-2025 11:59, Paul Rubin wrote:
    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    It's a *hair* faster (0.003s per 100K, 0.031s vs. 0.034s) than this one:

    : s>secs
    over swap chars + >r 0 tuck begin
    over r@ <
    while
    over c@ [char] : =
    if rot + 60 * swap 0 else 10 * over c@ [char] 0 - + then
    swap char+ swap
    repeat r> drop nip +
    ;

    But it doesn't survive a:

    s" 2:15:59" hms>secs s>d . . cr

    But it's an interesting approach, unrolling a loop. I tried something
    similar this morning:

    : till
    dup >r swap >r
    begin dup r@ < while dup c@ [char] : <> while char+ repeat
    r> r> -rot over char+
    ;

    : figure over over = if drop >zero ;then over - number ;
    : parse>secs figure >r figure >r figure r> r> swap 60 * + swap 3600 * + ;
    : timeparse1 bounds till till till drop drop parse>secs ;

    Hans Bezemer


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From melahi_ahmed@melahi_ahmed@yahoo.fr (ahmed) to comp.lang.forth on Thu Jun 12 14:04:38 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 9:59:56 +0000, Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    Hi,
    Thanks for all the ideas.
    What about this? is it clear, readable, ...?

    I think it is the same as yours but use directly the return stack and
    advance manually with subtracting 528 whihc is 48*10+48 where 48 is '0'.

    : hms ( u n -- h m s)
    drop >r
    r@ c@ 10 * r@ 1+ c@ + 528 -
    r@ 3 + c@ 10 * r@ 4 + c@ + 528 -
    r@ 6 + c@ 10 * r@ 7 + c@ + 528 -
    rdrop ;

    s" 12:34:56" hms .s <3> 12 34 56 ok

    Ahmed

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From minforth@minforth@gmx.net (minforth) to comp.lang.forth on Thu Jun 12 15:01:58 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 9:59:56 +0000, Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    Yes, good point. In my toolbox are two words
    STR-PARSE ( a u c -- ap up )
    STR-PARSE-NAME ( a u -- ap up )
    as twins to the standard words. They also use SKIP / SCAN
    but work on strings in memory, without needing to switch
    SOURCE. Now and then they are quite handy.

    --
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From B. Pym@Nobody447095@here-nor-there.org to comp.lang.forth on Thu Jun 12 16:13:30 2025
    From Newsgroup: comp.lang.forth

    Paul Rubin wrote:

    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    ^^^^^^^

    This serves no purpose.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Thu Jun 12 10:17:37 2025
    From Newsgroup: comp.lang.forth

    "B. Pym" <Nobody447095@here-nor-there.org> writes:
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    This serves no purpose.

    Yes I spotted that after posting. It could be worse. In C that might
    have been UB.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From peter@peter.noreply@tin.it to comp.lang.forth on Thu Jun 12 22:24:33 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 02:59:56 -0700
    Paul Rubin <no.email@nospam.invalid> wrote:
    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s
    This inspired me to write the following
    Requiring a well formed time string and a 64 bit cell size
    : hms ( a u -- h m s )
    drop @ $30303A30303A3030 -
    dup $FF0000FF0000FF00 and 8 rshift
    swap $00FF0000FF0000FF and dup 3 lshift swap 2* +
    +
    dup $ff and
    swap dup 24 rshift $ff and
    swap 48 rshift $ff and ;
    : test1 100000000 0 do "12:34:56" hms 2drop drop loop ;
    timer-reset test1 .elapsed 107 ms elapsed ok
    if I inline hmns the time goes down to 71 ms
    lxf64 now produces native code and works well!
    seea hms
    0x41FA70 488B5D00 mov rbx, qword [rbp]
    0x41FA74 488B1B mov rbx, qword [rbx]
    0x41FA77 48B830303A30303A3030 mov rax, 0x30303A30303A3030 0x41FA81 4829C3 sub rbx, rax
    0x41FA84 48B800FF0000FF0000FF mov rax, 0xFF0000FF0000FF00 0x41FA8E 4889D9 mov rcx, rbx
    0x41FA91 4821C1 and rcx, rax
    0x41FA94 48C1E908 shr rcx, 0x8
    0x41FA98 48B8FF0000FF0000FF00 mov rax, 0xFF0000FF0000FF
    0x41FAA2 4821C3 and rbx, rax
    0x41FAA5 4889D8 mov rax, rbx
    0x41FAA8 48C1E003 shl rax, 0x3
    0x41FAAC 48D1E3 shl rbx, 0x1
    0x41FAAF 4801C3 add rbx, rax
    0x41FAB2 4801CB add rbx, rcx
    0x41FAB5 4889D8 mov rax, rbx
    0x41FAB8 4825FF000000 and rax, 0xFF
    0x41FABE 4889D9 mov rcx, rbx
    0x41FAC1 48C1E918 shr rcx, 0x18
    0x41FAC5 4881E1FF000000 and rcx, 0xFF
    0x41FACC 48C1EB30 shr rbx, 0x30
    0x41FAD0 4881E3FF000000 and rbx, 0xFF
    0x41FAD7 48894DF8 mov qword [rbp-0x8], rcx
    0x41FADB 48894500 mov qword [rbp], rax
    0x41FADF 488D6DF8 lea rbp, [rbp-0x8]
    0x41FAE3 C3 ret
    116 bytes, 26 instructions
    Best Regards
    Peter Fälth
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Thu Jun 12 13:42:27 2025
    From Newsgroup: comp.lang.forth

    mhx@iae.nl (mhx) writes:
    It doesn't do the same thing. It isn't faster, which points out
    the '60 *' and 'M* D+' are not the limiting factors.

    Oh I copied the interface from another post, didn't realize it was
    supposed to convert to seconds. I didn't care about the speed since any slowness in any of these versions can be blamed on the compiler ;).
    Here is another version:

    variable p
    : advance 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + advance ; \ skips trailing colon
    : hms ( a u -- n ) drop p ! 2digit 60 * 2digit + 60 * 2digit + ;
    : test clearstack s" 12:34:56" hms ;

    test .s
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From mhx@mhx@iae.nl (mhx) to comp.lang.forth on Thu Jun 12 21:22:01 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 20:42:27 +0000, Paul Rubin wrote:

    mhx@iae.nl (mhx) writes:
    It doesn't do the same thing. It isn't faster, which points out
    the '60 *' and 'M* D+' are not the limiting factors.

    Oh I copied the interface from another post, didn't realize it was
    supposed to convert to seconds. I didn't care about the speed since any slowness in any of these versions can be blamed on the compiler ;).
    Here is another version:

    variable p
    : advance 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + advance ; \ skips trailing colon
    : hms ( a u -- n ) drop p ! 2digit 60 * 2digit + 60 * 2digit + ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    iSPICE> TEST TEST TEST TEST
    \ dtimescan : 2515 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 45296
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 420 clock ticks elapsed, 45296
    \ HMS : 420 clock ticks elapsed, 45296
    \ dtimescan : 838 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 45296
    \ dtimescan : 419 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 45296 ok

    It makes no difference. Whatever is holding it down must
    be quite severe.

    -marcel
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Thu Jun 12 15:01:07 2025
    From Newsgroup: comp.lang.forth

    mhx@iae.nl (mhx) writes:
    iSPICE> TEST TEST TEST TEST
    \ dtimescan : 2515 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 45296
    It makes no difference. Whatever is holding it down must
    be quite severe.

    Is dtimescan the one that uses double word arithmetic for 16 bit
    processors? It's doing more stuff, I would think. Weird that it
    catches up after a few tries. Cache warming?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From B. Pym@Nobody447095@here-nor-there.org to comp.lang.forth on Thu Jun 12 22:24:44 2025
    From Newsgroup: comp.lang.forth

    Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Fri Jun 13 14:00:10 2025
    From Newsgroup: comp.lang.forth

    On 13/06/2025 6:42 am, Paul Rubin wrote:
    mhx@iae.nl (mhx) writes:
    It doesn't do the same thing. It isn't faster, which points out
    the '60 *' and 'M* D+' are not the limiting factors.

    Oh I copied the interface from another post, didn't realize it was
    supposed to convert to seconds. I didn't care about the speed since any slowness in any of these versions can be blamed on the compiler ;).
    Here is another version:

    variable p
    : advance 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + advance ; \ skips trailing colon
    : hms ( a u -- n ) drop p ! 2digit 60 * 2digit + 60 * 2digit + ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    At least the variable is being used across words as opposed to within.
    One may debate whether the factoring was worth it.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Fri Jun 13 14:46:54 2025
    From Newsgroup: comp.lang.forth

    On 13/06/2025 8:24 am, B. Pym wrote:
    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    Not worth the locals IMO. OTOH I guarantee (number) will be re-used.

    : (number) ( adr len -- ud adr' len' ) 0. 2swap >number ;
    : get ( adr len -- u adr' len' ) (number) rot drop 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From mhx@mhx@iae.nl (mhx) to comp.lang.forth on Fri Jun 13 08:43:44 2025
    From Newsgroup: comp.lang.forth

    On Thu, 12 Jun 2025 22:01:07 +0000, Paul Rubin wrote:

    mhx@iae.nl (mhx) writes:
    iSPICE> TEST TEST TEST TEST
    \ dtimescan : 2515 clock ticks elapsed, 45296
    \ timestrscan : 419 clock ticks elapsed, 45296
    \ HMS : 419 clock ticks elapsed, 45296
    It makes no difference. Whatever is holding it down must
    be quite severe.

    Is dtimescan the one that uses double word arithmetic for 16 bit
    processors? It's doing more stuff, I would think. Weird that it
    catches up after a few tries. Cache warming?

    The TICKS? word apparently can't be trusted under Windows 11. This
    is better:

    #2000000 VALUE #iters
    CREATE num ," 12:34:56"
    : .SECS MS? #1000000 #iters 3 * */ ." / " . ." ns per iteration." ;

    : TEST2 ( -- )
    CR
    CR ." \ dtimescan : " TIMER-RESET num C@+
    #iters 0 ?DO 2DUP dtimescan 2DROP
    2DUP dtimescan 2DROP
    2DUP dtimescan 2DROP LOOP dtimescan UD.
    SECS
    CR ." \ timestrscan : " TIMER-RESET num C@+
    #iters 0 ?DO 2DUP timestrscan 2DROP
    2DUP timestrscan 2DROP
    2DUP timestrscan 2DROP LOOP timestrscan UD.
    SECS
    CR ." \ HMS : " TIMER-RESET num C@+
    #iters 0 ?DO 2DUP hms DROP
    2DUP hms DROP
    2DUP hms DROP LOOP hms .
    SECS
    CR ." \ HMS2 : " TIMER-RESET num C@+
    #iters 0 ?DO 2DUP hms2 3DROP
    2DUP hms2 3DROP
    2DUP hms2 3DROP LOOP hms2 ROT . SWAP . .
    SECS ;

    CLS TEST2 TEST2 TEST2 TEST2

    \ dtimescan : 45296 / 15 ns per iteration.
    \ timestrscan : 45296 / 13 ns per iteration.
    \ HMS : 45296 / 5 ns per iteration.
    \ HMS2 : 12 34 56 / 53 ns per iteration.

    \ dtimescan : 45296 / 15 ns per iteration.
    \ timestrscan : 45296 / 14 ns per iteration.
    \ HMS : 45296 / 5 ns per iteration.
    \ HMS2 : 12 34 56 / 53 ns per iteration.

    \ dtimescan : 45296 / 16 ns per iteration.
    \ timestrscan : 45296 / 14 ns per iteration.
    \ HMS : 45296 / 5 ns per iteration.
    \ HMS2 : 12 34 56 / 53 ns per iteration.

    \ dtimescan : 45296 / 15 ns per iteration.
    \ timestrscan : 45296 / 14 ns per iteration.
    \ HMS : 45296 / 5 ns per iteration.
    \ HMS2 : 12 34 56 / 53 ns per iteration. ok

    HMS is three times faster than dtimescan and timestrscan, while
    HMS2 is 3.5 times slower (as expected).

    In practice, I'd use some variant of hms2.

    -marcel
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Fri Jun 13 14:44:18 2025
    From Newsgroup: comp.lang.forth

    On 13-06-2025 06:46, dxf wrote:
    On 13/06/2025 8:24 am, B. Pym wrote:
    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    Not worth the locals IMO. OTOH I guarantee (number) will be re-used.

    : (number) ( adr len -- ud adr' len' ) 0. 2swap >number ;
    : get ( adr len -- u adr' len' ) (number) rot drop 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;


    That's why I consider the use of global variables in library functions
    worse than locals. You're polluting the namespace.

    In 4tH I can introduce and remove them from the namespace within the
    same module. That's done by simply kicking them individually (and not
    the whole shebang after it) from the symbol table.

    But still, I don't like to introduce new symbols. What if the user had
    his own library - and didn't take those precautions? He'll be wondering
    "Why is this thing complaining - instead of compiling?"

    That's also the reason I didn't define stuff like "ERROR" - since that
    is more likely to be used by end-users. Also guilty: I haven't
    documented all "publics" from all libs - so there is nothing to fall
    back on. On the other hand - I'd like to keep my sanity. More things documented means more things to maintain.

    Hans Bezemer
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Fri Jun 13 15:00:36 2025
    From Newsgroup: comp.lang.forth

    On 12-06-2025 22:24, peter wrote:
    On Thu, 12 Jun 2025 02:59:56 -0700
    Paul Rubin <no.email@nospam.invalid> wrote:

    This version with the string in memory, no error checking, and using a
    variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    This inspired me to write the following
    Requiring a well formed time string and a 64 bit cell size

    : hms ( a u -- h m s )
    drop @ $30303A30303A3030 -
    dup $FF0000FF0000FF00 and 8 rshift
    swap $00FF0000FF0000FF and dup 3 lshift swap 2* +
    +
    dup $ff and
    swap dup 24 rshift $ff and
    swap 48 rshift $ff and ;

    : test1 100000000 0 do "12:34:56" hms 2drop drop loop ;

    timer-reset test1 .elapsed 107 ms elapsed ok

    if I inline hmns the time goes down to 71 ms

    lxf64 now produces native code and works well!

    seea hms
    0x41FA70 488B5D00 mov rbx, qword [rbp]
    0x41FA74 488B1B mov rbx, qword [rbx]
    0x41FA77 48B830303A30303A3030 mov rax, 0x30303A30303A3030 0x41FA81 4829C3 sub rbx, rax
    0x41FA84 48B800FF0000FF0000FF mov rax, 0xFF0000FF0000FF00 0x41FA8E 4889D9 mov rcx, rbx
    0x41FA91 4821C1 and rcx, rax
    0x41FA94 48C1E908 shr rcx, 0x8
    0x41FA98 48B8FF0000FF0000FF00 mov rax, 0xFF0000FF0000FF 0x41FAA2 4821C3 and rbx, rax
    0x41FAA5 4889D8 mov rax, rbx
    0x41FAA8 48C1E003 shl rax, 0x3
    0x41FAAC 48D1E3 shl rbx, 0x1
    0x41FAAF 4801C3 add rbx, rax
    0x41FAB2 4801CB add rbx, rcx
    0x41FAB5 4889D8 mov rax, rbx
    0x41FAB8 4825FF000000 and rax, 0xFF
    0x41FABE 4889D9 mov rcx, rbx
    0x41FAC1 48C1E918 shr rcx, 0x18
    0x41FAC5 4881E1FF000000 and rcx, 0xFF
    0x41FACC 48C1EB30 shr rbx, 0x30
    0x41FAD0 4881E3FF000000 and rbx, 0xFF
    0x41FAD7 48894DF8 mov qword [rbp-0x8], rcx
    0x41FADB 48894500 mov qword [rbp], rax
    0x41FADF 488D6DF8 lea rbp, [rbp-0x8]
    0x41FAE3 C3 ret
    116 bytes, 26 instructions

    Best Regards
    Peter Fälth

    762| branch 795 hms
    763| drop 0
    764| call 499 n@
    765| +literal -3472339291344613424
    766| dup 0
    767| literal -72056498821202176
    768| and 0
    769| literal -8
    770| shift 0
    771| swap 0
    772| literal 71776123339407615
    773| and 0
    774| dup 0
    775| literal 3
    776| shift 0
    777| swap 0
    778| *literal 2
    779| + 0
    780| + 0
    781| dup 0
    782| literal 255
    783| and 0
    784| swap 0
    785| dup 0
    786| literal -24
    787| shift 0
    788| literal 255
    789| and 0
    790| swap 0
    791| literal -48
    792| shift 0
    793| literal 255
    794| and 0
    795| exit 0

    It compiles, it works, _I_ don't see any performance improvement.
    real 0m0,031s
    user 0m0,031s
    sys 0m0,000s

    Hans Bezemer


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Fri Jun 13 15:16:35 2025
    From Newsgroup: comp.lang.forth

    In article <102fk3b$2ut8q$1@dont-email.me>,
    B. Pym <Nobody447095@here-nor-there.org> wrote:
    Paul Rubin wrote:

    This version with the string in memory, no error checking, and using a
    variable, seems simplest to me.

    variable p
    : advance ( -- ) 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit 10 * digit + ;
    : hms ( a u -- h m s ) drop p !
    2digit advance 2digit advance 2digit advance ;
    : test clearstack s" 12:34:56" hms ;

    test .s

    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;


    Once more the superiority of the $@ $! $/ $\ wordset is shown,
    also shunning the clunky [CHAR]/CHAR stuff.
    Low level abstractions wins the day.

    : SDSWAP ROT ROT ;

    : hms &: $/ EVALUATE SDSWAP &: $/ EVALUATE SDSWAP EVALUATE ;

    "12:34:56" hms
    .S
    12 34 56 OK

    Most errors meet with an ERROR 10, malformed number.
    Not that I know what `hms is supposed to do,
    especially with regards to response to errors.


    test .s

    --
    Temu exploits Christians: (Disclaimer, only 10 apostles)
    Last Supper Acrylic Suncatcher - 15Cm Round Stained Glass- Style Wall
    Art For Home, Office And Garden Decor - Perfect For Windows, Bars,
    And Gifts For Friends Family And Colleagues.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Fri Jun 13 10:44:42 2025
    From Newsgroup: comp.lang.forth

    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    That's why I consider the use of global variables in library functions
    worse than locals. You're polluting the namespace.

    Obviously there are ways around that with wordlists, but it would be
    nice if doing that was more convenient. You could have program sections
    with their own encapsulated variables.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Fri Jun 13 10:47:24 2025
    From Newsgroup: comp.lang.forth

    mhx@iae.nl (mhx) writes:
    HMS is three times faster than dtimescan and timestrscan, while
    HMS2 is 3.5 times slower (as expected).

    Is HMS the one that I posted, and HMS2 the version that's almost the
    same? Why the speed difference: just because of the extra memory
    traffic of leaving extra things on the stack?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From mhx@mhx@iae.nl (mhx) to comp.lang.forth on Fri Jun 13 20:22:32 2025
    From Newsgroup: comp.lang.forth

    On Fri, 13 Jun 2025 17:47:24 +0000, Paul Rubin wrote:

    mhx@iae.nl (mhx) writes:
    HMS is three times faster than dtimescan and timestrscan, while
    HMS2 is 3.5 times slower (as expected).

    Is HMS the one that I posted, and HMS2 the version that's almost the
    same? Why the speed difference: just because of the extra memory
    traffic of leaving extra things on the stack?

    HSM see below.

    HSM2 is from dxf and uses >NUMBER . That is the kitchen sink compared
    to '60 * ...' so of course it is much slower with the current
    optimization level of iForth.

    -marcel

    --
    ----------------------------------------------------------------------
    ANEW -timescan

    : dtimescan ( addr count -- d )
    over swap + >r >r
    0. r> 0
    begin
    over r@ <
    while
    over c@ ':'
    = if swap >r s>d d+ #60 1 m*/ r> 0
    else #10 * over c@ '0' - +
    endif
    swap 1+ swap
    repeat
    -r nip s>d d+ ;

    : timestrscan ( addr count -- d )
    1 1 LOCALS| c6 c1 |
    0. 2SWAP
    OVER + 1- ?DO
    I C@ DUP ':' =
    IF DROP
    c6 #60 * TO c6
    1 TO C1
    ELSE '0' - C1 * C6 M* D+
    #10 TO C1
    ENDIF
    -1 +LOOP ;

    variable p
    : advance 1 p +! ;
    : digit ( -- n ) p @ c@ '0' - advance ;
    : 2digit ( -- n ) digit #10 * digit + advance ; \ skips trailing colon
    : hms ( a u -- n ) drop p ! 2digit #60 * 2digit + #60 * 2digit + ;

    : (number) ( adr len -- ud adr' len' ) 0. 2swap >number ;
    : get ( adr len -- u adr' len' ) (number) rot drop 1 /string ;
    : hms2 ( adr len -- h m s) get get get 2drop ;

    : TEST ( -- )
    CR ." \ dtimescan : " S" 12:34:56" TICKS-RESET dtimescan
    TICKS? UD. ." clock ticks elapsed, " UD.
    CR ." \ timestrscan : " S" 12:34:56" TICKS-RESET timestrscan
    TICKS? UD. ." clock ticks elapsed, " UD.
    CR ." \ HMS : " S" 12:34:56" TICKS-RESET hms
    TICKS? UD. ." clock ticks elapsed, " .
    CR ." \ HMS2 : " S" 12:34:56" TICKS-RESET hms2
    TICKS? UD. ." clock ticks elapsed, " ROT . SWAP . . ;

    TEST

    #2000000 VALUE #iters
    CREATE num ," 12:34:56"
    : .SECS MS? #1000000 #iters 3 * */ ." / " . ." ns per iteration." ;

    : TEST2 ( -- )
    CR
    CR ." \ dtimescan : " TIMER-RESET num C@+ #iters 0 ?DO 2DUP
    dtimescan 2DROP 2DUP dtimescan 2DROP 2DUP dtimescan 2DROP
    LOOP
    dtimescan UD. .SECS
    CR ." \ timestrscan : " TIMER-RESET num C@+ #iters 0 ?DO 2DUP
    timestrscan 2DROP 2DUP timestrscan 2DROP 2DUP timestrscan 2DROP
    LOOP
    timestrscan UD. .SECS
    CR ." \ HMS : " TIMER-RESET num C@+ #iters 0 ?DO 2DUP
    hms DROP 2DUP hms DROP 2DUP hms DROP
    LOOP
    hms . .SECS
    CR ." \ HMS2 : " TIMER-RESET num C@+ #iters 0 ?DO 2DUP
    hms2 3DROP 2DUP hms2 3DROP 2DUP hms2 3DROP
    LOOP
    hms2 ROT . SWAP . . .SECS ;

    TEST2
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Sat Jun 14 12:45:15 2025
    From Newsgroup: comp.lang.forth

    On 14/06/2025 3:44 am, Paul Rubin wrote:
    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    That's why I consider the use of global variables in library functions
    worse than locals. You're polluting the namespace.

    Obviously there are ways around that with wordlists, but it would be
    nice if doing that was more convenient. You could have program sections
    with their own encapsulated variables.

    Good point. As wordlists are expensive and complicated I leave them
    for situations equally demanding e.g. assembler. For libs (though not applications) I hide support words or data no longer needed with BEHEAD.
    To avoid their names potentially clashing with something during compile,
    I precede them with -? which disables redefinition warning for that
    item only.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Sat Jun 14 12:58:16 2025
    From Newsgroup: comp.lang.forth

    On 13-06-2025 19:44, Paul Rubin wrote:
    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    That's why I consider the use of global variables in library functions
    worse than locals. You're polluting the namespace.

    Obviously there are ways around that with wordlists, but it would be
    nice if doing that was more convenient. You could have program sections
    with their own encapsulated variables.

    Yeah.. that's why I don't got wordsets. And coming from C, I never ever
    really missed them. And as I said - I can clean up the symbol table
    anyway. And since I come from a place where loose variables are
    considered a PITA anyways, their usage is (let's say) limited.

    But I must confess that relinking the wordlists in Forth is vastly more elegant than C++ mangling names.

    Hans Bezemer
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Sat Jun 14 13:03:08 2025
    From Newsgroup: comp.lang.forth

    On 13-06-2025 06:46, dxf wrote:
    On 13/06/2025 8:24 am, B. Pym wrote:
    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    Not worth the locals IMO. OTOH I guarantee (number) will be re-used.

    : (number) ( adr len -- ud adr' len' ) 0. 2swap >number ;
    : get ( adr len -- u adr' len' ) (number) rot drop 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    Frankly, this is the first time I see how >NUMBER can be used as a
    dedicated parsing tool.

    Fun part, though -- in 4tH, single numbers are preferred (for reasons
    listed in the manual) and hence: double numbers are expensive.

    I do have both a single number version of >NUMBER as well as a double
    number version.

    The double number version of HMS (100K):
    real 0m5,679s
    user 0m5,679s
    sys 0m0,000s

    The single number version of HMS (100K):
    real 0m0,075s
    user 0m0,075s
    sys 0m0,000s

    Hans Bezemer


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From dxf@dxforth@gmail.com to comp.lang.forth on Sun Jun 15 12:31:27 2025
    From Newsgroup: comp.lang.forth

    On 14/06/2025 9:03 pm, Hans Bezemer wrote:
    On 13-06-2025 06:46, dxf wrote:
    On 13/06/2025 8:24 am, B. Pym wrote:
    : keep { lo hi adr len } lo adr len ;
    : get { adr len } 0. adr len >number keep 1 /string ;
    : hms ( adr len -- h m s) get get get 2drop ;
    : test clearstack s" 12:34:56" hms ;

    Not worth the locals IMO.  OTOH I guarantee (number) will be re-used.

    : (number) ( adr len -- ud adr' len' )  0. 2swap >number ;
    : get ( adr len -- u adr' len' )  (number) rot drop 1 /string ;
    : hms ( adr len -- h m s)  get get get 2drop ;
    : test  clearstack s" 12:34:56" hms ;

    Frankly, this is the first time I see how >NUMBER can be used as a dedicated parsing tool.

    Fun part, though -- in 4tH, single numbers are preferred (for reasons listed in the manual) and hence: double numbers are expensive.

    I do have both a single number version of >NUMBER as well as a double number version.
    ...

    It was with parsing in mind that I have these in the kernel:

    /NUMBER ( c-addr u -- c-addr2 u2 d|ud )
    /FLOAT ( c-addr u -- c-addr2 u2 r )

    From them I define NUMBER? >FLOAT etc.

    --- Synchronet 3.21a-Linux NewsLink 1.2