HP Forums
RPL programming questions - Printable Version

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Calculators (and very old HP Computers) (/forum-3.html)
+--- Forum: General Forum (/forum-4.html)
+--- Thread: RPL programming questions (/thread-697.html)



RPL programming questions - HP67 - 02-17-2014 01:54 PM

I'm getting to know RPL on an HP 48. I've done a lot of programming on the older so-called "keystroke" programmable models from HP and even TI and it's pretty simple compared to RPL. I've been trying to read everything I can find on the net about RPL and it seems like the general recommendation is to avoid local variables. That works fine on small programs but it gets tricky on programs with a lot of calculations and intermediate variables.

I looked at some information on Forth since there is more Forth doc out there than HP RPL doc and I understand you're supposed to factor problems into meaningful "words" (RPL commands). I can see how to do that when the calculations that make up a bigger problem are meaningful by themselves and can be reused. What I don't get is what to do when you have a big set of calculations and a lot of intermediate values that don't have any value from a reuse point of view.

To actually work through a big problem the simple solution seems to be to pick a letter for each temporary and save the calculation as a letter. Do that for all the calculations in the problem and then to write a classically-correct RPL solution that doesn't use local variables, you just string the commands together and get the result. I could make a complete RPL program buy pasting all those individual commands I just wrote to represent the individual calculations into one RPL command. That does work, but it doesn't take advantage of the fact that if you know ahead of time about all the stuff on the stack you could probably rewrite the individual calculations in the problem to take advantage of stuff on the stack and the program would actually be more efficient. But with more than some number of items on the stack it starts to get really hairy. I did something today and the stack depth got to 5 or 6 and I felt like more than that becomes unmanagable quickly.

So. How bad are local variables in terms of storage and time? How many items is it reasonable to keep on the stack before it's a bad idea? And can anybody help with general guidance on how to program a big solution elegantly in RPL that contains many individual calculations and temporary results where those temporary results aren't useful enough to be saved as commands to be reused in other solutions?


RE: RPL programming questions - Raymond Del Tondo - 02-17-2014 02:11 PM

I don't know who told you to avoid local variables, but it's plain nonsense.

Local vars are an elegant way to manage parameters and more.

However many tasks can be performed efficiently by using the data stack for holding parameters and variables, but there are various situations where local vars are more practical.
And if you dive deeper into RPL, and learn how to create and use Parameterized Outer Loops, or input forms (which are a special case of a POL),
then it's very likely you will make use of local vars;-)

HTH


RE: RPL programming questions - peacecalc - 02-17-2014 03:10 PM

If you are using a lot of stack for temporary result, then you get a total unreadable program text, RPL is hard to read anyway, but if you flavor your program with the whole bunch of DUPS, PICKS or DROPS (don't forget the SWAPS) you get something which could be only understandable with an additional paper and pencil drawing each status of the stack.

And if you have time sensible programs you need SysRPL but then you lost a good piece of comfort in programming and understandig the result.

Greetings
peacecalc


RE: RPL programming questions - Joe Horn - 02-18-2014 01:06 PM

I agree with Raymond and peacecalc. Local variables are almost as fast as doing everything on the stack, at least when compared with global variables, which are very slow. If you want to squeeze every millisecond out of a loop that iterates many times, then doing everything on the stack is faster than local vars... but outside of a loop the benefit is offset by the time lost attempting to modify or debug the code later.


RE: RPL programming questions - HP67 - 02-18-2014 01:39 PM

Thank you everyone for the help. I really appreciate hearing from the experts!

Is there any info on exactly how much the penalty for using local vars vs. stack is in terms of time and memory?

And does using local variables make it any easier/harder to convert user rpl to sysrpl later?


RE: RPL programming questions - peacecalc - 02-18-2014 07:15 PM

Surprising competition,

I wrote three little programs, for testing the speed of hp 50g recalling 5000 times five different storeplaces

a) global variables program "TGLO":

Code:

