2

How to implement copy-on-write technique for stack management in postfix calculations of big numbers?

I want to optimize my system regarding operations like:

duplicate top of stack
swap the two top elements
copy the second element on stack to the top
rotate the stack making the third element on top
apply n-ary operations on the stack 

A typical operation take a number of parameters from top of stack and leave a number of results instead.

My stack is implemented as an array where data grows from low-mem to hi-mem and pointers from hi-mem to low-mem.

Lehs
  • 812
  • 6
  • 18
  • I think you'll need to specify what you want to do more, I don't quite understand what you want to achieve? How would the COW stack work? – howerj Feb 23 '21 at 15:04
  • 2
    @howerj - The idea is to minimize the number of times that blocks is moved. If one is swaping data on the stack just to read and then swap it back, it would have been faster just to swap the referenses to the blocks. I know there are such cow stacks but I don't understand how it works. – Lehs Feb 23 '21 at 19:01
  • I still don't get where the copy-on-write bit comes into play, if you reference your bigints via a handle you can use the standard words for "swap" surely? One way to check if a bigint is equal by only using a reference to it would be to do something like string interning but for the bitints (which would work for say, the most common N bigints). In which case it is not the stack which is doing the COW work, but the bigint operators? – howerj Feb 23 '21 at 19:23

1 Answers1

2

As I can see, the problem is not in the copy-on-write technique per se, but in memory management and garbage collecting.

In Forth we cannot have operator overloading and should have an explicitly separate set of operations for each class of numbers. Then we have two options:

    1. A set of operations over bigint objects (their handlers), with manual memory management, similar to operations over files, dynamic memory buffers, or other objects.
    1. A full set of operations including the separate stack, like operations over floating-point numbers, with automatic memory management.

Note that option (2) contains (1) under the hood.

Reference counting

One approach to implement automatic memory management in option (2) is to use a dedicated stack (or maybe two stacks) and reference counting. The stack contains references to objects (buffers). The operations that alter data (like b::1+) should make copy of the buffer and replace the reference by a new one if the counter is greater than 1 (otherwise data can be altered in the place).

Placing an item (a reference) to the stack should increase its counter, removing from the stack should decrease its counter. When the counter become 0, the buffer should be freed.

Such operations like b::dup, b::over increase the counter. b::swap doesn't change any counter. b::drop decreases the counter (and frees the buffer if the counter is 0).

Moving an item from the stack into a bigint variable should not change the counter in the result. But the counter for the previous value of the variable (if any) should be decreased.

If named variables and constants are not enough (e.g., to have user-defined arrays), you may need to introduce into the API the pointers (a kind of anonymous variables) or handlers to bigint objects.

Immutable objects

Another approach to implement option (2) is to have immutable objects and garbage collection loop. In this approach we don't need to count references, but need to maintain the list of external pointers (including variables).

A simple example of such garbage collection can be found in the implementation of s-expressions by Peter Sovietov: Функциональное программирование на языке Форт ("Functional programming in Forth language" in Russian, but it can be easy translated using Google Translate).

ruvim
  • 7,151
  • 2
  • 27
  • 36