There was an oversight in the test code for the release version (only half of the gc() calls get carried out, i.e. those with the argument "force"), this version tries to take it into account:

   say "version:" .RexxInfo~debug~?("DEBUG", "RELEASE")
   d1=.dateTime~new
   count=10000
   -- in the release version only half of the gc() calls get executed
   effectiveCount=count/(.RexxInfo~debug~?(1,2))
   len=count~length

   t1=test_gc(count)
   t2=test_reverse(count)
   d2=.dateTime~new

   say "test_gc("count")     :" t1 "->" t1/effectiveCount "per call (effective calls:" 
effectiveCount")"
   say "test_reverse("count"):" t2 "->" t2/count "per call"
   say "total duration     :" d2 - d1


   ::routine test_gc
      use arg count
      d1=.dateTime~new
      do count/2
         call gc         -- call the garbage collector
         call gc "force" -- call the garbage collector
      end
      return .dateTime~new - d1

   ::routine test_reverse
      use arg count

      d1=.dateTime~new
      do count/2
         call reverse '' -- call reverse()
         call reverse '' -- call reverse()
      end
      return .dateTime~new - d1

Running this a couple of times yields:

   G:\test\orx\gc>test_gc_speed.rex
   version: RELEASE
   test_gc(10000)     : 00:00:00.037000 -> 00:00:00.000007 per call (effective 
calls: 5000)
   test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.037000

   G:\test\orx\gc>test_gc_speed.rex
   version: RELEASE
   test_gc(10000)     : 00:00:00.035000 -> 00:00:00.000007 per call (effective 
calls: 5000)
   test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.035000

   G:\test\orx\gc>test_gc_speed.rex
   version: RELEASE
   test_gc(10000)     : 00:00:00.036000 -> 00:00:00.000007 per call (effective 
calls: 5000)
   test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
   total duration     : 00:00:00.036000

So the speed improvement between the debug and the release version is about a factor of 14. Still, quite impressive.

---rony


On 30.08.2025 21:55, Rony G. Flatscher wrote:

For the record, running the same speed test on the same machine with the release version makes the gc() call about twenty-five (!) times faster:

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.037000 -> 00:00:00.000003 per call
    test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.037000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.034000 -> 00:00:00.000003 per call
    test_reverse(10000): 00:00:00.001000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.036000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.034000 -> 00:00:00.000003 per call
    test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.034000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.038000 -> 00:00:00.000003 per call
    test_reverse(10000): 00:00:00.001000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.039000

---rony

On 30.08.2025 15:41, Rony G. Flatscher wrote:

This is the planned implementation of gc() which

  * always runs in a debug version of ooRexx (.RexxInfo~debug=.true)
  * does *not* run in a release version of ooRexx (.RexxInfo~debug=.false) to 
cater for Rick's
    preoccupation; however, there is an overrule possibility if the argument 
"force" is supplied
    for edge cases, using it must be deliberate for a release version. This 
should at least rule
    out using it mistakingly in the release version, if used for debugging, but 
not meant for
    release.

The planned description of gc() would be:

    gc(---+-------------+---)
           |             |
           +---"Force"---+


    Returns /.true/ if the garbage collector was called, /.false/ else. This 
function should be
    used with great care as explicitly invoking the garbage collector imposes 
quite a burden on a
    running system and can severely affect applications. Therefore, garbage 
collection
    invocations should be best left to the interpreter. If invoked from a debug 
version of ooRexx
    (.RexxInfo~debug=.true), the garbage collector will always be called. If 
invoked from a
    release version of ooRexx (.RexxInfo~debug=.false), the garbage collector 
will not be called,
    unless the optional argument “force” is supplied.

Enclosed you'll find the diff.