%%HP: T(3)A(R)F(,);
\<< 1, 'A' STO 
    2, 'B' STO 
    3, 'C' STO 
    4, 'D' STO 
    5, 'E' STO 
    TICKS 
    1, 5000, START 
             'A' RCL DROP 
             'B' RCL DROP 
             'C' RCL DROP 
             'D' RCL DROP 
             'E' RCL DROP
              NEXT 
    TICKS SWAP - 8192 / 
    { A B C D E } PURGE
\>>

this proggie create five globales from "A" to "E" and in the loop these variables are
recalled and the values are immediately afterwards deleted. It takes on the real machine: 56 sec.

b) the second proggie: "TSTK":

Code:

%%HP: T(3)A(R)F(,);
\<< 1, 2, 3, 4, 5, 
    TICKS 
    1, 5000, START 
             5, PICK DROP 
             4, PICK DROP 
             3, PICK DROP 
             2, PICK DROP 
             1, PICK DROP
           NEXT 
    TICKS SWAP - 8192 /
\>>

this proggie create five values on stacklevel one to five and in the loop these variables are recalled by PICK and immediately afterwards deleted. It takes on the real machine: 26 sec.

Okay, two times faster.

But now the surprise:

c) working with locals "TLOC":

Code:

%%HP: T(3)A(R)F(,);
\<< 1, 2, 3, 4, 5, \-> A B C D E
  \<< TICKS 
    1, 5000, START 
             'A' RCL DROP 
             'B' RCL DROP 
             'C' RCL DROP 
             'D' RCL DROP 
             'E' RCL DROP
             NEXT 
   TICKS SWAP - 8192 /
  \>>
\>>

this proggie create five local variables from "A" to "E" and in the loop these variables are recalled and immediately afterwards deleted. It takes on the real machine: 57 sec.

So one can see in examples how to test certain commands about their time consumption. But you have each command for their own and the programming have to provide comparative environments.

Greetings
peacecalc


RE: RPL programming questions - DavidM - 02-18-2014 11:31 PM

(02-18-2014 07:15 PM)peacecalc Wrote:  Surprising competition,

I wrote three little programs, for testing the speed of hp 50g recalling 5000 times five different storeplaces

...

Greetings
peacecalc

A more typical use of both global and local variables in UserRPL uses the variable name without the single quotes, which implicitly "executes" the variable. In the case of numerical objects, this results in the object simply being recalled to the stack. As such, no explicit "RCL" step is required in your examples if the variables are referenced without the quotes.

When changing the code in this way, I think you'll find the execution times of TGLO and TLOC are more in line with expectations. Here's your TGLO when re-coded in this manner:

Code:

\<< 1, 'A' STO
    2, 'B' STO
    3, 'C' STO
    4, 'D' STO
    5, 'E' STO
    TICKS
    1, 5000, START
             A DROP
             B DROP
             C DROP
             D DROP
             E DROP
              NEXT
    TICKS SWAP - 8192 /
    { A B C D E } PURGE
\>>

My 50g gives times of 23 secs for TGLO2 and 21 secs for TLOC2.

IMHO, the real time savings for local variables comes when coding in SysRPL. SysRPL code can access locals in a special way that uses an offset table instead of named variables. This significantly reduces the time to locate the objects in memory. These locals have no names, and are referenced by a numerical index instead. To aid readability, SysRPL coders can take advantage of compiler DEFINEs to give meaningful names to these local variables as follows:

Code:

RPL

DEFINE   Rcl_A    1GETLAM
DEFINE   Rcl_B    2GETLAM
DEFINE   Rcl_C    3GETLAM
DEFINE   Rcl_D    4GETLAM
DEFINE   Rcl_E    5GETLAM

::
   ( no args required )
   CK0NOLASTWD

   ( Place reals 1-5 on stack, bind to LAMs )
   %1 %2 %3 %4 %5
   NULLLAM #5 NDUPN DOBIND

   ( Get current TICKS )
   SysTime

   ( Loop 5000 times: Recall each LAM and DROP )
   # 5000 #1+_ONE_DO (DO)
      Rcl_A DROP
      Rcl_B DROP
      Rcl_C DROP
      Rcl_D DROP
      Rcl_E DROP
   LOOP

   ( Get current TICKS, subtract starting time )
   SysTime SWAP bit-

   ( Divide by 8192, yielding seconds )
   HXS 4 0002 bit/

   ( release LAM bindings )
   ABND
