I disagree Lemur, rule 2 should rather only be ignored in exceptional circumstances.
There a frequent cases where clarity and simplicity can be the cost of performance optimization.
In your example one way is blazingly obvious better than the other.
Of course should pick the faster version when it also is the more conceptual neat version.
Avoid premature optimizations is a variant of rule 2.
Loop unrolling is an example of an optimization technique that really degrades coding quality. Unrolling a loop may have a significant impact if it's say the inner-most loop of a very hot rutine. P.s. I do not know whether the technique works in Ruby.
but in what other people explained I understand his use.
I agree with you this can be more readable but more useful?
not really, because why modify a code who work perfectly well and who have the merit to be short in lines?
Also your method seem more readable but think also this extend to much the method for nothing.
in script design at least in what I think forged from my own opinion so other scripter please do not bash on my head.
For me it's will be better to have a script who will be short of 1000 but who will do the same jobs and even more better then the big script of almost 3000 lines.
also thinks having all the code in multiple lines and calls like you made for wait_method() will not be more efficient. For admit you made the system doing non useful calculation who can be avoid with Fiber.yield
but like I said I don't played much with Fiber.yield and prefer avoid these method who are still unknown to me
as I said I will more go with Fiber.yield because shorter and do the job
and this not call other method who can slow my script speed.
One motivation behind using a method is so that you don't duplicate your code.
Calling Fiber.yield in multiple places to do basically the same thing, even though it's just one line of code, is still considered duplicate code to me.
It may not be efficient in terms of performance, but it may be more maintainable depending on what your use case is.
If you need every yield call to do something extra, now the decision to duplicate code for performance reasons suddenly becomes annoying.
Premature abstraction is not necessarily a bad thing in RPG Maker, since anyone that might want to do anything with your method (add stuff, remove stuff, change stuff, etc) would have less of a hard time and therefore improved compatibility.
I would rather abstract everything in development and have a separate process that takes the scripts and "compiles" them for production use that would go and replace all of the method calls with the actual code itself.
Calling Fiber.yield alone is not duplication. I would change to wait(1) if the waiting method need to call another method. If not, then I would leave it as is
In my battle system which also use Fiber, my wait method was like this
def method_wait Fiber.yield @anim_cell = @autopose.shift unless @autopose.empty? update_timed_hit if @timed_hit_setup[:timing] > 0 endAnd when you call wait x times
Code:
when SEQUENCE_WAIT; @acts[1].times { method_wait }
I'd like to take this opportunity to quote M.A. Jackson:
While it's true that Fiber.yield will run faster than wait, unless, after extensive testing, you've determined it to be a bottle neck, don't worry about it. Don't even think about performance optimizations until you are absolutely certain you need to. It'll over complicate your code and waste your time.
That being said, in this case, I'd still recommend Fiber.yield over wait(1). As Hudell said, it is more readable, and guaranteed to have no side effects.
There is still the enormous elephant in the room that nobody wants to acknowledge when they speak about coding:
Releasing scripts for public use is different than writing for your own personal projects. People combine hundreds of scripts in their games, you should aim to provide your best performance and usage wise every time because bottle necks are not your main concern anymore, it is not about your script's code alone anymore. it is about the interaction and combination of your code with the code from others.
That being said, the debate wasn't about optimization but about picking the obviously better design choice. There is a big difference between solving problems andnot making problems.
This sounds to me like a case of future coding. I quote from this article: it’s code written in an overly elaborate and general way in order to, the future-coder reasons, preemptively handle use cases that we’ll “probably see in the future”. It's a problem because, more commonly than you'd think, that "probably" is actually an "almost certainly not" in disguise. Not only that, but future-code is usually over abstracted, harder to read, more buggy, and takes longer to write. Future coding is perhaps the most common problem that plagues object oriented programming. In many cases it is actually easier to reactively refactor code than to proactively refactor code, despite the fact that this may seem counter-intuitive. The article explains it better than I can; I'd recommend reading it.
That being said, there are many cases I can think of already where you might want your yield method to do something extra every time. If your designing a system that has such a constraint, go ahead, pull it into a different method. Just don't do it unless you already know such a constraint exists. Additionally, like you said, you shouldn't duplicate code for "performance reasons"; trying to argue that simply doing this:
def method_wait Fiber.yieldenddef another_method method_waitendIs more expensive than inlining, while technically true, is really, really pedantic.
This sounds to me like a case of future coding. I quote from this article: it’s code written in an overly elaborate and general way in order to, the future-coder reasons, preemptively handle use cases that we’ll “probably see in the future”. It's a problem because, more commonly than you'd think, that "probably" is actually an "almost certainly not" in disguise. Not only that, but future-code is usually over abstracted, harder to read, more buggy, and takes longer to write. Future coding is perhaps the most common problem that plagues object oriented programming. In many cases it is actually easier to reactively refactor code than to proactively refactor code, despite the fact that this may seem counter-intuitive. The article explains it better than I can; I'd recommend reading it.
I agree with avoiding future coding in general. As people usually say, when the need arises, THEN you go and refactor. The extent of future coding I do is limited to "can I make as many methods as possible while looking somewhat sane", which doesn't seem to be that much different from cramming everything into a single method or two.
I have had cases where someone asks me to add something to one of my scripts a year after it was originally released, and I found myself re-factoring the code by pulling some methods apart to make it easier to work with. But it's not like my specifications at the time of writing required it, so I didn't write code assuming the specs would change.
I don't see this as a real issue if it's limited to my own scripts, since I just need to update them and then indicate that a newer version of any dependencies may be required.
However, often times I'm adding functionality to someone else's code, either as a compatibility patch or an add-on, and I need to completely overwrite their methods because they assumed no one else would need to touch it. This can be another scripter, or even the default scripts that the RM devs provided (examples include the way enemy drops are handled, battle end processing, damage processing, checking whether a player can move, handling player movement input, etc)
For me, refactoring only as needed may decrease compatibility between scripts depending on what you're refactoring, since a poorly designed "base" script would end up being overwritten by another script, and if multiple scripts need to overwrite it, now you need to introduce specific one-off fixes just to make them all work together. Or of course refactor the base script...but this may require changes to any other script that assumed the base script was written a certain way.
If the performance issue arises as a result of extra method calls (which already is an issue given the nature of aliasing), then I would recommend seeing if there's a way to take this code, which is super abstracted and plagued with future coding
And processing it down to something like this at run-time or even running a pre-processor that generates a new of scripts for "production use" (similar to how take SASS/LESS and convert it to CSS)
Code:
# Not much can be done heredef method_wait Fiber.yieldend# Determined that there are "unnecessary" method calls, and an optimization can be made:def another_method Fiber.yieldenddef third_method Fiber.yieldend
But Fiber.yield is already a method call on a class. Why would you, in this particular instance, abstract it further by putting it into a method called method_wait that does no extra processing for the method call? There is no reason, in this example, not to just call Fiber.yield directly in all calls...
Fiber.yield is a method call (as is everything that isn't a keyword).
However, it has no context. It's a singleton method defined in Fiber that does something specific, and if you wanted to add some extra logic specifically when called from Game_Interpreter, you can't. Plus the fact that it's a singleton method, you can't even just extend the class that is specific to the interpreter because it's already been hardcoded to reference the Fiber class.
Why would you, in this particular instance, abstract it further by putting it into a method called method_wait that does no extra processing for the method call? There is no reason, in this example, not to just call Fiber.yield directly in all calls...
This goes back to the future coding problem: what if someone actually wants to do extra processing in the method_wait? I have no way to determine whether I or anyone else may or may not want to do so. General programming consensus seems to agree that you should simply not do it, but I like to think it as simply a guideline. And in RPG Maker, that guideline doesn't work.
If no one needs to change how it functions, then I just wasted a lot of time writing extra methods (note, the only abstraction I do is extracting methods) and destroying performance by apparently 50% based on the ips benchmarks.
If someone does, then I have just saved them a lot of hassle and have definitely increased compatibility between scripts.
For this specific use case, simply swapping out Fiber.yield with perhaps an instance of a Fiber object (or InterpreterFiber, which also calls Fiber.yield internally) would work just as well for me, and would be even more flexible.
SOURCE suggests that people combine hundreds of scripts into a game. Yes, that is true. While performance is important, I think what's equally important is getting all hundred scripts to work together with minimal effort, and if something simple as having an extra method available to alias will solve the problem (which has been the case the majority of the time), I will do it even if it cuts performance in half.
@Joda:
You are viewing the method extraction at hand from an object-oriented perspective. To understand why it may have a value you have to consider that RPG Maker scripts also have a very aspect-oriented programming style. Implementing these extra methods makes it easier for other scripters to implement their crosscutting concerns. Of course there still is balancing to do, it’s just skewed in the direction of more methods.
if something simple as having an extra method available to alias will solve the problem (which has been the case the majority of the time), I will do it even if it cuts performance in half.
I don't feel like that is an acceptable tradeoff at all, as a script user and writer. If cross-compatibility with untold other scripts leads to a huge performance degradation in the script I am making, I will not make that compatibility change up front. If the point comes up where compatibility with a specific script is necessary, it would be better to solve it at this point to make it as performant as possible.
Now, Ace's script setup isn't ideal for a lot of reasons. This is one big one. But premature compatibility implementations that cause performance drops does actually do more harm than good. If you cut Ace's current performance in half, it will be unplayable for a lot of people. I know it was just an example, but I find it important to point out.
I don't feel like that is an acceptable tradeoff at all, as a script user and writer. If cross-compatibility with untold other scripts leads to a huge performance degradation in the script I am making, I will not make that compatibility change up front. If the point comes up where compatibility with a specific script is necessary, it would be better to solve it at this point to make it as performant as possible.
Now, Ace's script setup isn't ideal for a lot of reasons. This is one big one. But premature compatibility implementations that cause performance drops does actually do more harm than good. If you cut Ace's current performance in half, it will be unplayable for a lot of people. I know it was just an example, but I find it important to point out.
Which is a good point. Perhaps this is the cause of all the "jitter": each frame takes too long to process and so the graphics drawing is too slow and results in choppiness.
Based on the benchmarks on the 1st page, we can conclude that simply adding a single method call will result in about 33% drop in performance (3m ips vs 1.9m ips). I wouldn't be surprised if simply getting rid of extra method calls would eliminate the problem.
The trade-off in the end is compatibility/flexibility vs performance. I would rather focus on compatibility between scripts, and then focus on performance issues, rather than optimizing for performance before compatibility.
But the solution to both are the same in the end: copy lines of code from both scripts and merge them together.
I'd say just make compatibility for the stuff that is really needed to be compatible with the script. Trying to do it for a lot of scripts would just really cause some unnecessary additions to the code.
I'd say just make compatibility for the stuff that is really needed to be compatible with the script. Trying to do it for a lot of scripts would just really cause some unnecessary additions to the code.
As Zeriab suggests, you would need to find a balance. I agree, something likely aren't going to ever need to be used (they COULD be used, but the chances are slim)
On one extreme you have everything in one method (like some RMXP classes).
On the other extreme you have a method call for every single thing in existence.
And then you have everything in between where you pick and choose how you want to structure your code.
Take a look at the following example:
num = 1Or how about we instead do
Code:
num = get_number
And that method returns 1.Does it seem unnecessary?
It seems to me that sometimes abstracting Fiber.yield can actually does more harm than good. For example, let's say fiber_yield_def does Fiber.yield and only the below methods use it:
If another scripter wants to add the same thing right before or after Fiber.yield to all those methods, abstracting it to fiber_yield_def will of course help solving compatibility issues. But what if that scripter only wants some of those methods to add those stuffs, or what if that scripter wants to add different things on different methods? Then that scripter would still probably have to rewrite all those methods involved, as simply editing fiber_yield_def would also alter the behavior of all the other methods that scripter didn't want to change.
Although the compatibility issues wouldn't be any harder to solve, the performance cost would increase for almost nothing. If fiber_yield_def were called really frequently(like called every frame), the performance impact could even become significant(bear in mind there are some other things that are run every frame, and the increased performance impact here might just be enough to begin to cause a noticeable constant fps drop on not so powerful machines).
To me, it's likely a case-by-case problem. In some cases it's reasonable to think that most of the others are extremely likely to change all methods using Fiber.yield in the same way, but in some cases the opposite maybe true. I'd prefer a case-by-case analysis to help me decide whether abstraction will likely be better for compatibility, and whether that benefit will outclass the performance impact. Also, I think sometimes the compatibility vs code performance issues are dependent on the actual cases as well.
In some cases, code performance's just critical with next to no exception. Codes that are run every frame can be in these cases. An example is methods called every frame in a complicated atb system, as lots of stuffs are often updated per frame(and some of them demand quite some resources, like redrawing all battlers' atb bars when their atb values change, and they can be changed every frame). Here I'd probably lean my code to the performance side even if the compatibility has to be somehow sacrificed, as codes with poor performance here would probably cause significant constant fps drop unless the machine's powerful. I've been editing an atb system(although my edit isn't released) and in my machine, simply getting rid of most 1-2 line methods boosted the average fps by nearly 10 already, which is significant imo.
In some other cases, code performance's not such a great deal, at least not big enough to compromise even just a bit of compatibility. Most things that won't run frequently and have to run codes demanding lots of resources can be these cases. An example is refreshing the status window in Yanfly's Ace Battle Engine. Here it's the drawing itself that's really expensive(probably because it draws actor faces and action icons as well), almost everything else combined is trivial in terms of performance impact. Even that of the dreaded eval(as long as the codes evaluated are simple) is an utter joke compared to that of the drawing. In my test case, The cost of 1 such drawing alone already rivals 10000 simple eval. Not to mention that the cost of eval is much, much higher than that of method calls(can be up to about 86 times higher in my previous benchmarks). Note that all these are machine dependent though.
Maybe there are some cases where compatibility is critical, but as I haven't encountered any such cases, I can't talk about them. Maybe the core engines can be these cases but I'm not sure on this one(Like the notorious incompatibility between Yanfly's Battle Engine and Victor's Basic Module that breaks compatibility between many of their scripts. Some users still hope they can be solved although many of them are extremely unlikely to be solved).
About 1 line methods, I'll try to avoid that in general(although I'll still use them in some cases), but I'll also try to avoid the other extreme(or at least I think so) as much as possible, like some 40+ line methods(I've dealt with several of those before and it's really painful for me to rewrite the entire methods when I just have to change several lines). In my experience(although I just have 1 year of it), most 4-5 line methods are short enough to have a reasonable compatibility in general and many 10 line methods can sometimes still be acceptable, while most 20+ lines(let alone 40+ lines) method will probably start to harm compatibility. Of course I'd also prefer a case-by-case approach here.
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.