Register to post in forums, or Log in to your existing account
 

Play RetroMUD
Post new topic  Reply to topic     Home » Forums » CMUD Beta Forum
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Wed Jun 16, 2010 7:47 pm   

Variable access by "reference" vs by "value"
 
In the last update I fixed a problem with various functions like %additem, %delitem, etc that were modifying the list/table passed in the second argument. So I changed them so they create a copy of the table to return.

Problem with this is that it will slow down scripts that just use %additem because it allows duplicates. You don't really want a line like:
Code:
list = %additem("item", @list)

to cause a copy of the @list to be created every time.

So what we need here is a way to access @list by "reference" instead of by "value". What I'm going to try and support in the next version is something similar to how the #ADDITEM-like commands handle this and allow you to specify the name of the list without the @ character.

With this change, you'll have the following syntax:
Code:
list = %additem("item", @list)  // makes a copy of @list just like it is supposed to
list = %additem("item", list)  // access @list directly and modify it in place
#CALL %additem("item", list)  // same as above.  Since @list is modified in place, there is no need to assign it
list = %additem("item", "list")  // returns "list|item" treating the second argument as a literal string value
$list = %additem("item", $list)  // local variables will ALWAYS be access by reference, so $list is modified in place
#CALL %additem("item", $list) // same as above...assignment not necessary
$newlist = %additem("item", %list($list)) // the %list function can be used to force a Copy of a table to be created.  $list is not modified in this case

I think this covers all of the cases. Let me know if you see any potential problems with this.
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Wed Jun 16, 2010 7:50 pm   
 
Sounds like quite the elegant solution. Good stuff!

Edit: Nvm.
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Thu Jun 17, 2010 11:23 pm   
 
Will it be possible to use this on nested lists as is possible with #additem?

Such as: #call %additem( "item", list.item) or #call %additem( "item", db.key)

Instead of #var list.item %additem( "item", @list.item) or #addkey db key %additem( "item", @db.key)
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jun 18, 2010 2:19 am   
 
Yes. Give it a try, it should already work.
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Fri Jun 18, 2010 2:20 am   
 
Oh, I did not realize that this was already put in. I'll give it a go and give you an update.
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Fri Jun 18, 2010 2:57 am   
 
Alright, so a couple issue. If you feed it a key and it doesn't exist, then it won't create it. Additionally, if the key does exist, but there's nothing in it, then it will treat it as a null item. This script should illustrate what I'm talking about.

Moved to: http://forums.zuggsoft.com/forums/viewtopic.php?t=35355

I would think, ideally, that %additem( "item", ref) would act the same as #additem, but with duplicates.


Last edited by GeneralStonewall on Sat Jun 19, 2010 8:13 pm; edited 2 times in total
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Fri Jun 18, 2010 6:47 am   
 
It also appears that #call %delitem( "item", ref) is slower than #delitem even when the list has no duplicates; I would think the opposite would be true. Assuming a list with no duplicates, #delitem will search from the beginning to the end of the list every time, while %delitem would stop after finding an item. Am I making any erroneous assumptions here? I'm not quite sure what the difference between a command and a built-in function is.

Also, sort of off the issue between %delitem and #delitem, but I'm surprised to see that the unsorted tests are faster. Is this due to the list being re-sorted after each item is deleted? Seems to me that if you remove an item from a sorted list, that it should still already be sorted. Furthermore, if #delitem is going through a sorted list, finds an item, deletes it, and then the next item doesn't match, it would be logical that there's no more matching items in the list, so it should stop. Again, that's me making assumptions on top of assumptions, but I figured I'd throw some of my thoughts out there.

Here's the script I used to test:

code:
Code:
#local $nitems $list
#if (!%null(%1)) {
  $nitems = %1
  } {
  $nitems = 1000
  }
#print ""
#print {Setting up a local list with up to $nitems items, no duplicates.}
#loop $nitems {
  #additem $list %random(0, 9999)
  }