;

SysRPL code usually provides a speed benefit, and the above is no exception. It completes on my 50g in 3 seconds.

Regards -
David


RE: RPL programming questions - HP67 - 02-19-2014 09:52 AM

Peacecalc, DavidM, thank you both very much for taking the time to write and test code and show your results! I really appreciate it.

Curiously, David's example of locals is faster than Peacecalc's example using the stack only. This does not seem to make sense.

David wrote: "IMHO, the real time savings for local variables comes when coding in SysRPL. SysRPL code can access locals in a special way that uses an offset table instead of named variables. This significantly reduces the time to locate the objects in memory. These locals have no names, and are referenced by a numerical index instead."

This is what I have been asking about. I suppose but am not certain that a local variable takes more space than a stack item if not at least for creating a name and some method of pointing to the value. That's the storage cost over a stack entry. And there is also the issue of how the variables are accessed by name.

Can anybody explain these two issues further? How much extra storage is required for a local variable vs. a stack entry, and how much time cost is there in locating the local variable and placing it on the stack?


RE: RPL programming questions - David Hayden - 02-19-2014 01:49 PM

(02-19-2014 09:52 AM)HP67 Wrote:  Curiously, David's example of locals is faster than Peacecalc's example using the stack only. This does not seem to make sense.
The difference is sysRPL vs userRPL. In general, sysRPL commands don't check their arguments - they leave that to the programmer. userRPL commands always check and that overhead is significant.
(02-19-2014 09:52 AM)HP67 Wrote:  This is what I have been asking about. I suppose but am not certain that a local variable takes more space than a stack item if not at least for creating a name and some method of pointing to the value. That's the storage cost over a stack entry. And there is also the issue of how the variables are accessed by name.
An entry on the stack is just a pointer to an existing object. That takes 5 nibbles (2.5 bytes). Accessing the stack with words like SWAP, OVER and ROT takes 2.5 bytes each. A sequence like 4. PICK takes 5 bytes - 2.5 for "4." and 2.5 for "PICK". Note that "4" is only 2.5 bytes because the calc has copies of small integer REALs in ROM and the compiler is smart enough to use them when it sees a reference to the values.

The time required to access the stack is very fast - just a bit of binary arithmetic.

When you store a local variable, the calculator uses two pointers: one points to the local variable name (called a LAM) and the other points to the stored object. There are also 5 bytes of house keeping data for each block of locals. For example, when you execute "-> A B C << " the calculator needs 5 bytes of housekeeping plus 2 pointers for each of the 3 local variables for a total of 5+2.5*2*3 = 20 bytes.

If you refer to a local by name, the name takes 1 byte for each character plus 3.5 bytes of overhead. e.g. "A" takes 4.5 bytes and "AB" takes 5.5. Note that if you just put a local name in your program, the effect is ALWAYS to recall the variable whereas a global name will EXECUTE the variable. If the variable is something like a REAL, the results are the same, but if it's a program stored in a global, then you'll also need to EVAL the program if you want to run it. That takes another 2.5 bytes.

To lookup a local variable, the calculator scans through all the locals looking for one whose name matches. Thus shorter names take less time to scan. In sysRPL, you have the ability to say "get me the 3rd local variable" and the calc can find it with a little arithmetic - comparable to a PICK operation on the stack.

Local variables with short names are pretty fast and they make the code MUCH easier to write, read and maintain. I'd code your program using them first. If performance becomes an issue then you can think about modifying it to use the stack.

Dave


RE: RPL programming questions - HP67 - 02-19-2014 02:15 PM

(02-19-2014 01:49 PM)David Hayden Wrote:  
(02-19-2014 09:52 AM)HP67 Wrote:  Curiously, David's example of locals is faster than Peacecalc's example using the stack only. This does not seem to make sense.
The difference is sysRPL vs userRPL.

