+ Reply to Thread
Page 1 of 2 1 2 LastLast
Results 1 to 15 of 21

  Click here to go to the first Rift Team post in this thread.   Thread: What is going on with the layout API?

  1. #1
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default What is going on with the layout API?

    Is it just me, or has the overhead for layout-related calls gone through the roof?

    I've weeded out a number of superfluous calls, but there are some that just aren't optional for my addons to function properly. Since 1.6 dropped, I've gone from my addons running smooth as silk, to, what in my book, is unacceptable performance.

    Am I the only one seeing this?

  2. #2
    RIFT Community Ambassador the_real_seebs's Avatar
    Join Date
    Jan 2011
    Posts
    16,859

    Default

    Well, one thing is that the time for a lot of stuff is now budgeted to the caller, when it might not have been before.
    You can play WoW in any MMO. You don't have to play WoW in RIFT. Oh, and no, RIFT is not a WoW clone. Not having fun any more? Learn to play, noob! I don't speak for Riftui, but I moderate stuff there. Just came back? Welcome back! Here's what's changed. (Updated for 2.5!)

  3.   Click here to go to the next Rift Team post in this thread.   #3
    Rift Team
    Join Date
    Oct 2010
    Posts
    927

    Default

    Quote Originally Posted by the_real_seebs View Post
    Well, one thing is that the time for a lot of stuff is now budgeted to the caller, when it might not have been before.
    This is a pretty significant factor. Let's say 2% of the game CPU is spent in your addon, and 10% is spent rendering your UI. Before 1.6, that would show up as 2% CPU usage. Now it'll show up as 12% CPU usage. Nothing in that example changed besides us doing a better job of instrumentation.

    It's possible some things are running more slowly than they used to. If you have a good example of something running slowly, please post some example code so we can see what's going on.

    Note that masks are currently much slower than you'd expect - we recommend making sparse use of them if at all possible.

  4. #4
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default

    I will try to find time this weekend to extract some code to demonstrate it.

    On the one hand it's good b/c I identified some redundant layout code. On the other hand, there has to be a problem with 1.6 b/c even with the redundant layout code in place, everything ran smoothly. When 1.6 dropped, all of a sudden there are significant stutters. Eliminating significant amounts of the redundant layout code helped alleviate that to a great degree, but there is still a big slowdown.

  5. #5
    Sword of Telara p3t3r1's Avatar
    Join Date
    May 2011
    Posts
    841

    Default

    Quote Originally Posted by jca View Post
    I will try to find time this weekend to extract some code to demonstrate it.

    On the one hand it's good b/c I identified some redundant layout code. On the other hand, there has to be a problem with 1.6 b/c even with the redundant layout code in place, everything ran smoothly. When 1.6 dropped, all of a sudden there are significant stutters. Eliminating significant amounts of the redundant layout code helped alleviate that to a great degree, but there is still a big slowdown.
    Ya I tried to run the updated version of MyUI, it was causing stuttes/hiccups on my system even after I dropped the buffboxes and just had buffbars running. my CPU useage went up to 20% and I had to disable it entirely to be able to raid.

    Never had this issue before 1.6

  6. #6
    Champion Lorandii's Avatar
    Join Date
    Jun 2011
    Posts
    516

    Default

    One trick we used to do in WoW programming in order to save CPU cycles was to make local references of APIs, trading memory for CPU.
    Code:
    local _G = getfenv(0)
    local Inspect.System.Time = _G.Inspect.System.Time
    Is this still a valid solution to cut the CPU usage down?

  7. #7
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default

    Whether code could be better optimized or not really isn't the issue.

    The EXACT layout code executed w/o any issues whatsoever pre-1.6. After 1.6 (as in the same day) the EXACT same layout code causes a very significant stutter every time it's executed. I can put the layout routine on a timer and you'll get the stutter at that exact interval.

  8. #8
    Shadowlander
    Join Date
    Feb 2011
    Posts
    31

    Default

    Quote Originally Posted by Lorandii View Post
    One trick we used to do in WoW programming in order to save CPU cycles was to make local references of APIs, trading memory for CPU.
    Code:
    local _G = getfenv(0)
    local Inspect.System.Time = _G.Inspect.System.Time
    Is this still a valid solution to cut the CPU usage down?
    Access to local variables should still be faster than table lookup, hover I believe that you won't notice the difference.

    btw: by doing this:
    Code:
    local Inspect.System.Time = _G.Inspect.System.Time;
    you save only one table lookup.. if you really want to be as fast as possible go for
    Code:
    local InspectSystemTime = _G.Inspect.System.Time;

  9. #9
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default

    Here's an example of layout code that is magnitudes slower since 1.6. This basically just lays out a series of square buff boxes. Note, since 1.6 I can the eliminate,
    Code:
    box:ClearAll()
    box:Layout()
    calls here as those were needed in 1.5 to compensate for problems with Text frame sizing. Nevertheless, even with those two calls in place, the code executed with no measurable delay in 1.5. Since 1.6, every time this code is called there is a significant and noticeable stutter in Rift. I can put this on a timer that calls this 2 times/sec and there's a stutter, 2 times a second.

    Code:
    local function _layoutBarsBBS(self)
    	local def = self.Definition
    	
    	if (not def.enabled) then return end
    	
    	local row = 1
    	local col = 1
    	local maxRows = def.maxRows
    	local maxCols = def.maxCols
    	local n = 0
    	local max = maxRows * maxCols
    	
    	local anchorNext = self
    	local anchorRow = self
    	
    	local anchorFrom
    	local anchorTo
    	local anchorFromRow 
    	local anchorToRow
    	local anchorFromFirst
    	local anchorToFirst
    	
    	if (def.growHorz == "RIGHT") then
    		if (def.growVert == "DOWN") then
    			anchorFromFirst = "TOPLEFT"; anchorToFirst = "TOPLEFT";
    			anchorFrom = "TOPLEFT"; anchorTo = "TOPRIGHT";
    			anchorFromRow = "TOPLEFT"; anchorToRow = "BOTTOMLEFT";			
    		else
    			anchorFromFirst = "BOTTOMLEFT"; anchorToFirst = "BOTTOMLEFT";
    			anchorFrom = "BOTTOMLEFT"; anchorTo = "BOTTOMRIGHT";
    			anchorFromRow = "BOTTOMLEFT"; anchorToRow = "TOPLEFT";
    		end
    	else
    		if (def.growVert == "DOWN") then
    			anchorFromFirst = "TOPRIGHT"; anchorToFirst = "TOPRIGHT";
    			anchorFrom = "TOPRIGHT"; anchorTo = "TOPLEFT";
    			anchorFromRow = "TOPRIGHT"; anchorToRow = "BOTTOMRIGHT";
    		else
    			anchorFromFirst = "BOTTOMRIGHT"; anchorToFirst = "BOTTOMRIGHT";
    			anchorFrom = "BOTTOMRIGHT"; anchorTo= "BOTTOMLEFT";
    			anchorFromRow = "BOTTOMRIGHT"; anchorToRow = "TOPRIGHT";
    		end
    	end
    	
    	for _, box in ipairs(self.Boxes) do
    		if (n > max) then break end
    		
    		box:ClearAll()
    		box:Layout()
    		
    		if (n == 0) then
    			box:SetPoint(anchorFromFirst, self, anchorToFirst, 0, 0)
    			anchorRow = box
    		elseif (col >= maxCols) then
    			col = 1
    			box:SetPoint(anchorFromRow, anchorRow, anchorToRow, 0, 0)										
    			anchorRow = box	
    			row = row + 1
    		else
    			col = col + 1
    			box:SetPoint(anchorFrom, anchorNext, anchorTo, 0, 0)
    		end
    		
    		anchorNext = box
    		n = n + 1				
    	end
    end

  10. #10
    Champion
    Join Date
    Jun 2011
    Posts
    561

    Default

    Creating a lot of frames is definitely slower than before 1.6. I experienced it in the 'grid' widget in my nkGenie lib (or nkAdvisor addon where it is used). Before 1.6 there was a very small delay when first opening the addon interface. Since 1.6 the delay was very much noticeable. So much that I had to implement a functionality to my tabbed pane widget that UI elements will not be build before they are displayed.

    Once the frames are created showing / hiding them does not slow down things. To me it looks like a creating thing from the outside.

    Cheers
    N.

  11. #11
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default

    Quote Originally Posted by Naifu View Post
    Creating a lot of frames is definitely slower than before 1.6. I experienced it in the 'grid' widget in my nkGenie lib (or nkAdvisor addon where it is used). Before 1.6 there was a very small delay when first opening the addon interface. Since 1.6 the delay was very much noticeable. So much that I had to implement a functionality to my tabbed pane widget that UI elements will not be build before they are displayed.

    Once the frames are created showing / hiding them does not slow down things. To me it looks like a creating thing from the outside.

    Cheers
    N.
    I haven't really noticed whether frame creation has slowed or not. I'm using a recycling mechanism for any types of dynamically created frames so I'm not really noticing that.

    I'm seeing lag any time a significant number of frames are arranged. Take buff boxes for example, which is what the preceding example concerns. Say a player has 20 buffs. Whenever buffs are added/removed, I'm sorting the list of buffs based on a priority system, then I need to arrange the frames for each buffs in that order. Whenever I'm arranging those, for example, 20 buff frames, there's a very very noticable lag or stutter since 1.6. I can reproduce the behavior on demand by putting it on a timer or attaching it to a button click. As you can see from the posted code above, it's not something very complicated.

  12. #12
    Champion
    Join Date
    Jun 2011
    Posts
    561

    Default

    Quote Originally Posted by jca View Post
    I haven't really noticed whether frame creation has slowed or not. I'm using a recycling mechanism for any types of dynamically created frames so I'm not really noticing that.

    I'm seeing lag any time a significant number of frames are arranged. Take buff boxes for example, which is what the preceding example concerns. Say a player has 20 buffs. Whenever buffs are added/removed, I'm sorting the list of buffs based on a priority system, then I need to arrange the frames for each buffs in that order. Whenever I'm arranging those, for example, 20 buff frames, there's a very very noticable lag or stutter since 1.6. I can reproduce the behavior on demand by putting it on a timer or attaching it to a button click. As you can see from the posted code above, it's not something very complicated.
    It could be the same issue but not 100% sure. Cause at the creation of the grid Widget in my addon nkAdvisor my code will create something like 1000 frames (actually 2/3 frames and 1/3 texts). That wasn't really that much of a problem in 1.5. However with 1.6 you notice it takes a second or two.

    However if you're already experiencing the problem with 20 boxes it might still be something different.

    Cheers
    N.
    Last edited by Naifu; 11-18-2011 at 07:21 AM.

  13. #13
    jca
    jca is offline
    Rift Chaser
    Join Date
    Mar 2011
    Posts
    314

    Default

    Here's an extremely simple and easily reproduced sample of the problem. The following is a completely self-contained addon. It creates a grid of 80 squares that each contain a label. These squares need to be arranged in a grid and the label text changed whenever they're updated. I've put the update code on a timer that fires twice a second. Now, run this code and start moving forward. There will be a hitch/stutter every half second when the layout is done.

    Re-arranging 80 frames twice a second might seem excessive, but when you consider a full raid, each toon with multiple buffs/debuffs, constantly changing, it's not at all unrealistic that an addon would need to do layout work of this magnitude.

    I don't have a way to run a 1.5 environment now, but I guarantee that this code will run smoothly in 1.5. In 1.6 it doesn't.

    Code:
    local addonInfo = ...
    local context = UI.CreateContext("test")
    local boxes = {}
    
    local x = 100; local y = 100 -- position of first box
    
    local function _layoutBox(box)
    	box:SetWidth(48)
    	box:SetHeight(48)
    	box.text:SetPoint("CENTER", box, "CENTER")	
    end
    
    local function _updateBox(box)
    	local i = box.counter
    	i = i + 1
    	box.text:SetText(tostring(i))
    	
    	if (i >= #boxes) then i = 0 end
    	box.counter = i
    end
    
    local function _updateBoxes()
    	for _, box in ipairs(boxes) do
    		_updateBox(box)
    	end
    end
    
    local function _layoutBoxes()
    	-- arranges the boxes in a grid which moves 10 pixels each update
    	x = x + 20
    	y = y + 20
    	
    	local anchor
    	
    	for i, box in ipairs(boxes) do
    		if (i == 1) then
    			box:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x, y)
    		elseif (i == 21) then
    			box:SetPoint("TOPLEFT", boxes[1], "BOTTOMLEFT")
    		elseif (i == 41) then
    			box:SetPoint("TOPLEFT", boxes[21], "BOTTOMLEFT")
    		elseif (i == 61) then
    			box:SetPoint("TOPLEFT", boxes[41], "BOTTOMLEFT")
    		else
    			box:SetPoint("LEFTCENTER", anchor, "RIGHTCENTER")
    		end
    		
    		anchor = box
    	end
    		
    	if (x > 800) then x = 100 end
    	if (y > 800) then y = 100 end
    end
    
    local updateThrottle = 1/2 
    local lastUpdate = 0
    
    local function _onUpdate()
    	local now = Inspect.Time.Frame()
    	local elapsed = now - lastUpdate
    	
    	if (elapsed >= updateThrottle) then
    		lastUpdate = now
    		
    		_layoutBoxes()
    		_updateBoxes()
    	end
    end
    
    local function _loaded()
    	-- create a series of square frames, each containing a text frame and a text background frame
    	local box
    	for i=1,80,1 do
    		box = UI.CreateFrame("Frame", "TestFrame" .. tostring(i), context)
    		box:SetBackgroundColor(1,1,1,1)
    		
    		box.fg = UI.CreateFrame("Frame", box:GetName() .. ".fg", box)
    		box.fg:SetLayer(1)
    		box.fg:SetBackgroundColor(0,0,0,1)
    		box.fg:SetPoint("TOPLEFT", box, "TOPLEFT", 2, 2)
    		box.fg:SetPoint("BOTTOMRIGHT", box, "BOTTOMRIGHT", -2, -2)
    		
    		box.text = UI.CreateFrame("Text", box:GetName() .. ".text", box)
    		box.text:SetLayer(2)
    		box.text:SetFontColor(0,1,0,1)
    		box.text:SetPoint("CENTER", box, "CENTER")
    		
    		box.counter = i - 1
    		
    		table.insert(boxes, box)
    	end
    	
    	_updateBoxes()
    	_layoutBoxes()
    	
    	table.insert(Event.System.Update.End, {_onUpdate, addonInfo.identifier, "_onUpdate"})
    end
    
    table.insert(Event.Addon.Startup.End, {_loaded, addonInfo.identifier, "_loaded"})

  14. #14
    RIFT Community Ambassador the_real_seebs's Avatar
    Join Date
    Jan 2011
    Posts
    16,859

    Default

    Hey, folks.

    http://www.riftui.com/downloads/info...PerfORate.html

    ^-- can give you information on how much clock time is elapsing during a given hook being run. In particular, has a mechanism for giving you a function you can stash in an event table that will accumulate time and report to you how long it's actually taking to run your code.

    This kind of problem is EXACTLY what it was designed for.
    You can play WoW in any MMO. You don't have to play WoW in RIFT. Oh, and no, RIFT is not a WoW clone. Not having fun any more? Learn to play, noob! I don't speak for Riftui, but I moderate stuff there. Just came back? Welcome back! Here's what's changed. (Updated for 2.5!)

  15.   This is the last Rift Team post in this thread.   #15
    Rift Team
    Join Date
    Oct 2010
    Posts
    927

    Default

    Quote Originally Posted by jca View Post
    Here's an extremely simple and easily reproduced sample of the problem.
    Aha, just what I needed

    So, some performance numbers, and note that these are all pretty arbitrary (it's my development box running development code with whatever detail settings I happen to be using):

    First, I changed the update to every frame, not every 1/2 second, just to get a better sense of the impact.

    The current code uses about 80% of the CPU to do 10 updates per second.

    There are some pretty significant improvements that can be made, and those will be showing up in a hotpatch. Those bring it down to 45% of the CPU to do 35 updates per second.

    That said, your code is rather inefficient in the first place. Take this function, for example:

    Code:
    local function _oldlayoutBoxes()
    	-- arranges the boxes in a grid which moves 10 pixels each update
    	x = x + 20
    	y = y + 20
    	
    	local anchor
    	
    	for i, box in ipairs(boxes) do
    		if (i == 1) then
    			box:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x, y)
    		elseif (i == 21) then
    			box:SetPoint("TOPLEFT", boxes[1], "BOTTOMLEFT")
    		elseif (i == 41) then
    			box:SetPoint("TOPLEFT", boxes[21], "BOTTOMLEFT")
    		elseif (i == 61) then
    			box:SetPoint("TOPLEFT", boxes[41], "BOTTOMLEFT")
    		else
    			box:SetPoint("LEFTCENTER", anchor, "RIGHTCENTER")
    		end
    		
    		anchor = box
    	end
    		
    	if (x > 800) then x = 100 end
    	if (y > 800) then y = 100 end
    end
    :SetPoint() creates a persistent constraint - there's no reason to call it over and over with the same parameters. Renaming this function to "_createLayout", calling it in _loaded(), and creating a new function "_layoutBoxes" like this:

    Code:
    local function _layoutBoxes()
    	-- arranges the boxes in a grid which moves 10 pixels each update
    	x = x + 20
    	y = y + 20
    	
    	local anchor
    	
    	boxes[1]:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x, y)
    		
    	if (x > 800) then x = 100 end
    	if (y > 800) then y = 100 end
    end
    results in 20% CPU usage for 45 updates per second.

    At this point, the largest CPU drain is setting the text - recalculating the text bounds can be a bit slow. For comparison, disabling the :SetText() in _updateBox brings it all the way down to 8% CPU usage for 45 updates per second, although by now the addon system is eating 4% of the CPU as well. We'll probably be improving that in the future, but most of the text shown likely doesn't change every frame in a realistic situation.

    Anyway, we'll hopefully get our performance issues taken care of soon, and there's some suggestions on how to speed things up a lot.

+ Reply to Thread
Page 1 of 2 1 2 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts