> On Aug 9, 2016, at 7:00 PM, Félix Cloutier <[email protected]> wrote: > No, I fully understand this. My point is that this doesn't seem to accurately > represent the cost of exceptions. > > In a JSON parser, since the topic has been brought up, you don't have Y*P > calls that succeed and N*(1-P) calls that fail. You have Y*P calls that > succeed and *at most one* call that fails. That's because once you hit the > (1-P), you stop parsing. This heavily biases calls in favor of succeeding, > which is what I tried to illustrate with my anecdote.
This is true of JSON deserialization, where typically you wouldn't do something like prospectively deserialize a value one way and then try something else if that fails. But it's not true of, say, a parser for a more ambiguous language, or for any number of other applications in which non-terminal failures are more common. As Joe said, our intended ABI here is to get this down to a single branch-on-nonzero-register instruction immediately after the return, which is an overhead we're pretty comfortable with. John. > > I haven't attempted statistics in a while, but that looks like a geometric > distribution to me. That would give something like: > > Y_1 * (1/P) + N_1 < Y_2 * (1/P) + N_2 > > in which Y completely dominates N, especially as P goes smaller. > > Félix > >> Le 9 août 2016 à 16:22:08, John McCall <[email protected] >> <mailto:[email protected]>> a écrit : >> >>> >>> On Aug 9, 2016, at 8:19 AM, Félix Cloutier via swift-evolution >>> <[email protected] <mailto:[email protected]>> wrote: >>> >>>> “Zero cost” EH is also *extremely* expensive in the case where an error is >>>> actually throw in normal use cases. This makes it completely >>>> inappropriate for use in APIs where errors are expected in edge cases >>>> (e.g. file not found errors). >>> >>> Anecdote: I work with a web service that gets several million hits a day. >>> Management loves to use the percentage of succeeding web requests as a >>> measure of overall health. The problem with that metric is that when a web >>> request fails, clients fall in an unhealthy state and stop issuing requests >>> for a while. Therefore, one failing request prevents maybe twenty more that >>> would all have failed if the client hadn't bailed out, but these don't show >>> in statistics. This makes us look much better than we actually are. >>> >>> If I had any amount of experience with DTrace, I'd write a script that logs >>> syscall errors to try and see how the programs that I use react to >>> failures. I'm almost certain that when one thing stops working, most >>> programs backs out of a much bigger process and don't retry right away. >>> When a program fails to open a file, it's also failing to read/write to it, >>> or whatever else people normally do after they open files. These things are >>> also expensive, and they're rarely the type of things that you need to (or >>> even just can) retry in a tight loop. My perception is that the immediate >>> cost of failing, even with expensive throwing, is generally dwarfed by the >>> immediate cost of succeeding, so we're not necessarily losing out on much. >>> >>> And if that isn't the case, there are alternatives to throwing that people >>> are already embracing, to the point where error handling practices seem >>> fractured. >>> >>>>> I don't really know what to expect in terms of discussion, especially >>>>> since it may boil down to "we're experts in this fields and you're just >>>>> peasants” >>>> >>>> I’m not sure why you think the Swift team would say something that >>>> derogatory. I hope there is no specific action that has led to this >>>> belief. If there is, then please let me know. >>> >>> Of course not. All of you have been very nice and patient with us peasants, >>> at least as far as "us" includes me. :) This was meant as a light-hearted >>> reflection on discussing intimate parts of the language, where my best >>> perspective is probably well-understood desktop/server development, whereas >>> the core team has to see that but also needs a high focus on other things >>> that don't even cross my mind (or at least, that's the heroic picture I >>> have of you guys). >>> >>> For instance, my "expensive" stops at "takes a while". Your "expensive" >>> might mean "takes a while and drains the very finite energy reserves that >>> we have on this tiny device" or something still more expansive. These >>> differences are not always immediately obvious. >>> >>>>> However, as linked above, someone did for Microsoft platforms (for >>>>> Microsoft-platform-style errors) and found that there is an impact. >>>> >>>> C++ and Swift are completely different languages in this respect, so the >>>> analysis doesn’t translate over. >>> >>> The analysis was (probably?) done over C++ and HRESULTs but with the >>> intention of applying it to another language (Midori), and it most likely >>> validated the approach of other languages (essentially everything >>> .NET-based). Several findings of the Midori team are being exported to >>> Microsoft's new APIs, notably the async everywhere and exceptions >>> everywhere paradigms, and these APIs are callable from both so-called >>> managed programs (GCed) and unmanaged programs (ref-counted). >>> >>> Swift operations don't tend to throw very much, which is a net positive, >>> but it seems to me that comparing the impact of Swift throws with another >>> language's throws is relatively fair. C# isn't shy of >>> FileNotFoundExceptions, for instance. >> >> I think you may be missing Chris's point here. >> >> Exception ABIs trade off between two different cases: when the callee throws >> and when it doesn't. (There are multiple dimensions of trade-off here, but >> let's just talk about cycle-count performance.) Suppose that a compiler can >> implement a call to have cost C if it just "un-implements" exceptions, the >> way that a C++ compiler does when they're disabled. If we hand-wave a bit, >> we can pretend that all the costs are local and just say that any particular >> ABI will add cost N to calls that don't throw and cost Y to calls that do. >> Therefore, if calls throw with probability P, ABI 1 will be faster than ABI >> 2 if: >> Y_1 * P + N_1 * (1 - P) < Y_2 * P + N_2 * (1 - P) >> >> So what is P? Well, there's a really important difference between >> programming languages. >> >> In C++ or C#, you have to compute P as a proportion of every call made by >> the program. (Technically, C++ has a way to annotate that a function >> doesn't throw, and it's possible under very specific circumstances for a C++ >> or C# implementation to prove that even without an annotation; but for the >> most part, every call must be assumed to be able to throw.) Even if >> exceptions were idiomatically used in C++ for error reporting the way they >> are in Java and C#, the number of calls to such "failable" functions would >> still be completely negligible compared to the number of calls to functions >> that literally cannot throw unless (maybe!) the system runs out of memory. >> Therefore, P is tiny — maybe one in a trillion, or one in million in C# if >> the programmer hasn't yet discovered the non-throwing APIs for testing file >> existence. At that kind of ratio, it becomes imperative to do basically >> anything you can to move costs out of N. >> >> But in Swift, arbitrary functions can't throw. When computing P, the >> denominator only contains calls to functions that really can report some >> sort of ordinary semantic failure. (Unless you're in something like a >> rethrows function, but most of those are pretty easy to specialize for >> non-throwing argument functions.) So P is a lot higher just to begin with. >> >> Furthermore, there are knock-on effects here. Error-handling is a really >> nice way to solve certain kinds of language problem. (Aside: I keep running >> into people writing things like JSON deserializers who for some reason >> insist on making their lives unnecessarily difficult by manually messing >> around with Optional/Either results or writing their own monad + combinator >> libraries or what not. Folks, there's an error monad built into the >> language, and it is designed exactly for this kind of error-propagation >> problem; please just use it.) But we know from experience that the expense >> (and other problems) of exception-handling in other languages drives people >> towards other, much more awkward mechanisms when they expect P to be higher, >> even if "higher" is still just 1 in 100 or so. That's awful; to us, that's >> a total language failure. >> >> So the shorter summary of the longer performance argument is that (1) we >> think that our language choices already make P high enough that the >> zero-cost trade-offs are questionable and (2) those trade-offs, while >> completely correct for other languages, are known to severely distort the >> ways that programmers use exceptions in those languages, leading to worse >> code and more bugs. So that's why we aren't using zero-cost exceptions in >> Swift. >> >> John. >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