#print {There are %numitems($list) items in the list.}
#print {The len of the list is %len($list) characters.}
#print "-------------------------"
// test1
#print {Test1: sorted, #delitem}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 1
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #delitem testvar %i
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"
// test2
#print {Test2: sorted, #delnitem, ~%ismember}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 1
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #delnitem testvar %ismember( %i, @testvar)
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"
// test3
#print {Test3: unsorted, #delitem}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 0
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #delitem testvar %i
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"
// test4
#print {Test4: unsorted, #delnitem, ~%ismember}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 0
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #delnitem testvar %ismember( %i, @testvar)
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"
// test5
#print {Test5: sorted, ~%delitem}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 1
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #call %delitem( %i, testvar)
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"
// test6
#print {Test6: unsorted, ~%delitem}
#print {Setting testvar to value of list.}
testvar = $list
#sort testvar 0
#print {Looping through testvar and deleting each item.}
$start_time = %secs
#forall @testvar {
  #call %delitem( %i, testvar)
  }
#print {Finished in %eval( %secs - $start_time) seconds.}
#print {Testvar: @testvar}
#print "-------------------------"


example output:
Quote:
Setting up a local list with up to 1000 items, no duplicates.
There are 952 items in the list.
The len of the list is 4660 characters.
-------------------------
Test1: sorted, #delitem
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 1384 seconds.
Testvar:
-------------------------
Test2: sorted, #delnitem, %ismember
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 1393 seconds.
Testvar:
-------------------------
Test3: unsorted, #delitem
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 1024 seconds.
Testvar:
-------------------------
Test4: unsorted, #delnitem, %ismember
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 1032 seconds.
Testvar:
-------------------------
Test5: sorted, %delitem
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 2207 seconds.
Testvar:
-------------------------
Test6: unsorted, %delitem
Setting testvar to value of list.
Looping through testvar and deleting each item.
Finished in 1884 seconds.
Testvar:
-------------------------
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jun 18, 2010 4:46 pm   
 
Thanks for the script. That's just the kind of additional benchmark that I was planning to do next week.

The difference between #delitem and %delitem definitely looks to be odd. #delitem *should* be faster because it has to do an additional "ismember" to determine if there is more than one matching item that needs to be deleted. But it's possible that %delitem is still converting the table to a string value somewhere along the line even though you are using #call and the new "by reference" syntax. Definitely something I'll look into.

The reason the Sorted #delitem is slower than unsorted is that when a string list is sorted, CMUD needs to maintain an additional binary search tree for the items in the list. Deleting an item from a binary search tree requires the tree to be rebalanced sometimes. I haven't looked at the algorithm used for this in any real detail, but that's a small overhead that the unsorted list doesn't have. The difference between the two is small enough that I'm not really worried about this unless it doesn't scale properly for larger lists.

Oh, and for other people who might be wondering, the timing values above are really "milliseconds" and not "seconds" :)
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Fri Jun 18, 2010 6:14 pm   
 
I noticed that none of these tests scaled very well. When I did 10k items, the time it took was absolutely huge. Though, I don't know anyone who's going to do anything like this on a 10k long list. But yeah, milliseconds, not seconds.

Also, did you see my previous post about %additem?
Reply with quote
Yodous
Apprentice


Joined: 23 Jun 2004
Posts: 105
Location: Poland

PostPosted: Fri Jun 18, 2010 6:56 pm   
 
Ok. I have got questions:
1)
This works fine:
Code:
$tmpName = %list("one");
#VAR test %additem("two", $tmpName)


But this:
Code:
$tmpName = "one";
#VAR test %additem("two", $tmpName)


Creates the variable "one" with no value and the variable "test" with value "one|two". Shouldn't those two examples works in the same way (without creating the variable 'one')?

2)
Before the update of the version
Code:
$tmpName = %null
$tmpName = %additem("one", $tmpName)

Force $tmpName to has a value "one". Currently, the same code, make the $tmpName with value "|one". Why and how can I have the same functionality as before (to initialize the null variable and add items using %additem)

Best regards
Yodous


Last edited by Yodous on Fri Jun 18, 2010 6:57 pm; edited 1 time in total
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jun 18, 2010 6:57 pm   
 
Yep, I saw the %additem issue. Creating keys that don't exist yet is on my to-do list. I want to fix it in a general way that will allow subkeys so that stuff like list.key1.key2 will also work. Not sure if I'll get this into 3.20 or not.

On #delitem, yep, I confirmed the scaling issue. I re-ran my original #additem benchmarks to confirm that they still worked, and they are even faster than before (the nosave optimization helps here). But #delitem is definitely too slow and I'm not exactly sure why. But I'm going to look into it.

Edited: Also, if I test the speed of %additem vs #ADDITEM, the %additem seems to have the same problems as #DELITEM. So maybe they are related and somewhere it's converting the list to a string when it shouldn't.
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Fri Jun 18, 2010 7:10 pm   
 
