Thread Tools Display Modes
05-03-10, 11:59 PM   #1
corveroth
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 29
Maintaining a sortable database, garbage creation

I have an issue, to the tune of 80 KB of junk every time COMPANION_UPDATE fires.

For my addon, I find it necessary to keep up to date information on your companions. Beyond simply registering COMPANION_UPDATE and calling GetCompanionInfo, I also pull some extra data from tooltips and hard-coded tables, in order to offer more information to the user (such as mount speed).

The issues arose when I added in the capacity to sort that data. Three columns appear in the display, and the tabs at the top of each enable sorting, much like the Guild or Who frames. To allow for this behavior, I found it necessary to store several pieces of information in a table, which I could later sort, and whenever the display needed to be refreshed, I read off the appropriate rows from the table. The problem appears to lie in the need to rebuild the entire table every time that COMPANION_UPDATE fires (very common in cities, as it fires for nearby players as well).

What recommendations might you have to offer to cut down on the garbage generation inherent to this approach? What alternative approaches might exist?

lua Code:
  1. local MountData = {}
  2. local function UpdateMountData()
  3.     MountData = {}
  4.     local creatureName, spellID, icon, active
  5.     local speed, mountType, numPassengers, isUsable, varies
  6.     local notes
  7.    
  8.     for i=1, GetNumCompanions("MOUNT") do
  9.         notes = ""
  10.         _, creatureName, spellID, icon, active = GetCompanionInfo("MOUNT", i)
  11.         -- GetExtraCompanionData is my function that does the tooltip scanning and such
  12.         speed, mountType, numPassengers, isUsable, varies = unpack(GetExtraCompanionData("MOUNT", spellID),1,5)
  13.        
  14.         if numPassengers > 0 then
  15.             -- NOTE: addLine is just a utility function to insert newlines as appropriate
  16.             notes = addLine(notes, format(L["Passengers: %d"], numPassengers))
  17.         end
  18.        
  19.         if varies then
  20.             notes = addLine(notes, L["Varies"])
  21.         end
  22.        
  23.         if not strfind(notes, "\n") then
  24.             if mountType == MOUNTTYPE_LAND then
  25.                 notes = addLine(notes, L["Ground"])
  26.             elseif mountType == MOUNTTYPE_FLYING then
  27.                 notes = addLine(notes, L["Flying"])
  28.             elseif mountType == MOUNTTYPE_AQUATIC then
  29.                 notes = addLine(notes, L["Aquatic"])
  30.             end
  31.         end
  32.        
  33.         tinsert(MountData, {NAME = creatureName, NOTES = notes, SPEED = speed, ICON = icon, ACTIVE = active, ISUSABLE = isUsable, ID = i})
  34.     end
  35. end

Last edited by corveroth : 05-04-10 at 12:13 AM.
  Reply With Quote
05-04-10, 12:51 AM   #2
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Get rid of the assignment of a new table to MountData inside of the function. Just call wipe(MountData) at the beginning of your function.
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
05-04-10, 01:01 AM   #3
Shadowed
...
Premium Member
Featured
Join Date: Feb 2006
Posts: 387
Originally Posted by Torhal View Post
Get rid of the assignment of a new table to MountData inside of the function. Just call wipe(MountData) at the beginning of your function.
That isn't what is causing the garbage, it's due to creating a new table for every mount. Because you never actually lose a mount, you can do this pretty easily, where you just reuse the table and only add new data when necessary. Change it to something like this:

