Slow performance compared to C++, ideas?

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Mon Jun 3 21:07:15 PDT 2013


On 6/3/13 10:51 PM, Jonathan M Davis wrote:
> On Monday, June 03, 2013 22:25:13 Andrei Alexandrescu wrote:
>> It's useless to focus on the breakage override has caused. Yes it did
>> cause breakage. There is no conclusion to draw from that without
>> considering the considerably complex dynamics surrounding the whole
>> matter (experience, benefits, number of users affected positively and
>> negatively).
>>
>> To use that breakage as an argument linked to absorbing breakage caused
>> by switching to final-by-default does not make sense. I'll try to
>> abstain replying to this particular point in the future, it just
>> instantly lowers the quality of the dialog.
>
> The comparison is made because we're talking about a related change, and the
> actual breakage caused by override was fairly recent, so while the decision to
> cause that breakage was made quite some time ago, we were still willing to
> cause that breakage fairly recently, so the implication then is that it would
> be acceptable to do something related which causes less breakage.

This nice one-long-sentence paragraph does little in the way of helping 
because it just restates the same well-understood matter that I disagree 
with without adding information.

My argument is that the relatedness of the change is tenuous, and 
explains why I think so. The paragraph above presupposes again 
relatedness, and proceeds with a tedious re-explanation of the 
consequences of that assumption.

We don't make progress like this:

Speaker A: "XYZ, therefore ABC".

Speaker B: "I disagree with XYZ because TUV."

Speaker A: "But since XYZ then ABC."

> Now, that being said, I do think that we need to look at this change in its
> own right, and it needs to justify itself, but clearly folks like Manu think
> that it does justify itself and feel that there's something off if we're
> willing to make the change with override and not this one, when they're
> related, and this one causes even less breakage.

The matters of override vs virtual/final are not related so analyzing 
the consequences of said relatedness is not very productive.

That doesn't make one necessarily more important than the other, but it 
must be understood that they are distinct matters, and we can't compare 
one comma in one with one comma in the other.

Requiring "override" in overriding methods:

1. Protects against categories of bugs that otherwise would be 
impossible to protect against: accidental overriding and accidental 
non-overriding.

2. Provides an important maintenance tool for code evolution, statically 
breaking code that would otherwise change semantics silently.

It's important that without "override" there is virtually no protection 
against these issues. We're talking about an "all goodness" feature, and 
this kind of stuff is in very low supply in this world.

Choosing "virtual" by default:

1. Fosters flexibility by allowing derived classes to override 
unannotated methods in base classes.

2. Is suboptimal in speed because users pay for the potential 
flexibility, even when that flexibility is not actually realized 
(barring a static analysis called class hierarchy analysis).

3. Ultimately lets the programmer choose the right design by using 
annotations appropriately.

Choosing "final" by default:

1. Fosters speed by statically binding calls to unannotated methods.

2. Is suboptimal in flexibility because users pay for the speed with 
loss of flexibility, even when speed is not a concern but flexibility is.

3. Ultimately lets the programmer choose the right design by using 
annotations appropriately.

The introduction of "override" allows a language to choose either final 
or virtual by default, without being exposed to potential bugs. This is 
pretty much the entire extent to which "override" is related to the 
choice of virtual vs. final by default.

Today, D makes it remarkably easy to choose the right design without 
significant boilerplate, regardless of the default choice:

- "struct" introduces a monomorphic type with a limited form of 
subtyping (via alias this) and no dynamic binding of methods.

- "final class" introduces a leaf class that statically disallows 
inheritance and consequently forces static calls to all methods. (BTW I 
recall there were some unnecessary virtual calls for final classes, has 
that been fixed?)

- "final { ... }" introduces a pseudo-scope in which all declared 
methods are final

- "final:" introduces a pseudo-label after which all declared methods 
are final

(Granted, there's an asymmetry - there's no "~final:" label to end 
final, which makes it marginally more tedious to arrange final and 
non-final methods in the class.)

