Hey,
So recently I began rewriting a keyboard/mouse input system of mine due to discovering how inefficient it was, anyway, during this process, began to ensure my methods where faster than the default methods - or at least as quick...
First of all, I loaded / inserted the benchmarking script into my project.
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/benchmark/rdoc/Benchmark.html
Secondly, I made a small test like so...
module Input def self.blankmethod(aarrgg) endendLOOPS = 500_000Benchmark.bm do |x| p "Input.test = " x.report { LOOPS.times { Input.press?:)UP) } } p "Blank.test = " x.report { LOOPS.times { Input.blankmethod:)UP) } } endThird - I hit play and viewed the results...
( user ) , ( system ) , ( total ) , ( real )Input.test = 0.109000 , 0.000000 , 0.109000 , 0.111007Blank.test = 0.140000 , 0.000000 , 0.140000 , 0.133007Now as you can see, to call a Input.blank method 500,000 times takes longer than calling Input.press?(buttonsymbol) (the same amount of times) does.
I mean, how is that possible?
How is one supposed to write a script that is just as / more efficient when I cannot even create a blank method that processes quicker? Anyone have any ideas on that?
So far I have managed to get my Input.update method running around 3-4x faster than normal input.update, but imo, the press and trigger methods are just as/more important in the long term goal of efficiency.
I guess I am just not able to comprehend how the press method runs faster than a blank method does. I mean, if press? was just calling pure C/C++ code then yea, i could understand, but if that was the case why is the input.update method dramatically inefficient. ?
Input.press?(:UP) faster than blank method???
● ARCHIVED · READ-ONLY
-
-
Repeat the test many times.
-
I don't know for sure, but as far as I remember that some specific methods are handled different from the regular ones. For example Array#[] is not executed as a regular method by default and therefore a bit faster in execution (does not work for subclasses of Array when I remember correctly).
I could neither find the source of that statement again nor am I aware how the ruby interpreter processes method calls in the first place, so if anyone knows about the details I'd love to hear them. :) -
The fact that Input::press? is written in C is alone enough to make it faster than a blank Ruby method, so there's that.
EDIT: In fat, it's wise to remember that almost all methods like Input::press?, Audio, Graphics, etc are written in C, then exposed to Ruby via Ruby's C API. Ruby will never be as fast as the language it's written in, because of interpretation overhead, internal type checking, extra pointer arithmetic (from VALUEs in C to Objects in Ruby) and many more things. Unless you write the method in C and expose it to Ruby, it won't be as fast.
That being said, most things, when called through a dll, are faster than the de facto methods Enterbrain has provided us, apparently because they only exercised minimal optimization on all of their code. This is seen when we take a Tilemap and pair it with a Resolution breaker, or an Input script with an FPS delimiter script. -
1. Loop counter is too low.
2. I can't reproduce the result at all. Was this your only try and did you reverse the two tests?
3. Most importantly: you're using benchmark and not benchmark-ips:
Benchmark.ips do |x| x.report('Input.test = ') { Input.press?:)UP) } x.report('Blank.test = ') { Input.blankmethod:)UP) }endCode:Kinda pointless. One big performance gain is to prevent object allocation, but (at least in a legal way) it's unlikely to be able make your own methods (that actually do the same stuff) as fast as the original ones. And ask yourself, does speed actually matter in this case? Drawable objects are another big issue.Calculating ------------------------------------- Input.test = 48149 i/100ms Blank.test = 50717 i/100ms------------------------------------------------- Input.test = 2676286.0 (±2.4%) i/s - 13385422 in 5.004277s Blank.test = 2861435.4 (±2.9%) i/s - 14302194 in 5.002288s
In RGSS3 Ruby is compiled to bytecode. To get an textual representation of it, see the disassemble methods. And yes, I know that I've linked to 2.1.3 and not to 1.9.2, because they were undocumented back then, but there isn't much difference - the bytecode and the output may differ, but that's not important now.To read the C implementation of the bytecode instructions, read insns.def. For Array#[] or Hash#[] see opt_aref (line 1876 to 1904). If you scroll up or down, you'll see other optimized instructions. Of course, redefining such methods cancels the performance gain.I could neither find the source of that statement again nor am I aware how the ruby interpreter processes method calls in the first place, so if anyone knows about the details I'd love to hear them. :) -
The fact that Input::press? is written in C is alone enough to make it faster than a blank Ruby method, so there's that.
That doesn't add up. It's not like it's inlined C. You still have the overhead of the ruby method call in both cases. The only difference is that one does nothing, the other does something. Since doing nothing is always faster than doing something, the empty method should be faster.
That said, the Ruby interpreter is not exactly built for performance. The overhead of the loop itself is probably significant in such a contrived performance test. The actual code being called is probably in the noise, i.e., the loop overhead and method call comprise N% of the CPU time, where N is significant. You also need to realize that context switches occur, so perhaps your torrent downloading in the background interrupted the dummy method call a few times, hence the slightly longer run time. All sorts of things can affect performance profiles, especially when you're attempting to measure something that takes so little time.
Contrived tests like these don't mean much of anything, and that method call is never going to be a bottleneck. Measuring performance is non-trivial and nuanced. You need to understand what is going on behind the scenes in order to make sense of the results. -
@Tsukihime
Yep, done that. Each test yields similar results.
@Another Yen
Yes this is quite likely. A lot of ruby methods just trigger some C/C++ code directly. So yea, it would make sense that making a child of array would not be as quick as array as your child class is likely going to be altering methods somewhere (otherwise what is the need for an array child?) and subsequently, ruby code is being added which has to then be interpreted. :)
@FenixFyreX
Yea, that is the only thing I can come up with for how the method is so quick!
Trust me, we dont even want to get into how the dir4 and dir8 methods are EVEN FASTER!...
What I dont get though, is why the update method is so slow. If the Input is all written in C/C++, surely they would have made the update method - the only one that is intended to be called each and every frame - faster than the others?
I mean, my update method is MUCH faster than the default method - if I can make that faster, why not the others..
I have figured that the only way to actually beat the speed is to write up a dll that adds ruby methods into the engine that when called, call some C code directly. - Still a while to go on that one.. I'm at the process of figuring out how to make my dll attach onto the engine and then I will be trying to figure out how to add ruby methods into the engine directly from my dll.
For a Ruby input script though, mine is running faster than those currently tested against (except the default one of course). I am currently downloading as many as I can find to test against. :D
@cremnophobia
I have also tested on a 2,500,000 loop check with the blank method again, taking longer.
Wasn't aware of that Benchmarking util. Gonna get the pieces together and test it out later, thanks for linking it :)
Yea, the Input's speed may not be the biggest of concerns and honestly, it wasnt until I realized how inefficient my old input script was. (took 135 secs for 100,000 update loops - yes, really poorly written). Now I have accepted that I wont be able to beat the input's normal speed without writing up a dll. - Currently processing various ideas on how I could do such a thing with my current level of C++ knowledge but uhh... yea...
@BigEd
Yea, my thinking was also that "doing nothing has to be faster than doing something" but evidently, that is not the case here... (worryingly, the dir4 and dir8 methods are even faster (quite a bit) than the press? method.)
I did test the performance of various loop types as well to ensure that my chosen loop was 'the quickest' - was very close but overall, seemed that N.times was the route to go :p
As far as torrents etc, I have tested this whilst my computer had nothing else running - at all. Just RPG maker and the bare essentials, no media or internet connection or anything like that. the tests all show that doing nothing actually takes more time than doing something :D -
My point was that you're not measuring what you think you're measuring. There is error in every measurement, caused by any number of unpredictable events occurring in your system. If the thing that you are trying to measure is so fast that it comprises a statistically insignificant amount of the total CPU time then you end up measuring the error and really nothing else. You can be confident in the fact that doing nothing is faster than doing something. That is a given. Now, since I have to guess at what is causing the small discrepancy, I have guessed that it is caused by the reasons that I have previously stated. I could be wrong, I'd have to do more work to convince myself of it. Again though... an empty method call is not slower than a method call which does *anything* other than nothing.@BigEd
Yea, my thinking was also that "doing nothing has to be faster than doing something" but evidently, that is not the case here... (worryingly, the dir4 and dir8 methods are even faster (quite a bit) than the press? method.)
I did test the performance of various loop types as well to ensure that my chosen loop was 'the quickest' - was very close but overall, seemed that N.times was the route to go :p
As far as torrents etc, I have tested this whilst my computer had nothing else running - at all. Just RPG maker and the bare essentials, no media or internet connection or anything like that. the tests all show that doing nothing actually takes more time than doing something :D -
Yea I see what your getting at now. Never really thought of it like that but i guess its a pretty accurate way to see things. :)
I guess I will just have to be satisfied with 'the most efficient ruby input script'. Really, my methods are showing that 'overall', my input method is more efficient (but i assume thats just from the way it was tested (doing input.update and input.press checks in the same method and testing those speeds) ).. Really though, yea, the amount of cpu thats being used is nothing compared to some other things.
I just want to make sure my input system is as efficient as possible as its one of the few systems I use in all my projects. :D