-- Copyright: Peter Welch, Adam Sampson, Mario Schweigler (C) 1984-2006
-- Institution: Computing Laboratory, University of Kent, Canterbury, UK
-- Description: Testcase for pony: communications time measurement

-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-- MA 02110-1301, USA.

#INCLUDE "course.module"
#INCLUDE "pony.module"
#INCLUDE "convert.module"

--{{{  PROC seq.delta (CHAN INT in?, out.0!, out.1!)
PROC seq.delta (CHAN INT in?, out.0!, out.1!)
  WHILE TRUE
    INT n:
    SEQ
      in ? n
      out.0 ! n
      out.1 ! n
:
--}}}

--{{{  PROC consume (VAL INT n.loops, CHAN INT in?, CHAN BYTE out!)
PROC consume (VAL INT n.loops, CHAN INT in?, CHAN BYTE out!)
  TIMER tim:
  INT t0, t1:
  INT value:
  SEQ
    --{{{  warm-up loop
    VAL INT warm.up IS 16:
    SEQ i = 0 FOR warm.up
      in ? value
    --}}}
    WHILE TRUE
      SEQ
        tim ? t0
        --{{{  bench-mark loop
        SEQ i = 0 FOR n.loops
          in ? value
        --}}}
        tim ? t1
        --{{{  report
        VAL INT microsecs IS t1 MINUS t0:
        VAL INT64 nanosecs IS 1000 * (INT64 microsecs):
        SEQ
          out.string ("Last value received = ", 0, out!)
          out.int (value, 0, out!)
          out.string ("*c*n", 0, out!)
          out.string ("Time = ", 0, out!)
          out.int (microsecs, 0, out!)
          out.string (" microsecs*c*n", 0, out!)
          out.string ("Time per loop = ", 0, out!)
          out.int (INT (nanosecs/(INT64 n.loops)), 0, out!)
          out.string (" nanosecs*c*n", 0, out!)
          out.string ("Context switch = ", 0, out!)
          out.int (INT ((nanosecs/(INT64 n.loops))/4), 0, out!)
          out.string (" nanosecs*c*n*n", 0, out!)
        --}}}
:
--}}}

--{{{  CHAN TYPE INT.CT
CHAN TYPE INT.CT
  MOBILE RECORD
    CHAN INT c?:
:
--}}}

--{{{ PROC start.pony (VAL []BYTE name, VAL INT node.type, RESULT PONY.NETHANDLE! net.handle)
PROC start.pony (VAL []BYTE name, VAL INT node.type, RESULT PONY.NETHANDLE! net.handle)
  MOBILE []BYTE ans.name, app.name, node.name:
  INT node.id, result:
  SEQ
    ans.name := ""
    app.name := "commstime"
    node.name := name
    pony.startup.unh (PONYC.NETTYPE.TCPIP, ans.name, app.name, node.name, node.type, node.id, net.handle, result)
    ASSERT (result = PONYC.RESULT.STARTUP.OK)
:
--}}}

--{{{  PROC comms.time (CHAN BYTE keyboard?, screen!, error!)
PROC comms.time (CHAN BYTE keyboard?, screen!, error!)
  INITIAL MOBILE []BYTE node.name IS "":
  PONY.NETHANDLE! net.handle:
  INITIAL BYTE mode IS '?':
  INITIAL INT n.loops IS 1000000:
  INITIAL BOOL use.seq.delta IS FALSE:
  SEQ
    --{{{  Parse options
    CHAN GETOPT opts:
    PAR
      file.get.options ("0123n:l:s", opts!)
      INITIAL BOOL running IS TRUE:
      WHILE running
        BYTE b:
        MOBILE []BYTE s:
        opts ? CASE
          opt ; b
            CASE b
              '0', '1', '2', '3'
                mode := b
              's'
                use.seq.delta := TRUE
          opt.arg ; b ; s
            CASE b
              'n'
                node.name := s
              'l'
                BOOL error:
                SEQ
                  STRINGTOINT (error, n.loops, s)
                  ASSERT (NOT error)
          arg ; s
            mode := '?'
          bad ; b
            mode := '?'
          done
            running := FALSE
    --}}}
    CASE mode
      '?'
        out.string ("Usage: commstime -0|-1|-2|-3 [-s] [-n NODENAME] [-l NUM-LOOPS]*n", 0, error!)
      ELSE
        INT type:
        SEQ
          CASE mode
            '0'
              type := PONYC.NODETYPE.MASTERRESET
            ELSE
              type := PONYC.NODETYPE.SLAVEWAIT
          start.pony (node.name, type, net.handle)

          --{{{  announce
          SEQ
            out.string ("*c*nCommstime in occam ...*c*n*n", 0, screen!)
            out.string ("Using the SEQ-output version of the delta process*c*n", 0, screen!)
            out.string ("yields a more accurate measure of context-switch time*c*n*n", 0, screen!)
            out.string ("Using the PAR-output version carries an extra overhead*c*n", 0, screen!)
            out.string ("of one process startup/shutdown per Commstime loop*c*n*n", 0, screen!)
            out.string ("By comparing **loop** times between the SEQ and PAR versions,*c*n", 0, screen!)
            out.string ("the process startup/shutdown overhead may be deduced*c*n*n", 0, screen!)
          --}}}
          out.string ("Using sequential delta: ", 0, screen!)
          out.yes.no (use.seq.delta, 0, screen!)
          out.string ("*nNumber of loops: ", 0, screen!)
          out.int (n.loops, 0, screen!)
          out.string ("*nCommstime starting ...*c*n*n", 0, screen!)

          INT result:
          CASE mode
            '0'
              INT.CT? b:
              INT.CT! a:
              SEQ
                pony.alloc.us (net.handle, "b", PONYC.SHARETYPE.UNSHARED, b, result)
                pony.alloc.uc (net.handle, "a", PONYC.SHARETYPE.UNSHARED, a, result)
                prefix (0, b[c]?, a[c]!)
            '1'
              INT.CT? a:
              INT.CT! c, d:
              SEQ
                pony.alloc.us (net.handle, "a", PONYC.SHARETYPE.UNSHARED, a, result)
                pony.alloc.uc (net.handle, "c", PONYC.SHARETYPE.UNSHARED, c, result)
                pony.alloc.uc (net.handle, "d", PONYC.SHARETYPE.UNSHARED, d, result)
                IF
                  use.seq.delta
                    seq.delta (a[c]?, c[c]!, d[c]!)    -- the one defined above
                  TRUE
                    delta (a[c]?, c[c]!, d[c]!)        -- the one that does a parallel output
            '2'
              INT.CT? c:
              INT.CT! b:
              SEQ
                pony.alloc.us (net.handle, "c", PONYC.SHARETYPE.UNSHARED, c, result)
                pony.alloc.uc (net.handle, "b", PONYC.SHARETYPE.UNSHARED, b, result)
                succ (c[c]?, b[c]!)
            '3'
              INT.CT? d:
              SEQ
                pony.alloc.us (net.handle, "d", PONYC.SHARETYPE.UNSHARED, d, result)
                consume (n.loops, d[c]?, screen!)

          --{{{  Shut down
          pony.shutdown (net.handle)
          --}}}
:
--}}}