I may have misread David's comments but I thought he posted times for his User RPL version using local variables. That's what I intended to ask about. David said "My 50g gives times of 23 secs for TGLO2 and 21 secs for TLOC2" and I was comparing that to Peacecalc's stack example where Peacecalc said "this proggie create five values on stacklevel one to five and in the loop these variables are recalled by PICK and immediately afterwards deleted." That took 26 seconds.

Your detailed explanation about local variable overhead (and additional programming tips about variable references) is what I hoped somebody would explain. Thank you very much for the info! I will read it over a few times to make sure I get it.

When you say the calculator has to scan through all the locals, I suppose you mean there is a linked list or table of locals. I was expecting a hash would have been used for this. I'm going to read up on as much internals doc as I can find. Thanks again!

(02-19-2014 01:49 PM)David Hayden Wrote:  Local variables with short names are pretty fast and they make the code MUCH easier to write, read and maintain. I'd code your program using them first. If performance becomes an issue then you can think about modifying it to use the stack.

That sounds pretty sensible although I find I don't like the nesting requirement and having to have something on the stack to create a local variable with is a little opposite of the way I would usually code something- I would tend to try to understand what variables I need and declare and use them, rather than only name them once they're on the stack.


RE: RPL programming questions - peacecalc - 02-19-2014 04:15 PM

Hello HP50g fans,

first of all I can confirm the results of DavidM and thank you for simplefy my code. It is funny, of course in my all day programming the hp 50g I use consequently A instead of 'A' RCL (maybe it is a kind of sclerosis).


Now I've tested the creating of global, local variables and putting values (which are deleted in every through going of the loop) on the stack with three little programs. All proggies have a loop from 1 to 1000 and we also have five different store places.

TGLO for globals (182 sec)

Code:

%%HP: T(3)A(R)F(,);
\<< TICKS
    1, 1000, START 
         1, 'A' STO 
                2, 'B' STO 
         3, 'C' STO 
         4, 'D' STO 
         5, 'E' STO 
         { A B C D E } PURGE
           NEXT 
   TICKS SWAP - 8192 /
\>>

TLOC for locals (3 sec), here you don't need extra code for for deleting the variables, because after leaving the sub program the variables doesn't exist any longer.

Code:

%%HP: T(3)A(R)F(,);
\<< TICKS 
    1, 1000, START 
             1, 2, 3, 4, 5, \-> A B C D E
              \<<
             \>>
           NEXT 
    TICKS SWAP - 8192 /
\>>

TSTK with stack (2 sec)

Code:

%%HP: T(3)A(R)F(,);
\<< TICKS 
    1, 1000, START 
             1, 2, 3, 4, 5, 5, DROPN
           NEXT 
    TICKS SWAP - 8192 /
\>>

You see the globals are 90 times slower as the stack or 60 times slower as the local variant. That sounds very much, but how often you need a programm which create and deletes 5000 variables. Therefore if you are not familiar with SysRPL use simply locals it is fast as a stack storage place and you need not any code for organizing the stack. The argument for a more readable code is untouched.

greetings
peacecalc


RE: RPL programming questions - David Hayden - 02-19-2014 05:15 PM

(02-19-2014 02:15 PM)HP67 Wrote:  I may have misread David's comments but I thought he posted times for his User RPL version using local variables. That's what I intended to ask about. David said "My 50g gives times of 23 secs for TGLO2 and 21 secs for TLOC2" and I was comparing that to Peacecalc's stack example where Peacecalc said "this proggie create five values on stacklevel one to five and in the loop these variables are recalled by PICK and immediately afterwards deleted." That took 26 seconds.
Hmm. I'm not sure what to make of that either.
(02-19-2014 02:15 PM)HP67 Wrote:  When you say the calculator has to scan through all the locals, I suppose you mean there is a linked list or table of locals. I was expecting a hash would have been used for this. I'm going to read up on as much internals doc as I can find. Thanks again!
Yes it's a table. See section 13 of RPLMAN.DOC for a detailed explanation of temporary variables and environments. In getting to know the RPL design, it seems to me that space was at a premium and they didn't everything they could to save it. A hash would have taken more space.
(02-19-2014 02:15 PM)HP67 Wrote:  [Starting with local variables] sounds pretty sensible although I find I don't like the nesting requirement and having to have something on the stack to create a local variable [which] is a little opposite of the way I would usually code something-
Having something on the stack is just a way to provide an initial value for the variable. If you want to create all your locals at the beginning then just push a bunch of zero's on the stack and create them all. You can store the meaningful values at the appropriate place in the code.

