• 0

    posted a message on Self-generating code anyone?
    Quote from Mist »

    I'm curious about your unit frames addon, and is the code optimization solely responsible for the 100x improvement? Or agUF is doing more stuff (aggro, range check, etc)?

    I have to get a little wordy here, sorry. :)
    I think there's only one feature missing from ag_unitframes, namely updates on targettarget and focustarget. Unfortunately, WoW doesn't generate events for these units, so AddOns would need to update them in OnUpdate code. As the MT I don't really care about targettarget health/mana being updated, I mainly use the targettarget frame so I can look at my pretty face in the portrait. ;) So that's the only difference I'm aware of: targettarget and focustarget only update if they change, while agUF updates these twice a second.

    ag_unitframes has quite a few more features than my mod, all of which I have disabled. I never use this Banzai stuff, or range checking, or debuff coloring/sorting etc, or cast bars. My mod also doesn't have a GUI to set options, I use a hard coded table at the moment. I believe agUF uses DogTag for labels, I don't have that either. But all these features shouldn't affect the performance (in theory), since I disabled them in agUF.

    One of the things that has an impact on performance is the event code. My event code looks like this:
    function(frame, event, unitid)
    	local callback = eventreg[unitid]
    	if callback then
    		callback()
    	end
    end

    which is used for all the UNIT_* events like UNIT_HEALTH. This approach is similar to PerfectRaid.
    agUF uses Ace2 for events, and pretty much every unitframe registers for all event. So in a raid situation about 30 OnEvent handlers are called every time an event like UNIT_HEALTH fires, and every handler performs a check like [font=Verdana]if unit~=self.unit then return end[/font].

    Another big difference: I think the only place where I use tables is for auras. The buttons, textures, counts etc that display buffs/debuffs are stored in arrays. But that's it for tables. I don't use globals, I don't use self:foo(...) or even manabar:SetValue(...), but I store everything (including function references) in upvalues. The latter would become manabar_SetValue(manabar,...) in my code. A little hackish (because it relies on the fact that manabar.SetValue is constant), but faster.
    agUF on the other hand uses an OO-approach entirely based on tables, like the examples in PIL. From a performance point of view, the big disadvantage of that approach is that there is only one function closure per class, not per object. So all object data has to go into a table, you can't use upvalues. Whenever you need access to object data, you have to do a table lookup, which is slower than upvalue access. I'm not sure how much slower exactly, but upvalue lookup is O(1), and table lookups are probably in O(log n), maybe even O(n).
    Posted in: Lua Code Discussion
  • 0

    posted a message on Self-generating code anyone?
    Well, by "optimal code" I meant code with the smallest possible execution time. For example, using loadstring() enables you to perform table lookups for options outside of the actual code that depends on it. So you can not only skip the table lookup, but even the if-then-end construct. This optimization, of course, would only make sense if the options don't change often, as you'd have to recreate the code if they do.

    And as I said, I'd use it mostly for inner loops, as these are the 10% your rule is referring to. I know this stuff is not for everyone, and a lot of programmers don't really care about performance that much. It's about the difference between working code, fast code and optimal code. Most programmers are happy with working code, and leave it at that. Some strive for fast code, and put in some effort to profile and optimize their code. The question is when to stop, as code optimization always seems to be a trade-off between speed and code readability.
    Please don't get me wrong, I don't want to be judgemental here, I just happen to like optimizing, it's fun for me to fine-tune code to make it as fast as I possibly can.
    Posted in: Lua Code Discussion
  • 0

    posted a message on Need help with saving settings - started today.
    Quote from Ringleron »

    http://fara.webeddie.com/ui/
    Awesome link, thanks!
    Posted in: Lua Code Discussion
  • 0

    posted a message on Self-generating code anyone?
    Quote from Jerry »

    Tifi, the example you give has little to do with code generation. It's an code transformation often used in "optimising" compilers, and is called CSE. Lua's compiler does not do CSE, so it's up to the code author to use it where it is useful. The tricky part about CSE is to determine what is a constant and what is not. In your example alone, it's impossible to decide if the two are equivalent or not.

    You're right, it was not a good example. Maybe this one is better: I wrote a class Function that handles the code generation stuff. The example above, using the Function class, would look like this:
    bla = Function('') -- bla is a function with no arguments
    local code = bla.code
    code(' dosomething()')
    code(' dosomethingelse()', self.db.some.expensive.and.slow.table.lookups.optionset) -- insert this line iff optionset is non-nil
    
    bla = bla.using({
     dosomething = dosomething,
     dosomethingelse = dosomethingelse,
    })
    --now bla is a real Lua function


    Here's another example for Function:
    local verbose = print and type(print)=='function'
    
    local f = Function('x,y')
    local code = f.code
    code('local a=x*15')
    code('print("a is "..a)',verbose)
    code('return a+y+UnitHealth("player")+z')
    
    f = f.using{UnitHealth=UnitHealth, z=3, print=print}
    
    print(assert(f(1,2)))

    I always want as few table lookups as possible, and I store everything I can in upvalues. So what Function.using does is iterate through everything in the passed table, and declare it as an upvalue. You could call the argument table a pseudo environment. Function.using also sets a real (empty) function environment, and if the generated function uses a global, it prints an error message (using metatable magic on the environment).

    Here's the Function class code. No guarantees though, the code was never really tested, and is used only by myself.
    local function Function(args)
    	local o = {}
    	local data = {}
    	local env = {}
    
    	args = args or ""
    
    	local function code(line,...)
    		for i=1,select('#',...) do
    			if not select(i,...) then 
    				return
    			end
    		end
    		table.insert(data,line)
    	end
    
    
    	local function __index(t,k)
    		ChatFrame1:AddMessage('undefined upvalue '..tostring(k), 1, 0.3, 0.3)
    		local v = _G[k]
    		if v then
    			env[k] = v
    			return v
    		end
    	end
    	setmetatable(env, {__index=__index})
    
    	local function asstring()
    		local joined = strjoin('\n',unpack(data))
    		data = {joined}
    		return string.format('return function(%s) %s end',args,joined)
    	end
    
    	local function using(fenv)
    		local code = asstring()
    
    		local defs = {'return function(fenv)'}
    		for k,_ in pairs(fenv) do
    			if type(k) == "string" then
    				table.insert(defs, string.format(' local %s = fenv.%s',k,k))
    			else
    				ChatFrame1:AddMessage('error: not a string '..tostring(k), 1, 0.3, 0.3)
    			end
    		end
    		table.insert(defs, code)
    		table.insert(defs, 'end')
    
    		local joined = strjoin('\n',unpack(defs))
    
    		local result = assert(loadstring(joined))
    		if not result then
    			return
    		end
    		local func = result()
    		func = func(fenv)
    		setfenv(func, env)
    		return func
    	end
    
    	o.code = code
    	o.asstring = asstring
    	o.using = using
    
    	return o
    end


    Jerry, I agree on the pros and cons. Most of the time, loadstring is the only feasible way to get optimal code. One of the drawbacks is that it's harder to debug. For example if the generated code throws an error, the line number you get is relative to the string used to generate the code.
    Also, it's optimization by hand. As you pointed out, there's no way to determine if and when the value of self.db.some.expensive.and.slow.table.lookups.optionset changes. The programmer has to know this, and write the code accordingly.

    It's a tool, and I certainly wouldn't use it for everything, but I like it for inner loops (OnUpdate code for example), even for event handlers. For me, the fact that you can write optimal code outweights the disadvantages.
    Posted in: Lua Code Discussion
  • 0

    posted a message on Self-generating code anyone?
    Are there addons out there using this approach? I wrote two addons, a unitframe addon and one that displays cooldown bars. Both outperform comparable addons by a large margin, ag_unitframes for example uses about 100 times more CPU than my addon in boss fights.

    If you don't know what I'm talking about, consider this:
    function bla()
     dosomething()
     if self.db.some.expensive.and.slow.table.lookups.optionset then
     dosomethingelse()
     end
    end

    versus
    if self.db.some.expensive.and.slow.table.lookups.optionset then
     function bla()
     dosomething()
     dosomethingelse()
     end
    else
     function bla()
     dosomething()
     end
    end

    A more versatile approach is to generate a string, and feed it to loadstring(), which is what I'm using.
    Posted in: Lua Code Discussion
  • 0

    posted a message on Aura Time Left Reporting Library
    Quote from Javek »

    I'm interested in making an addon that watches raid members auras and reports the time left on them.
    Why? Is it that hard for your raid members to not fuck up rebuffing? :)
    Posted in: Libraries
  • 0

    posted a message on SpecialEvents-Aura-2.0 bug (weapon enchants)
    Nice, that was fast. Thank you. :)
    Posted in: Libraries
  • 0

    posted a message on Quartz: Modular Casting Bar
    Quote from Ragnor »

    Chain casting works fine because you can start another cast as soon as the GCD is over. You should base your chain cast start on the GCD spark, not when the cast bar goes into/gets to the latency section of the bar.
    That is not true if the cast time is longer than 2.0s. Casting too early prevents your client from sending another cast request until it recieves a fail message, and time spent waiting is wasted dps/hps. This becomes worse with higher latency. Timing > spamming just like before 2.3, but since 2.3 timing works a lot better for casted spells (where the cast time is longer than a GCD).
    Posted in: General AddOns
  • 0

    posted a message on SpecialEvents-Aura-2.0 bug (weapon enchants)
    SpecialEvents_PlayerItemBuffRefreshed is fired every time PlayerItemBuffScan() is called, because of faulty logic in the code, line 723 and below:
    		elseif mainhandItemBuff and oldMainhandTimeLeft < mainhandTimeLeft + 1 then -- duration refreshed

    needs to be changed to
    		elseif mainhandItemBuff and oldMainhandTimeLeft + 1 < mainhandTimeLeft then -- duration refreshed

    Let's say oldMainhandTimeLeft==10.2 and mainhandTimeLeft==10.0, in this case the event should obviously not be fired. But oldMainhandTimeLeft == 10.2 < 11.0 == mainhandTimeLeft + 1.
    Same thing for the offhand code.
    Posted in: Libraries
  • 0

    posted a message on Omen - Bug Reports and Suggestions
    Quote from Pusikas »

    Wouldn't it do to just use another Frontend for the Threat-Library? Or is it the Threat-Library that uses SpecialEvents-Aura-2.0?

    They both use SpecialEvents-Aura-2.0. Threat-1.0 uses it for threat related stuff like Sunder debuff, Salvation buff. Omen uses it.... well... because the player might be a Paladin and might be a tank (Righteous Fury).

    Some CPU data from two Lurker tries last night. I use a home baked Profiler that only records CPU times while in-fight, i run everything but the Ace2-internals (AceEvent etc) disembedded. As always it's hard to tell which AddOn uses how much from Ace2.
    6241ms 6030ms Ace2
    2365ms 1831ms Omen
    1376ms 1254ms SpecialEvents-Aura-2.0
    0643ms 0629ms Threat-1.0
    Posted in: Raid AddOns
  • 0

    posted a message on Omen - Bug Reports and Suggestions
    I like Omen in general, but the one thing I really hate about it is that it uses SpecialEvents-Aura-2.0. In pretty much every thread about Omen I read that KTM is oh-so-slow and Omen was designed to be much lighter on the CPU. And still, it uses one of the worst libraries ever when it comes to CPU, SpecialEvents-Aura-2.0. The bad thing about this library is that it has a lot of features, none of which can be turned off. Some of these features might be useful to some AddOns, but they all take CPU.

    For example, the library keeps track of every buff/debuff on every raid member. Whenever some random rogue gains Mungoose, all his buffs are reiterated multiple times, lots of strings are constructed, because the library wants to find out what exactly happened: Was a buff gained or lost, was a debuff gained or lost? The thing is: No one cares. Really. No one.
    Now if a boss debuffs the entire raid, the library does the exact same thing, but this time for every member of the raid, maybe even for pets. About 400 lines of code (the function AuraScan) are executed 25+ times. Again, no one cares.

    Now I know that Threat-1.0 needs to keep track of certain player buffs/target debuffs, like Salvation or Sunder. But please use another way than SpecialEvents-Aura-2.0 if you really want Omen to be as light on the CPU as promised.
    Posted in: Raid AddOns
  • 0

    posted a message on Quartz: Modular Casting Bar
    Quote from Seerah »

    The latency bar shows what you have at the time the spell *starts casting*. Latency is always changing, so even with the latency bar, you're still just guessing and going by feel - it's just easier now.

    Before patch 2.3 that was true, but this changed with the improved chain casting mechanics since that patch.
    Quartz Latency shows the time between UNIT_SPELLCAST_SENT and UNIT_SPELLCAST_[CHANNEL_]START as the lag, which is the time between your client sending the "start casting now" message and recieving the message from the server that the spell started casting. That was a good thing before the patch, because UNIT_SPELLCAST_SENT time plus the time it took that message to the server really marked the time when the cast started on the server. This is no longer the case in 2.3, as clients are now allowed to send UNIT_SPELLCAST_SENT early for chain casting. After some testing, I assume the server recognizes a chain-cast if it recieves a UNIT_SPELLCAST_SENT 500ms or less before the previous cast ends (server-side).
    So even with 0ms of lag, you are now allowed to cast the next spell 500ms before the last spell ends, but Quartz would display 500ms of lag.

    Quartz Latency should take into account the earliest possible start for a cast. For example Imp Frostbolt chain casting (2.5s cast time):
    0.000s UNIT_SPELLCAST_SENT
    0.210s UNIT_SPELLCAST_START
    2.320s UNIT_SPELLCAST_SENT
    2.690s UNIT_SPELLCAST_START

    In this example we cast the second FB at 2.320s, an receive the message from the server at 2.690s, so Quartz will display 370ms of lag for the second cast. However, we sent the first FB at 0.000s, so it's just not possible at all that the second FB starts before 2.500s! That means our maximum lag is 2.690s-2.500s = 190ms. Quartz Latency should display this number instead.
    Posted in: General AddOns
  • 0

    posted a message on Is there an addon that displays incoming heals only(!) on myself?
    Thank you so much! That's exactly what I was looking for.
    Quote from wesleydungan »

    *snip*
    Remember, for VisualHeal (or other spell communication addons) to be effective, everyone needs to be using addons based on compatible spell communication libraries.

    Most of our healer use Grid or Healbot, so maybe I'll write an adapter to HealComm. Or just tell them to install VisualHeal. :) Anyway, thanks again!
    Posted in: Unit Frames
  • 0

    posted a message on Is there an addon that displays incoming heals only(!) on myself?
    As a tank I'd *love* to have an addon that displays heals cast on myself, for encounters like Tidewalker where I can actually die, and have to manage health potion/stone/Last Stand CDs. I imagine it for example as a health bar for myself, where incoming heals are bars added to the end of my current health, possibly even colored by cast time left. Another possibility would be the cast bars of healers healing me.

    I considered writing one myself, but gave up when I realized that the caster target is undetectable in vanilla WoW. Yesterday I saw a healer transmitting data with CastCommLib and IncomingHealsLib (or sth), so before I start coding I thought I'd ask here if there already is an addon out there for this purpose.
    I found a lot of addons that sort-of display this information, but all of them seem to be designed for healers, and display incoming heals on all raid members.
    Posted in: Unit Frames
  • 0

    posted a message on wowace.py - lightweight, cross-platform addon updater
    Maybe I'm not the only one who is tired of fixing OptionalDeps every single time after using the updater. I posted messages in the Aloft and Threat-1.0 threads, but the authors don't bother fixing one single line in the toc. Too much work I guess. Whatever.

    Here's a python script to add missing dependencies to .toc files. Save it to e.g. fix.py (it has to be in the same dir as wowace.py). Then add the broken addons you're using and their missing dependencies. I run this script after wowace.py.

    import os
    import re
    from wowace import TocFile, default_wowdir
    
    missing = {
      "Aloft": ["GratuityLib"],
      "FuBar_MoneyFu": ["AbacusLib"],
      "Threat-1.0": ["SpecialEventsEmbed"], 
      "DrDamage": ["GratuityLib","SpecialEventsEmbed"], 
      "ErrorMonster": ["SinkLib"],
    }
    
    for name, deps in missing.iteritems():
      try:
        toc = TocFile(os.path.join(default_wowdir, name, name+'.toc'))
    
      except IOError:
        print 'addon',name,'not found'
      else:
        if toc.meta.has_key("OptionalDeps"):
          optdeps = [x.strip() for x in toc.meta["OptionalDeps"].split(',')]
          for dep in deps:
            if optdeps.count(dep) == 0:
              print "fixing dependency",dep,"for",name
              toc.meta["OptionalDeps"] += ", "+dep
              toc.dirty = True
            else:
              print name,"fixed",dep
    
          if toc.dirty:
            print toc.meta["OptionalDeps"]
          toc.save()
    Posted in: Updaters
  • To post a comment, please or register a new account.