6 ms·
All of the performance improvements combined were about 0.00002s per call. They came at the cost of simplicity.
by scosman 13y ago
All of the performance improvements combined were about 0.00002s per call. They came at the cost of simplicity.
- grandalf 13y agoGreat point. Simplicity is one of the main reasons to use Ruby, not performance.
- vidarh 13y agoThat may be so, but sometimes, once your design has settled, you'll have hotspots you want to speed up. Of course I hope nobody starts jumping through all kinds of hoops before they know that it matters...
- catnaroek 13y agoEase of use. Which is not the same thing as simplicity.
- grandalf 13y agoWell, code can be organized in a simple way where the constructs of the language do not get in the way of conceptual clarity. This may or may not translate to ease of use depending on the user.
- catnaroek 13y agoMy understanding of simplicity is that it can be nailed down to a few universal principles: 1. Facilitating local reasoning: I do not want to read the whole program to understand what a single line of code may or may not do. 2. Compartmentalizing concepts: There should be a single, easily identifiable feature for each of: defining classes of data, separating concerns between different code units, enabling code reuse. 3. Keeping abstraction leaks to a minimum: Abstraction leaks force corner cases to be handled outside of the abstraction (that is, low-level programming). So abstractions should ideally not be leaky, and leaks should only be tolerated if 1. there is no nonleaky (or less leaky) alternative, 2. the benefits of the abstraction outweigh the cost of handling the leak. Simplicity leads to conceptual clarity. On the other hand, ease of use is more about the pragmatic desire to invest the least amount of effort possible in making a working program. At its most short-sighted extreme, the easiest thing to do is simply following the path of least resistance. A more refined version is following design patterns that experience tells work okay. Simplicity and ease of use are most certainly positively correlated, but it is perfectly possible for a horribly contrived construct to be easy to use, if its complex internal details happen to match nicely the problem domain one is working on. My heuristic take (admittedly not justified on anything beyond aesthetical preference) is that simplicity is the more fundamental concept, and ease of use is just a corollary. And my impression of Ruby is that it seeks to maximize ease of use, resorting to non-simple means if necessary.
- jamesbritt 13y agoIndeed, it would be simpler if there was only "if" and no "unless", or multiple names for what are the same method (Array#size and Array#length), but (often) they're there to make things easier to read and write.
- catnaroek 13y agoA more serious offender is the .to_s method: Why on earth is nil.to_is == "", but [nil].to_s == "[nil]"?
- danenania 13y agoWhat would you expect [nil].to_s to be? Seems like a reasonable representation to me. I certainly wouldn't want "['']", and I also wouldn't want nil.to_s to give me "nil". In practice having nil evaluate to an empty string is quite useful.
- catnaroek 13y agoI think nil.to_s == "nil" is pretty reasonable. At least, it gives .to_s an elegant definition that does not need to case-analyze the nil-ness of the elements of an array. In any case, this just reinforces my point that Ruby emphasizes ease of use, not simplicity.
- egonschiele 13y agoIn most cases I agree with you. In this case though, the speed penalty could have been an argument not to use contracts.ruby...and I wanted to avoid that :)
- catnaroek 13y agoRuntime tests always cause a performance penalty. (And they do not prevent you from writing buggy programs! They just make the runtime environment yell at you if a bug is found.) If you want to guarantee that a property holds for all the elements of a set (every possible running instance of a program), forget about the elements and work with the set's definition (the program itself).
- egonschiele 13y agoDo you mean something like laser?: https://github.com/michaeledgar/laser https://github.com/michaeledgar/laser
- catnaroek 13y agoLASER's homepage ( http://carboni.ca/projects/p/laser http://carboni.ca/projects/p/laser ) for some reason does not load in my browser. http://downforeveryoneorjustme.com/http://carboni.ca/projects/p/laser http://downforeveryoneorjustme.com/http://carboni.ca/project... Judging the project from its feature list in the README.md, it does not seem like LASER is capable of handling arbitrary invariants.
- ufo 13y agoStatic analysis is really hard in general though, and in complex and highly dynamic languages like Ruby its even harder. Some things you really are better off using runtime checks for... And in the end, smaller overhead on the contract checker means that the programmer has more room to add extra tests and less reason to turn off the checks in production.
- catnaroek 13y ago
- vidarh 13y agoArguably, a lot of them came through improvements that did nothing to harm simplicity. Removing the unnecessary callback, for example. And ".times" is certainly more idiomatic than ".zip" for his case. It's a toss up between .each_with_index and .times, but really both will be readable enough. And from experience, you'll find Ruby devs that are unfamiliar with "zip", but not with "each_with_index" or "times". "alias_method" is also far more idiomatic than grabbing a reference to the method - I don't think I've ever actually seem method() used in production code (not that I've explicitly looked)
- cortesoft 13y agoThis was my thought as well. This is one of the main fallacies of code optimization; unless these functions are called a LOT, you very well might be optimizing something that is not your bottleneck. You can see the author is missing this point because he talks about going from "20 seconds to 1.5 seconds." He is using those numbers as if they are real use case times, when in fact they are generated from an arbitrary amount of calls to a function. So many times I will see people make code optimizations of this type to shave fractions of a millisecond from a function call at the cost of complexity, memory usage, etc. Meanwhile, most of the time their program is actually waiting on IO or something of the like. Blind optimization is counter productive.
- egonschiele 13y agoWhy do you think this is blind optimization? When do you think an optimization like this would be appropriate? Can you give an example of a performance optimization you have made recently?
- jasonlotito 13y ago> Why do you think this is blind optimization? Because it is. It's a clear case of premature optimization. Especially since nothing was offered up to to suggest otherwise. > When do you think an optimization like this would be appropriate? When it's being reported back that it's one of the top bottlenecks. Before that, it's mostly a educational trip. It's great to learn how to write optimized code, but, frankly, removing empty function calls isn't an optimization. > Can you give an example of a performance optimization you have made recently? Eliminating the need for excessive memcache calls by not only changing the objects being called, but also by eliminating the need to make the calls in the first place by intelligently using existing locally cached… cache. If that makes sense. Anyways, that improved performance by ~20ms. It wasn't the slowest call running, but it was the slowest call running that could be optimized at this point.
- nonchalance 13y agoYou don't know what project he was using that gem for. It's very well possible that he did some tests and found that this contracts library was the bottleneck. If that were the case, then the approach makes sense. Although, given the nature of the library, I highly doubt that was the bottleneck