This leaves an arguably small subset of designs, scenarios, projects, 
and teams that would be affected by the choice of default. When that 
point has been made, it has been glibly neglected with an argument along 
the lines of "yeah, well programmers will take the path of least 
resistance, not really think things through, come from C++ and assume 
the wrong default", which may as well be true for a subset of 
situations, but further reduces the persona typically affected by the 
choice of default.

I personally have a hard time picturing someone who is at the same time 
obsessed with performance, disinclined to assess it, unwilling to learn 
how to improve it, and incapable of using simple tools to control it. 
Yet this persona is put at the center of the argument that we must 
change the default right now seeing as it is a huge problem. To top it 
off, the entire fallacy about override causing more breakage is brought 
about. Yes, smoking kills, and the fact that cars kill more people 
doesn't quite have a bearing on that.

> I do think that virtual-by-default was a mistake and would like to see it
> fixed, but I'm also not as passionate about it as Manu or Don.

Choosing virtual (or not) by default may be dubbed a mistake only in a 
context. With the notable exception of C#, modern languages aim for 
flexibility and then do their best to obtain performance. In the context 
of D in particular, there are arguments for the default going either 
way. If I were designing D from scratch it may even make sense to e.g. 
force a choice while offering no default whatsoever.

But bottom line is, choosing the default is not a big deal for D because 
this wonderful language offers so many great building blocks for any 
design one might imagine.

> Manu in
> particular seems to be sick of having to fix performance bugs at Remedy Games
> caused by this issue and so would really like to see non-virtual be the
> default. The folks using D in companies in real-world code seem to think that
> the ROI on this change is well worth it.

I'm wary/weary of polls with a small number of participants. Let's also 
not forget that these people do use D successfully, and if sticking 
"final" here and there is the most difficult endeavor that has helped 
performance of their programs, I'd say both them and D are in great shape.

> And a technical issue which affects us all is how this interacts with
> extern(C++). Daniel Murphy is having to improve extern(C++) in order to be
> able to port the dmd frontend to D (so that it can properly interact with the
> backends), and the fact that member functions are virtual by default definitely
> causes problems there. He would know the details about that better than I
> would, but IIRC, it had to do with the fact that we needed to be able to
> interface with non-virtual member C++ functions. So, depending on the details
> with that, that alone could make it worth switching to non-virtual by default,
> particularly when the breakage is actually quite loud and easy to fix.

I don't know much about that matter, as I don't know about the argument 
related to mock injection and such, so I won't comment on this.

Finally, I'll note that I'd started a reply to this remark by Manu (who 
in turn replied to David):

> Is there a reason this change offends you enough to call me names? Or
> can you at least tell how I'm being narrow-minded?

I deleted that reply, but let me say this. In a good argument:

1. Participants have opinions and beliefs derived from evidence they 
have accumulated.

2. The very ongoing discourse offers additional evidence to all 
participants by means of exchange of information. This is to be expected 
because participants have varied backgrounds and often it's possible to 
assess how competent they are.

3. The merits of various arguments are discussed, appreciated, and 
integrated within the opinions and beliefs of the participants.

4. A conclusion is reached in light of everything discussed and 
everybody is richer that way.

In a not-so good argument:

1. Participants start each from an immutable belief.

2. Their preoccupation is to amass, bend, or fabricate any argument that 
would make that belief prevail, and to neglect any argument to the contrary.

3. The entire discussion has a foregone conclusion for everyone 
involved, i.e. nobody changes opinions and nobody is gained.

The attitude "I know what's right, the only problem is to make you 
understand" doesn't serve anyone, because it locks "me" in a trench with 
no horizon and no mobility, and elicits an emotional response in "you".

Here we don't want to keep "virtual" default and we don't want to make 
"final" default. We want to do what's right. So the discussion should 
progress toward finding what's right, not starting from knowing what's 
right and working arguments from there.


Andrei


More information about the Digitalmars-d mailing list