lua Code:
  1. local MountData = {}
  2. local function UpdateMountData()
  3.     local creatureName, spellID, icon, active
  4.     local speed, mountType, numPassengers, isUsable, varies
  5.     local notes
  6.    
  7.     for i=1, GetNumCompanions("MOUNT") do
  8.         notes = ""
  9.         _, creatureName, spellID, icon, active = GetCompanionInfo("MOUNT", i)
  10.         -- GetExtraCompanionData is my function that does the tooltip scanning and such
  11.         speed, mountType, numPassengers, isUsable, varies = unpack(GetExtraCompanionData("MOUNT", spellID),1,5)
  12.        
  13.         if numPassengers > 0 then
  14.             -- NOTE: addLine is just a utility function to insert newlines as appropriate
  15.             notes = addLine(notes, format(L["Passengers: %d"], numPassengers))
  16.         end
  17.        
  18.         if varies then
  19.             notes = addLine(notes, L["Varies"])
  20.         end
  21.        
  22.         if not strfind(notes, "\n") then
  23.             if mountType == MOUNTTYPE_LAND then
  24.                 notes = addLine(notes, L["Ground"])
  25.             elseif mountType == MOUNTTYPE_FLYING then
  26.                 notes = addLine(notes, L["Flying"])
  27.             elseif mountType == MOUNTTYPE_AQUATIC then
  28.                 notes = addLine(notes, L["Aquatic"])
  29.             end
  30.         end
  31.         mount = MountData[spellID] or {NAME = creatureName, NOTES = notes, SPEED = speed, ICON = icon, ACTIVE = active, ISUSABLE = isUsable, ID = i}
  32.         mount.active = active
  33.         MountData[spellID] = mount
  34.         tinsert(MountData, mount)
  35.     end
  36. end

That way you're only creating a new table if you see a new mount, the only thing to keep in mind with this method is you can't do for id, mount in pairs(MountData) for looping without getting extra data, you want to do for id=1, #(MountData) do local mount = MountData[id] end.

Last edited by Shadowed : 05-04-10 at 01:11 AM.
  Reply With Quote
05-04-10, 01:03 AM   #4
corveroth
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 29
Originally Posted by Torhal View Post
Get rid of the assignment of a new table to MountData inside of the function. Just call wipe(MountData) at the beginning of your function.
I'd had it that way for a while; changing it back, there's no appreciable difference in the rate of memory growth.
  Reply With Quote
05-04-10, 01:06 AM   #5
corveroth
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 29
Originally Posted by Shadowed View Post
That way you're only creating a new table if you see a new mount
Unfortunately, that reasoning is fallacious. For many mounts, that's true, and the only piece of data that needs to be changed is the active state. However, there are now a number of variable-speed or -type mounts in game which must be rechecked every time the event fires.

EDIT: Hold on. Pause. Your changes make sense; let me edit them as appropriate and see how that goes.
  Reply With Quote
05-04-10, 01:15 AM   #6
Torhal
A Pyroguard Emberseer
 
Torhal's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2008
Posts: 1,196
Originally Posted by Shadowed View Post
That isn't what is causing the garbage, it's due to creating a new table for every mount.
Ah. That's what I get for spouting off after merely glancing
__________________
Whenever someone says "pls" because it's shorter than "please", I say "no" because it's shorter than "yes".

Author of NPCScan and many other AddOns.
  Reply With Quote
05-04-10, 01:20 AM   #7
corveroth
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 29
Many thanks, Shadowed. That change, with a couple of tweaks, has cut growth by an order of magnitude, and I think I can find the rest in cleanup. Thank you greatly.
  Reply With Quote
05-04-10, 01:28 AM   #8
Shadowed
...
Premium Member
Featured
Join Date: Feb 2006
Posts: 387
Originally Posted by corveroth View Post
Unfortunately, that reasoning is fallacious. For many mounts, that's true, and the only piece of data that needs to be changed is the active state. However, there are now a number of variable-speed or -type mounts in game which must be rechecked every time the event fires.

EDIT: Hold on. Pause. Your changes make sense; let me edit them as appropriate and see how that goes.
Ah, yea in that case you'll want to just update the notes or whatnot on update. The companion events fire semi-frequently from what I can recall, so you might also want to bucket them every 0.5-1 seconds.

Originally Posted by Torhal View Post
Ah. That's what I get for spouting off after merely glancing
Yea I was a bit puzzled how a single table was making 80KB of garbage for a minute!
  Reply With Quote
05-04-10, 01:32 AM   #9
corveroth
A Fallenroot Satyr
AddOn Author - Click to view addons
Join Date: Apr 2006
Posts: 29
Originally Posted by Shadowed View Post
The companion events fire semi-frequently from what I can recall, so you might also want to bucket them every 0.5-1 seconds
They're horribly unpredictable, really. If you're in Dalaran at prime time, you might see them that often, but if you're out questing, you'll hardly get any at all.
  Reply With Quote
05-04-10, 03:51 AM   #10
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
The only situation in which you lose mounts is by Blizzard removing them and a faction change with faction-only mounts I think.
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Maintaining a sortable database, garbage creation

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off