Yodous, Zugg is aware of this bug and I presume it is likely to be fixed by a release later today. http://forums.zuggsoft.com/forums/viewtopic.php?t=35331
Reply with quote
Yodous
Apprentice


Joined: 23 Jun 2004
Posts: 105
Location: Poland

PostPosted: Fri Jun 18, 2010 7:12 pm   
 
Oh, right. So another thing came out with my second question. Thx for answer :)
And maybe you can give me some hint how to download the previous beta version?
Reply with quote
MattLofton
GURU


Joined: 23 Dec 2000
Posts: 4834
Location: USA

PostPosted: Fri Jun 18, 2010 8:13 pm   
 
Quote:

And maybe you can give me some hint how to download the previous beta version?


You find it on your computer and install it again (you had to dowload it first in order to install). Keep in mind that depending on which previous version you are going back to might force you to create new data files entirely instead of using the ones that made it into the newer version you are leaving.

If it's not on the computer, you need to get it from a fellow player. Zugg doesn't hand out older versions for any other reason than "a large number of my customers cannot reasonably use the latest version and I am out of time to fix this issue".

If fellow players are unwilling to give it to you, you stop looking for the file.
_________________
EDIT: I didn't like my old signature
Reply with quote
charneus
Wizard


Joined: 19 Jun 2005
Posts: 1876
Location: California

PostPosted: Fri Jun 18, 2010 8:42 pm   
 
As I stated in the thread where you asked this question, Youdus, the purpose of the Beta version is not to use the most stable one. It's for testing purposes, not for playability purposes. If you want a newer, stable public version, then we need to get people to test the Betas and provide exact procedures on how to recreate bugs. That's really the only way Zugg has of knowing what needs to be fixed since players around create different scripts for different things and can break things others can't.

Charneus
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Fri Jun 18, 2010 8:43 pm   
 
Please NEVER go back to a previous beta version. You can go back to the Public version, but not a previous Beta version. Going back to a previous beta version can cause your settings to be corrupted or lost. There is no compatibility going backwards.

If you are not willing to wait for the next update, then you should not be using beta versions at all.
Reply with quote
Yodous
Apprentice


Joined: 23 Jun 2004
Posts: 105
Location: Poland

PostPosted: Fri Jun 18, 2010 9:25 pm   
 
Ok. Thx!
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Sat Jun 19, 2010 8:02 am   
 
For v3.20

Should there be any functional difference between %db( var, "key") and %ismember( "item", var) versus %db( @var, "key") and %ismember( "item", @var)? It seems to me that they should behave exactly the same since none of them actually modify the variables passed to them. That said, it seems using the @var syntax causes a ridiculous decrease in speed as illustrated by the following script:

code:
Code:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<cmud>
  <alias name="benchmark" copy="yes">
    <value>#show ""
#show {-Makelist %1-}
makelist %1
#show {-Makedb %1-}
makedb %1
#show {-Querylist 100-}
querylist 100
#show {-Querylist 10000-}
querylist 10000
#show {-Querydb 100-}
querydb 100
#show {-Querydb 10000-}
querydb 10000
#show {-Querylist_Ref 100-}
querylist_ref 100
#show {-Querylist_Ref 10000-}
querylist_ref 10000
#show {-Querydb_Ref 100-}
querydb_ref 100
#show {-Querydb_Ref 10000-}
querydb_ref 10000</value>
  </alias>
  <alias name="makedb" copy="yes">
    <value>db = %null
$StartTime = %secs
#LOOP %1 {#ADDKEY db %i %i}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
  <alias name="makelist" copy="yes">
    <value>list = %null
$StartTime = %secs
#LOOP %1 {#ADDITEM list %i}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
  <alias name="querydb" copy="yes">
    <value>$StartTime = %secs
#LOOP %1 {#CALL %db( @db, %i)}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
  <alias name="querydb_ref" copy="yes">
    <value>$StartTime = %secs
#LOOP %1 {#CALL %db( db, %i)}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
  <alias name="querylist" copy="yes">
    <value>$StartTime = %secs
#LOOP %1 {#CALL %ismember( %i, @list)}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
  <alias name="querylist_ref" copy="yes">
    <value>$StartTime = %secs
