I am working on code to speed up short sentences, to reduce the penalty for writing FORTRAN-like J code. So far, so good. As part of this work, I had to grapple with in-place operations, to allow

y =. y + 1

and

t =. 2 * 1 + z

to execute without allocating/freeing memory unduly [the second sentence still requires one alloc/free].

I had an epiphany: it is not a BLOCK that is eligible for in-placing, but an ARGUMENT. The same block may be inplaceable or not, depending on the context. The question then was, How to get the in-placing info to the verb? and I found the answer to be, In the 2 LSBs of jt. This allows flags to be passed without changing the arguments to anything.

This works pretty well in my testbed.  Here are the details.

Henry

// In-place operations
//
// The key point is that an in-place operation is connected to a FUNCTION CALL, not an DATA BLOCK. // For example, in (+/ % #) <: y the result of <: y has a usecount of 1 and is // not used anywhere else, so it can be passed into the fork as an inplaceable argument. // This same block is not inplaceable when it is passed into # (because it will be needed later by +/)
// but it can be inplaceable when passed into +/.
// Thus, it is up to the caller to decide whether it can tolerate having an argument overwritten;
// if so, it marks the argument as inplaceable for that call only.
//
// Generally an argument is eligible for inplacing if its usecount is 1, but there are exceptions. // We can't inplace any argument that came from the queue, because the sentence may // be reexecuted later. [This is actually two different cases. Names in the queue are replaced, // by either their value or a reference, so there is no issue of overwriting the queue; but the value // of a noun must be protected since it is attached to the name. It would be permissible to modify // a reference but there is no advantage in doing so. Unnamed noun values in the queue must be protected // because they are stored in the saved sentence, which might be in an explicit definition that will // be run again later. It would be OK to modify values coming from ". or immex, but that is not
// in a critical-performance path.]
// Individual verbs may also decide not to allow inplacing, as in the example above. // And, if a non-inplaceable argument is returned from a verb (such as ]), we need to make sure
// that that result is also marked non-inplaceable.
//
// The upshot is that results of verbs are (usually) inplaceable. When one of these has usecount 1, // we indicate its inplaceability by setting an LSB of jt: bit 0 to indicate inplaceable w, bit 1 for inplaceable a. // It is our responsibility not to set these flags unless we know that the receiving verb // can handle inplace arguments (if it can, it needs to execute F?PREFIP as its first act, to preserve // the flags in jt and cleanse jt before it is used). For primitive verbs, the verb flags
// VINPLACEOK? indicate whether the monad and dyad can handle inplace calls.
//
// We use stack[].ipt.ip to indicate that the stacked noun block is an argument that supports inplace calls.
//
// There is one more piece to the inplace system: reassigned names. When there is an // assignment to a name, the block being reassigned can be reused for the output if:
//   the usecount is 1
//   the current execution is the only thing on the stack
//   the assignment is to a local name
// We detect these cases only for local assignments, to avoid worries about aliasing // with other uses of the names in sentences in execution at higher stack levels; and only when // the verb about to be executed is the only thing on the stack (but there may have been previous // executions in the sentence before the final execution whose result is about to be assigned). // The name jt->assignsym is set to point to the symbol-table entry whenever a local assignment is detected // (note that some such assignments, coming from ". or (name)=. might go undetected), as a time-saving maneuver
// because such an assignment can complete quickly.
// When a symbol is being reassigned and the data area can be reused, the name jt->zombieval is set
// to point to the data block for the name.
//
// Note that if jt->zombieval is set, any argument that is equal to jt->zombieval will be
// ipso facto inplaceable, and moreover the flag in jt corresponding to such
// an argument WILL NOT be set because it came from evaluating a name. So, a verb supporting inplace
// operation must check for (jtinplace&1||jt->zombiesym==w) for example
// if it wants to inplace the w argument.

// Note kludge: the stack bits to indicate inplaceability could be replaced by performing
// ra();tpush();  when a word is moved to the stack; but until we
// get nonrecursive usecounts we don't do that.

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to