Dave


RE: RPL programming questions - DavidM - 02-19-2014 05:23 PM

(02-19-2014 02:15 PM)HP67 Wrote:  I may have misread David's comments but I thought he posted times for his User RPL version using local variables. That's what I intended to ask about. David said "My 50g gives times of 23 secs for TGLO2 and 21 secs for TLOC2" and I was comparing that to Peacecalc's stack example where Peacecalc said "this proggie create five values on stacklevel one to five and in the loop these variables are recalled by PICK and immediately afterwards deleted." That took 26 seconds.

You didn't misread my comments. The 23/21 sec. times were for the UserRPL programs that were re-coded without the single quotes around the variable names (and the subsequent RCL).

Keep in mind that these are very specific (and useless) programs that are attempting to time some very specific actions. Real examples would more likely have a blend of operations that would tend to minimize some of the time differences.

I was simply trying to make the examples more realistic in terms of how you would code them; instead of writing code like this:

Code:
'A' RCL 3, * 'B' RCL +

you would almost certainly want to use this:

Code:
A 3, * B +

The latter is both faster and easier to read (my opinion, of course). Even better for readability might be:

Code:
ob1Height 3, *
vOffset +

But the above would be slightly slower and take up more memory. Is it worth it? As the programmer, you have to make that decision. If you're working on something that is likely to be updated later, it may be. If the above is in a tight loop that is executed thousands of times, the speed penalty may be excessive.

Using stack operations (instead of named variables) to speed things up gains efficiency more from the standpoint of how you sequence your operations than from looking at the stack as storage area to PICK things from. Stack operations such as SWAP, DUP, ROT, DUP2, etc. are very fast. So doing something like this:

DUP2 +

will be faster than this:

A B +

But as you can see, the former requires that the variables are already on the stack at the outset. The penalty for using stack-based operations is as has been mentioned by others: readability and code maintainability suffers. These are important factors that should not be discounted when considering how you write any application (on any platform). A program written with a bias toward using stack operations may be more efficient than one using local variables, but in most cases it will also be more fragile. Adding a new computation in the middle of previously written ones can wreak havoc with your carefully planned stack.


RE: RPL programming questions - HP67 - 02-20-2014 03:27 PM

(02-19-2014 05:15 PM)David Hayden Wrote:  Yes it's a table. See section 13 of RPLMAN.DOC for a detailed explanation of temporary variables and environments. In getting to know the RPL design, it seems to me that space was at a premium and they didn't everything they could to save it. A hash would have taken more space.

Thanks. I have a long list of stuff to do and read. RPLMAN is on the list.

(02-19-2014 05:15 PM)David Hayden Wrote:  Having something on the stack is just a way to provide an initial value for the variable. If you want to create all your locals at the beginning then just push a bunch of zero's on the stack and create them all. You can store the meaningful values at the appropriate place in the code.

Local var declarations are ugly enough syntax-wise without going out of my way to accomodate them. For now I'm toughing through the stack stuff which helps me remember the stack operations anyway. I thought it was interesting that Claudio mentioned in the "new 50g firmware thread" one of the things they are doing is not requiring local variables to be on the stack initially.


RE: RPL programming questions - HP67 - 02-20-2014 03:40 PM

(02-19-2014 05:23 PM)DavidM Wrote:  You didn't misread my comments. The 23/21 sec. times were for the UserRPL programs that were re-coded without the single quotes around the variable names (and the subsequent RCL).

