| 05-25-2006, 12:21 PM | #1 | ||||||
Is the speed difference between direct gamecache native + return bug exploitters and tables worth enough to use the gamecache natives and add non-sense to your code? Common for both tests:function H2I takes handle h returns integer return h return 0 endfunction // ... function testthread takes nothing returns nothing local integer j=1 local location loc loop exitwhen (j>100) call SetLoc("a",I2S(j),Location(0,j*100)) set j=j+1 endloop set j=1 loop exitwhen (j>100) set loc= GetLoc( "a",I2S(j) ) call RemoveLocation(loc) set j=j+1 endloop endfunction function test takes nothing returns nothing local integer j=1 loop exitwhen (j>2500) call ExecuteFunc("testthread") set j=j+1 endloop endfunction //=========================================================================== function InitTrig_bench_Copy takes nothing returns nothing local trigger t=CreateTrigger() set udg_G=InitGameCache("CasterSystem.vx") call TriggerAddAction(t, function test) call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC) endfunction comparission:
29.4 / 27.66 equals 1.062 . So tables are 6.2% slower than natives+return bug all right 200*2500 equals 500000 that's the amount of set / get operations. If we devide things we get that in one get / set combo tables take 0.00000696 more seconds than direct native+return bug usage What to do now? That's a good question Edit: I am using gamecache variable directly on the table function simulation, considering that either the optimizer or someone with a couple of seconds and control on the map's initialization order can get rid of functions like CSCache() and use a well initialized variable instead |
| 05-25-2006, 01:20 PM | #2 |
Proves once again that all this "its more efficient" is not necessarily true. To me, saving 0.00000696 seconds does not make up for the extra time it would take for people to understand the nonsense that the code would become when using natives. |
| 05-25-2006, 02:20 PM | #3 |
well that's as relative as it can get |
| 05-25-2006, 02:51 PM | #4 | |
Quote:
See now this is what I say all the time about BJ calls. Yes, many of them are very redundant, but what does it matter if you can call them a million times with negligible speed difference? I'd rather use things like UnitRemoveBuffBJ even though it's just a wrapper because when I read the code I know I'm dealing with a buff. I treat these table calls the same way. So what if it takes an extra 7 microseconds for a table call? If tables are slowing down your map, there is something wrong with your algorithm. While I'm here, can someone please post the speed comparisons for BJ calls? Something like using ConvertedPlayer instead of Player, or any other straight wrapper function? I'm so sick of this "BJ IS BAD" movement. It's the most irritating thing about asking for help around here. The last time a guy wanted help speeding up his code, everyone just told him to get rid of his BJ calls when that wasn't even the problem. I was the only one who actually bothered to look at the code and fix the algorithm; BJ calls had nothing to do with it. This happens all the time on this forum, and a lot of new mapmakers get discouraged with how their code is treated here. People need to take a more realistic standpoint on things like this. |
| 05-25-2006, 03:36 PM | #5 | ||||||||||||||
All right , the common things are still there Benchmark:
all right 25.34 - 21.1 seconds equals 4.24 seconds . That by 1250000 is 0.000003392 seconds Of course, they don't make things slower than when you use tables. But: Did you notice that there is no reason to use a BJ swapped function at all? In JASS SetUnitAbilityLevel has more sense to have the unit argument before the others cause the name of the function is SetUnitAbilityLevel not SetAbilityLevelForUnit in which using the Swapped version would have more sense. Tables do much more than just swapping the natives or renaming them, they call the CSCache() function, they handle return bug exploiting and they have auto flushing. So tables do have an use unlike BJ swapped functions which don't have any. BJ functions won't make your code easier to read, it will make your code look retarded, they will not save you time either because you will have to write extra BJ or Swapped. Thus their increment of execution time is really worthless. Tables vs. BJ functions:
Finally you should not really consider the speed difference on the number of seconds they add cause that tends to make things look harmless. Instead you should watch percentages. in this case 25.34 / 24.1 equals 1.051 so BJ are 5.1 % slower. Is what bj functions do worth the extra 5.1% of time wasted? |
| 05-25-2006, 03:50 PM | #6 |
Oh man, I think you may have annoyed Vex, do we tremble in fear or what(I dunno if we should, its not like this is Tim. or something). Anyway, BJs probably don't make that much of a difference, but I still do find, as vex mentioned, that the argument list tends to be more intuitive in the natives. As for Tables, tables > all. Mainly cause of lameass shit with Attachables(attaching shit to reused handles is so evil, imagine it with a unit, you think you are done so you flush it, but you flush anything else stored on that unit....dumbass). |
| 05-25-2006, 04:26 PM | #7 |
Anyways Vuen, there are times where not even what tables do are worth the performance hit. Take a look at the caster system's movement core I did not use tables there and the reason is that direct gamecache usage ended with more framerate when seeing the missiles. |
| 05-25-2006, 04:32 PM | #8 | |
Quote:
Yes, I agree that BJ wrappers are useless. You're completely missing the point of what I'm saying. What I'm saying is that people around here don't understand how insignificant a BJ call is; they think that calling a BJ is what slows maps to a crawl, and they pass this bad advice on to others as though it's the one thing that's breaking everyone's map. They don't even care how algorithms work; they just care how many BJ functions they call. People ask for help on this forum, and instead of being helped, they get told to "clean out their BJs" when for 99.99999% of applications it makes no difference whatsoever. My point is that everyone around here treats BJ calls as the most critical factor in the speed of an algorithm, when really it's by orders of magnitude the LEAST important part. Yes, in something computationally intensive like the caster system, BJ calls can make a difference. But for what nearly everyone brings to this forum in search of help, cleaning out BJs is about the most useless thing they can do to improve their algorithms. |
| 05-25-2006, 04:42 PM | #9 | |
Quote:
Well besides of the swapped ones there are other BJ functions. Some BJ functions cause memory leaks because they not set stuff to null. Some help you fall in the leak world. Some add operations that are not needed. I wouldn't say that they are the most critical factor, but I don't remember anyone here saying so. You'd have to link to them Fact is that getting rid of them is a kinda good optimization that is really easy to make so I would recommend doing so as the first step when entering to JASS, it is a matter of habits. They have to start by getting rid of them. It is also good for them because that lets them understand more about natives and common.j . It is a great first step when optimizing your map, that was the first thing I done to mine after joining the JASS club and it did affect map speed overall. Anyways this is what I myself tend to say Recipe to make your map faster in order of priority - Remove memory leaks - Remove calls to blizzard.j functions that leak. - Optimize your code more, beware of unnecesary operations, get rid of loops when you can use hash tables. Replace calls to bj functions that swap arguments. - Use the optimizer to make your function / variable names shorter. |
| 05-25-2006, 04:42 PM | #10 | |
Quote:
But unless you're moving a hundred projectiles around and calculating collision for them like I did, there's no reason to drop game cache for globals. |
| 05-25-2006, 05:11 PM | #11 | |
Quote:
Your "recipe" is missing the most important step, which is whether you can rewrite the algorithm to reduce unnecessary calls to very processor intensive functions. For example, suppose you set up a custom missile system. But instead of storing the active missiles in a group, you simply request a "Units of Type Missile in Playable Map Area" to see what missiles are currently in motion. Of course you diligently clean up the group leak, but requesting such a group every, say, 0.02 seconds slows the map to a crawl. This could trivially be replaced with a cached group, which you add the missiles to when spells get cast. Seems like an obvious speed drain, doesn't it? Well this is exactly what happened in this thread, and nobody noticed, because once the leaks were fixed everyone immediately dismissed it; they simply blamed it on BJ calls: http://wc3campaigns.net/showthread.php?t=82336&page=5 5 pages of various people telling him to uselessly convert his triggers to JASS. Had anyone actually looked at his code, they would have immediately seen this obvious mistake; instead the reaction from the forum was "Clean your BJs, or we can't help you." After all this mess, turns out all he needed to do was change exactly 4 lines of GUI to solve all his problems. The most important part your recipe is missing is actually looking at whether the algorithm can be improved. I consider this to be far more important than even memory leaks. Here a simple cached unit group improved the speed of his algorithm more than fixing a million BJ calls per second would have. This is the part that people need to be aware of; going around barking at newbies to "fix their BJs" is both irritating and pointless, and it makes this forum a very bad place to get help. |
| 05-25-2006, 05:12 PM | #12 |
It all comes down to personal preference. Tables effectively are just native game cache functions dressed up. Not everyone needs, or wants, that. Claiming that the natives add 'non-sense' to code, that's an opinion. I'd rather keep the 'non-sense' as performance wise it is better and in my opinion is still extremely readable. As for using globals over game cache there is one simple reason: performance. Will everyone notice it? Of course not. But the fact remains that it is still there. And globals versus game cache isn't black versus white; you can integrate them together. |
| 05-25-2006, 05:18 PM | #13 |
natives don't add nonsense to code, return bug exploiters do. game cache will always be structured in tables and globals will not, even if you used some thing to make globals behave in some kind of structure you would be limited and have to use at least 2 of them one for integers and one for strings, later you'll have to deal with the little 8192 array limit |
| 05-25-2006, 05:22 PM | #14 | |
Quote:
Read it again. It is third step the one about unnecesary operations. It is not the most important step though, it is after leaks, unless your algorythm is really terribly wrong it won't hit performance as much as memory leaks. In that link the algorythm problems were obvious for example. edit: And you didn't really fix it you just saved one of the issues but it still has like 50 problems, err, where was I back then? those guys need some kicking also that trigger is so wrong. But after solving the mistakes there, if it stays in GUI it will still use BJ functions and still be executed each 0.02 BJ functions will still matter. Edit: The lag there was so excessive because of wrongly coded GetUnitsOfTypeIdAll BJ function the devil:function GetUnitsOfTypeIdAll takes integer unitid returns group local group result = CreateGroup() local group g = CreateGroup() local integer index set index = 0 loop set bj_groupEnumTypeId = unitid call GroupClear(g) call GroupEnumUnitsOfPlayer(g, Player(index), filterGetUnitsOfTypeIdAll) call GroupAddGroup(g, result) set index = index + 1 exitwhen index == bj_MAX_PLAYER_SLOTS endloop call DestroyGroup(g) return result endfunction Really, if he replaced it with something like native call GroupEnumUnitsOfType(group,UnitId2String(rawcode),null) The algorythm would have stayed the same and that would have been a similar speed boost than the one you accomplished. Also you optimized the code by getting rid of an evil BJ function (Irony) There are still many algorythm - related optimizations in that trigger. Wonder if that guy is still interested in optimizing it |
| 05-25-2006, 05:31 PM | #15 |
As I said before, integration of globals and game cache. I use the return bug to get a integer 'key' for a unit from the game cache. Then I can access numerous globals for a multitude of stored information, at a faster speed than trying to retrieve everything from game cache. I still use game cache; just as minimal as possible. I could use the unit's user data for the key storage as well; but personally I prefer to use this for other applications. |
