Showing posts with label perform. Show all posts
Showing posts with label perform. Show all posts

Friday, 4 July 2014

Generating random numbers (again)

A couple of years ago I wrote a post about generating random numbers.  I talked about three different ways of generating a random number (in absence of a $random function in Uniface)...

1) Use a perform to call a DLL (or shared object) that returns a random number.
2) Use $uuid to generate a random number in Uniface.
3) Use a DIY Linear Congruential Generator, built in Uniface.


I found that the perform was the best for performance, and I also stated that it gave a better randomness as well, but I failed to back this up (sorry). 

This issue has raised it's head again, as we're currently trying to get rid of all our own external libraries (DLL/SO) in order to improve maintainability and interoperability.

This time I'm going to test over 1,000,000 iterations (instead of 2,000,000 as before), as there's a little more code inside the loop to store the generated numbers.  Each method will be generating numbers between 0 and 99, and I will then plot the number of times each of these numbers is generated, and report the standard deviation as well - this should give a good idea of the spread.

1) perform: 35.98 seconds (0.036ms per value) - standard deviation: 28.870


2) $uuid42.8 seconds (0.048ms per value) - standard deviation: 28.869


3) DIY: 56.54 seconds (0.057ms per value) - standard deviation: 28.894


As you can see from these charts, and the standard deviation of the numbers generated, all three of these methods have a similarly decent spread.  The time difference hasn't been quite as drastic this time, although that's likely to be due to the extra code in the loop that stores the values for the spread analysis.  Unless you're planning to generate a million random numbers, it's not likely to have a noticeable effect on performance though.

One thing to note though, if you're only concerned with Windows environments, then you don't have a problem, and as long as your DLLs (or shared objects in Unix) are equivalent, then you don't have a problem.  However, the $uuid method does not work as well in Unix environments...

4) $uuid in Unix: (time is not comparable) - standard deviation: 28.833



As you can see, the spread is not uniform at all.  This is because UUID is not sufficiently random in Unix - the first 5 characters seem to be time based, then 3 random characters, then the rest remains constant within the same userver (presumably based on the machine itself and the process ID).  

Summary: As before, perform is the quickest method, but if you're supporting multiple environment configurations then it's likely that building your own random number generator (possibly using $uuid on Windows) is going to make your life much easier.

Additional: There's now a blog post about this on Uniface.info, if you're interested... http://unifaceinfo.com/random-number-generator-uniface/

Saturday, 7 June 2014

Sendmessage versus postmessage

If you wanted to send a message from one form to another, you could simply use sendmessage.  This was deprecated in favour of using activate, a command which has become pretty much a catch-all for all sorts of earlier commands, including run, perform, and spawn.  These all still work in Uniface 9.6, but as they were deprecated in Uniface 9.1, it's well worth trying to write these out of your code.

One example of this might be if you have retrieved multiple records in one form, and also have a second form open with related records in it; when you scroll through the occurrences in the first form you might want to post the primary key details through to the second form, so that only the records related to the current occurrence in the first form are displayed.

In order to do this, the first form would need to have code in the "Occurrence Gets Focus" <OGF> trigger, something like this...


  sendmessage "SECOND_FORM","PKEY","FIELD1.ENT=%%FIELD1.ENT%%%"

As you can see, because sendmessage is now deprecated, it is not highlighted as a reserved word.  You also get a compile warning like this...

  warning: 1000 - Deprecated statement 'sendmessage' used

You then need to have some code to receive this message in your second form, which needs to be in the "Asynchronous Interrupt" <ASYS> trigger, something like this...


  the_result = $result ;will be "message"
  message_id = $msgid ;will be "PKEY"
  message_data = $msgdata ;will be "FIELD1.ENT=xxx"

When using sendmessage or postmessage, $result will always be set to "message".  The second value that you define will become the $msgid and then third value will become the $msgdata.  

Please note:  If the <ASYS> trigger in the called component is blank then the application <ASYS> trigger will be called, as defined in the start-up shell.

So what's the difference between sendmessage and postmessage?  Can you simply go through and replace every sendmessage with a postmessage, or should you be using activate instead?

Well the difference is that sendmessage is synchronous and postmessage is asynchronous, which means that potentially there's a delay before the message will be received and processed with a postmessage.  Using an activate would be synchronous but you'd need to rewrite your code, probably moving the code in the <ASYS> trigger into an operation and then calling that from the <OGF> trigger.