Thanks for clarifying. That is interesting and hard for me to reconcile, as I mentioned, unless the 50g devices themselves are that variable.


(02-19-2014 05:23 PM)DavidM Wrote:  
Code:
A 3, * B +

I missed something here. What does the comma do? Is that 50g-speak for integer in this example?

(02-19-2014 05:23 PM)DavidM Wrote:  But the above would be slightly slower and take up more memory. Is it worth it? As the programmer, you have to make that decision. If you're working on something that is likely to be updated later, it may be. If the above is in a tight loop that is executed thousands of times, the speed penalty may be excessive.

Until I get a cable I'm keying everything in on the device and that is slow (but not painful due to HP's unbelievable ability to pay attention and put out incredible stuff). On my listings that I keep elsewhere I have comments. I'm not worried about readability in the abstract, the HP 48 is slow, and for me whatever I code is going to be for performance uberalles.

(02-19-2014 05:23 PM)DavidM Wrote:  But as you can see, the former requires that the variables are already on the stack at the outset. The penalty for using stack-based operations is as has been mentioned by others: readability and code maintainability suffers.

Well, the point with stack-based languages is to keep everything on the stack as much as possible, right? I consider the code that makes it onto the device important only as far as that it works. The "real" source will live in a place where I can keep comments with it, so readability etc. should be fine, when I'm reading stuff in the right place.

(02-19-2014 05:23 PM)DavidM Wrote:  These are important factors that should not be discounted when considering how you write any application (on any platform). A program written with a bias toward using stack operations may be more efficient than one using local variables, but in most cases it will also be more fragile. Adding a new computation in the middle of previously written ones can wreak havoc with your carefully planned stack.

Yes! This is one very big difference from stack-based languages (and languages implemented on stack machines or machines that use a stack) as opposed to environments that are not stack-based. Making a mistake or having to change something later can be very time-consuming. And annoying! Happened to me just this morning when I coded up a command (is that the word for an RPL program? I keep wanting to say "word") as I went through a text and when I got to the end of a formula I saw what I thought was a typo. Then I found the formula was actually wrong. I contacted the author and got a correction and then I had to shoehorn the fix into the middle of my stack-dance. Not fun! But in the end, it is fun.


RE: RPL programming questions - DavidM - 02-20-2014 04:10 PM

(02-20-2014 03:40 PM)HP67 Wrote:  
(02-19-2014 05:23 PM)DavidM Wrote:  You didn't misread my comments. The 23/21 sec. times were for the UserRPL programs that were re-coded without the single quotes around the variable names (and the subsequent RCL).

Thanks for clarifying. That is interesting and hard for me to reconcile, as I mentioned, unless the 50g devices themselves are that variable.

I'm not sure what you mean about the 50g devices being variable. I believe the times for the same code on different 50g's should be similar. The fact that the modified code object runs faster is due to the simplification of the code.


(02-20-2014 03:40 PM)HP67 Wrote:  
(02-19-2014 05:23 PM)DavidM Wrote:  
Code:
A 3, * B +

I missed something here. What does the comma do? Is that 50g-speak for integer in this example?

The comma forces the 49/50 calculator to compile the "3" as a real instead of an integer value. Whether you use the comma or a period as the fraction mark depends on the mode settings for your specific calculator.


RE: RPL programming questions - HP67 - 02-20-2014 04:36 PM

Thank you.


RE: RPL programming questions - RMollov - 02-24-2014 12:47 PM

(02-17-2014 01:54 PM)HP67 Wrote:  I'm getting to know RPL on an HP 48. I've done a lot of programming on the older so-called "keystroke" programmable models from HP and even TI and it's pretty simple compared to RPL. I've been trying to read everything I can find on the net about RPL and it seems like the general recommendation is to avoid local variables. That works fine on small programs but it gets tricky on programs with a lot of calculations and intermediate variables.

Matt Agajanian would know a lot about all that stuff Wink
I smell a rat.