#LOOP %1 {#CALL %ismember( %i, list)}
#SHOW {Time: %eval( %secs - $StartTime)}</value>
  </alias>
</cmud>


3.20 output:
Quote:
-Makelist 1000-
Time: 18
-Makedb 1000-
Time: 20
-Querylist 100-
Time: 218
-Querylist 10000-
Time: 21690
-Querydb 100-
Time: 111
-Querydb 10000-
Time: 13132
-Querylist_Ref 100-
Time: 1
-Querylist_Ref 10000-
Time: 123
-Querydb_Ref 100-
Time: 1
-Querydb_Ref 10000-
Time: 128


These are all aliases, slightly modified, that I pulled from your benchmark test here. The scaling of nearly all of these are drastically different from the ones you posted. So, something is definitely off.

For additional reference, here are the same scripts, minus the _ref aliases, ran on 3.17:

3.17 output:
Quote:
-Makelist 1000-
Time: 227
-Makedb 1000-
Time: 45
-Querylist 100-
Time: 3
-Querylist 10000-
Time: 2024
-Querydb 100-
Time: 1
-Querydb 10000-
Time: 495


Of course, there's always the possibility that I frak'd something up.


Last edited by GeneralStonewall on Sat Jun 19, 2010 11:48 pm; edited 1 time in total
Reply with quote
Yodous
Apprentice


Joined: 23 Jun 2004
Posts: 105
Location: Poland

PostPosted: Sat Jun 19, 2010 7:35 pm   
 
Ok. And what about my seconds question:

Code:
$tmpName = %null
$tmpName = %additem("one", $tmpName)

Before the update of the version, the above code forces $tmpName to has a value "one". Currently, the same code, make the $tmpName with value "|one". How can I initialize the empty variable, and add to it a value (building a list)? Below I show a thing, that I would like to achieve:

Code:
$tmpName = %null //initialize the variable
$tmpName = %additem("one", $tmpName) //I want to have a value "one"
$tmpName = %additem("two", $tmpName) //Now I want to have a value "one|two"

Is there some nice way to achieve this without using some substrings?

Best regards
Reply with quote
charneus
Wizard


Joined: 19 Jun 2005
Posts: 1876
Location: California

PostPosted: Sat Jun 19, 2010 7:54 pm   
 
Unless you're planning on having duplicate items in the string, then you don't need to use %additem anyway.

Charneus
Reply with quote
Yodous
Apprentice


Joined: 23 Jun 2004
Posts: 105
Location: Poland

PostPosted: Sat Jun 19, 2010 8:02 pm   
 
I want to do exacly what I wrote. Because I have to use hmm.. maybe another example
Code:

$tmpList = "onet|two|three"
$tmpValue = %null
#FO $tmpList {
     $tmpValue = %additem(%i, $tmpValue)
     //do some checks
}

So once more time, how to force this functionality:
Code:
$tmpName = %null //initialize the variable
$tmpName = %additem("one", $tmpName) //I want to have a value "one"
$tmpName = %additem("two", $tmpName) //Now I want to have a value "one|two"


After an update to the 3.20 beta, "$tmpValue = %list()" or simply "#SA %list()" throw an application exception (access violation at read...)


Last edited by Yodous on Sat Jun 19, 2010 8:13 pm; edited 2 times in total
Reply with quote
GeneralStonewall
Magician


Joined: 02 Feb 2004
Posts: 364
Location: USA

PostPosted: Sat Jun 19, 2010 8:11 pm   
 
That's a bug and doesn't really have anything to due with accessing by reference versus by value, you should make a new thread.
Reply with quote
Zugg
MASTER


Joined: 25 Sep 2000
Posts: 23379
Location: Colorado, USA

PostPosted: Mon Jun 21, 2010 4:30 pm   
 
This thread is starting to get a bit off track.

Remember to create a NEW POST is you want to report a bug.

General: I think the answer to "should these work the same..." above is "Yes", however any time you have @varname in a function, CMUD is probably going to expand it to a string value, thus slowing down the script. Without the @ a reference to the variable just gets pushed onto the internal stack rather than the string value itself.

But as the other benchmarks have already shown in other posts, there is still something wrong with the %functions in terms of their speed and scaling, so I'm already planning to look into that in more detail.
Reply with quote
Display posts from previous:   
Post new topic   Reply to topic     Home » Forums » CMUD Beta Forum All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

© 2009 Zugg Software. Hosted by Wolfpaw.net