Here a little test program that invokes the garbage collector 10,000 times and compares this to invoking 10,000 times the reverse() BIF on a debug version of ooRexx:

    d1=.dateTime~new
    count=10000
    t1=test_gc(count)
    t2=test_reverse(count)
    d2=.dateTime~new

    say "test_gc("count")     :" t1 "->" t1/count "per call"
    say "test_reverse("count"):" t2 "->" t2/count "per call"
    say "total duration     :" d2 - d1


    ::routine test_gc
       use arg count
       d1=.dateTime~new
       do count/2
          call gc         -- call the garbage collector
          call gc "force" -- call the garbage collector
       end
       return .dateTime~new - d1

    ::routine test_reverse
       use arg count

       d1=.dateTime~new
       do count/2
          call reverse '' -- call reverse()
          call reverse '' -- call reverse()
       end
       return .dateTime~new - d1

Running this on a quite old PC yields:

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.971000 -> 00:00:00.000097 per call
    test_reverse(10000): 00:00:00.000000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.971000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:01.005000 -> 00:00:00.000100 per call
    test_reverse(10000): 00:00:00.004000 -> 00:00:00.000000 per call
    total duration     : 00:00:01.009000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.987000 -> 00:00:00.000098 per call
    test_reverse(10000): 00:00:00.004000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.991000

    G:\test\orx\gc>rxenv rexx test_gc_speed.rex
    test_gc(10000)     : 00:00:00.972000 -> 00:00:00.000097 per call
    test_reverse(10000): 00:00:00.008000 -> 00:00:00.000000 per call
    total duration     : 00:00:00.980000

Any comments, feedback?

---rony


On 29.08.2025 11:59, Rony G. Flatscher wrote:

Thank you all very much for your feedback (also to those who reacted with a 
direct e-mail to me)!

My take away currently is that

  * using a keyword is not really acceptable (it really looks a little bit 
awkward)
  * restricting the invocation by a predefined number may not be possible if 
programs run 7/24
    (where this feature might be important in edge cases)
  * the hints at popular languages that allow for invoking the garbage 
collector freely, making
    it the responsibility of the programmer to use it cautiously and only, if 
it is needed (e.g.
    for unpinning non-Rexx resources that are held in Rexx objects that have 
gone out of scope)
  * one should clearly document and warn about misusing this feature pointing 
out at potential
    runtime performance implications (like in other programming languages)

---rony


On 26.08.2025 22:28, Rony G. Flatscher wrote:

There is a new library for ooRexx in development by one of my students, where objects from the other side get proxied in registries. For debugging, it is necessary to be able to kick off a garbage collection run.

However, as has been pointed out and discussed, it has been seen as dangerous for the performance of ooRexx programs if ooRexx programmers use such a feature wrongly (e.g., too many times at the wrong place, etc.).

My take would be that if programmers are made aware of that potential problem and strongly advised not to use it for regular programs, only for situations where it becomes important that external resources get freed that are held by Rexx objects that became garbage but do not get garbage collected in a timely manner.

Maybe an approach like this can be acceptable?

  * define a gc() built-in function that works by default in a debug version of 
ooRexx (i.e.,
    RexxInfo~debug yields .true) and returns .true to indicate that the BIF got 
carried out,

  * invoking some gc() on the release version of ooRexx (i.e., RexxInfo~debug 
yields .false)
    does not invoke the garbage collector and returns therefore .false.

      o for very rare, special situations (like freeing external resources as 
timely as
        possible if Rexx objects pinning them down turn to garbage) it may make 
sense to allow
        gc() to be run on a release version; in order to make this explicit one 
could think of
        allowing an argument like "EMERGENCY" (intentionally in uppercase and 
spelled out), but
        only for the release version, which would indeed invoke the garbage 
collection on a
        release version as well and then return .true to indicate it. This 
would inhibit a
        mistakenly usage of gc(). If one leaves the gc() invocation from the 
debug version, it
        would not run the garbage collector by default on a release version. 
If, however, a
        programmer needs a garbage collection in the release version, it must be 
"cumbersomely"
        invoked, such that this can only happen intentionally.

What do you think? Any comments?

---rony

_______________________________________________
Oorexx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to