Is this potential delay likely to cause a problem though?  Well for starters, can we see it in practice?  The answer to this is "yes", by putting sending code like this somewhere in a form (I went with the <EXEC> trigger)...


  putmess "Before sendmessage"
  sendmessage "TEST_PERF","MESSAGE","SENDMESSAGE"
  putmess "After sendmessage"

  putmess "Before postmessage"
  postmessage "TEST_PERF","MESSAGE","POSTMESSAGE"
  putmess "After postmessage"

And then putting the receiving code in the <ASYS> trigger, like this...


  putmess "In ASYS: ID=%%$msgid%%%, DATA=%%$msgdata%%%"

We can then run the form and view the message buffer...

  Before sendmessage
  In ASYS: ID=MESSAGE, DATA=SENDMESSAGE
  After sendmessage
  Before postmessage
  After postmessage
  In ASYS: ID=MESSAGE, DATA=POSTMESSAGE

As you can see, the sendmessage happens between the "before" and "after" (synchronously), whereas the postmessage happens at the very end (asynchronously).  This means that you have to be very careful about changing any sendmessage statements into postmessage statements - the code order will change, and you need to be aware of this and check this will not cause a problem.

As far as my testing has gone, the messages are always received and processed in the order that they were posted, as you may expect from a message queue.  Having said that, I've not found anything in the manuals that states explicitly that this order is enforced, so writing code that relies on the messages being processed in a particular order might not be wise.


Summary: The deprecated sendmessage command was sychronous whereas the postmessage command is asychronous, so you must be careful if you replace one with the other.  However, this is likely to be more straight forward than replacing with the activate command, as the manual suggests.

Tuesday, 31 July 2012

Generating random numbers

One thing that is always difficult in a system is generating a truly random number.  Computers aren't random, they're very logical, therefore this is inherently difficult.  Having said that, there's always a way to calculate a number which is "random enough".  There is no function for this in Uniface, so I'm going to look at a few different ways to achieve this.


1) perform - the only way we could find to do this originally was to build a random number function in C++ and then call out to it from our Uniface program.  Something like this...


  perform "GetRandomNumber" ;call 3gl function which returns 0-32767
  rand = $1 / 32767


2) $uuid - since the Uniface Unique Identifer function was added, this has given an alternative method.  This is largely based on the current timestamp and either includes the processor ID or the ethernet address, depending on the operating system you are using.  The value returned is a 32 character hexadecimal string, so we need to remove the non-numeric characters.  



  rand = "0.%%$replace($replace($uuid,1,'&',"",-1),1,"-","",-1)%%%" * 1



We actually found that this was not random enough on non-Windows systems, as the last part of the identifier is the same throughout each transaction, so we have used characters from 3 identifiers.


3) DIY - you could also create your own random number generator, after all, these are just mathematical formulas.  The C++ "rand" function that we utilise in method (1) is a simple Linear Congruential Generator.  This takes an initial seed value and then uses it to create the next number in the sequence, which is then used as the seed for the next number.  The key is finding a combination of values that gives an evenly distributed spread of numbers, to ensure that the numbers appear suitably random.


  $$rand = ((214013 * $$rand) + 2531011) % 4294967296
  rand = $$rand / 4294967296


As you can see, this relies on the seed value already being populated, which I've stored in a global register in this example.  This could be set in the application shell execute trigger, maybe using $uuid or a time based numeric.  


Often these algorithms return a subset of the bits in order to improve the spread, but it is not possible to do extraction at the bit level in Uniface, as far as I'm aware.  Another algorithm that is popular (and generally considered better) is a Mersenne Twister, but this uses bit-shifting techniques that I don't think are possible in Uniface either.


So let's test the performance of these different methods of 2,000,000 iterations...


1) perform = 00:10.00, 00:10.00, 00:10.01 (10 seconds)
2) $uuid = 00:32.31, 00:32.44, 00:32.39 (over 32 seconds)
3) DIY = 00:34.75, 00:34.72, 00:34.72 (under 35 seconds)


As you can see, the original perform is the quickest method (although we've found that generally using a 3GL function does not hold up very well under load and these tests are only as a single user).  It can be hard to support a 3GL function across multiple platforms, but this solution is mathematically the most random method.  Out of the alternatives, $$uuid is quite simple but does not give a good spread of random numbers, not compared with the DIY method.  


It should be emphasised that none of these methods are truly random, and therefore should not be used for cryptographic purposes.  They should be suitable for simple things though, like simulating a dice throw.


Hopefully one day Uniface will provide it's own $rand or $random function - a native function should perform the best and would hopefully be implemented in a way that was suitably random with a decent spread.


Summary: If it's feasible to use a perform then this is the best way to go, both for speed and randomness.  However, you may wish to consider building your own random number generator, possibly using a Linear Congruential algorithm.