On 11/1/14 9:30 AM, Manu via Digitalmars-d wrote:
On 31 October 2014 01:30, Steven Schveighoffer via Digitalmars-d


Sorry, I meant future *D supported* platforms, not future not-yet-existing
platforms.

I'm not sure what you mean. I've used D on current and existing games
consoles. I personally think it's one of D's most promising markets...
if not for just a couple of remaining details.

I don't think D officially supports these platforms. I could be wrong.

Also, my suggestion will certainly perform better on all platforms.
There is no platform that can benefit from the existing proposal of an
indirect function call per write vs something that doesn't.

Performance isn't the only consideration. In your case, it has a higher priority than ease of implementation, flexibility, or usability. But that's not the case everywhere.

Consider the flip-side: on x86, your mechanism may be a hair faster than just having a delegate. Is it worth all the extra trouble for those folks to have to save some state or deal with reallocating buffers in their toString functions?

Before we start ripping apart our existing APIs, can we show that the
performance is really going to be so bad? I know virtual calls have a bad
reputation, but I hate to make these choices absent real data.

My career for a decade always seems to find it's way back to fighting
virtual calls. (in proprietary codebases so I can't easily present
case studies)
But it's too late now I guess. I should have gotten in when someone
came up with the idea... I thought it was new.

At the moment, you are stuck with most toString calls allocating on the GC every time they are called. I think the virtual call thing should be a pleasant improvement :)

But in all seriousness, I am not opposed to an alternative API, but the delegate one seems to find the right balance of flexibility and ease of implementation.

I think we can use any number of toString APIs, and in fact, we should be able to build on top of the delegate version a mechanism to reduce (but not eliminate obviously) virtual calls.

For instance, D's underlying i/o system uses FILE *, which is about as
virtual as you can get. So are you avoiding a virtual call to use a buffer
to then pass to a virtual call later?

I do a lot of string processing, but it never finds it's way to a
FILE*. I don't write console based software.

Just an example. Point taken.

A reentrant function has to track the state of what has been output, which
is horrific in my opinion.

How so? It doesn't seem that bad to me. We're talking about druntime
here, the single most used library in the whole ecosystem... that shit
should be tuned to the max. It doesn't matter how pretty the code is.

Keep in mind that any API addition is something that all users have to deal with. If we are talking about a specialized, tuned API that druntime and phobos can use, I don't think it would be impossible to include this.

But to say we only support horrible allocate-every-toString-call mechanism, and please-keep-your-own-state-machine mechanism is not good. The main benefit of the delegate approach is that it's easy to understand, easy to use, and reasonably efficient. It's a good middle ground. It's also easy to implement a sink. Both sides are easy, it makes the whole thing more approachable.

The largest problem I see is, you may not know before you start generating
strings whether it will fit in the buffer, and therefore, you may still end
up eventually calling the sink.

Right. The api should be structured to make a virtual call _only_ in
the rare instance the buffer overflows. That is my suggestion.
You can be certain to supply a buffer that will not overflow in many/most cases.

I, and I'm sure most of the developers, are open to new ideas to make something like this as painless as possible. I still think we should keep the delegate mechanism.

Note, you can always allocate a stack buffer, use an inner function as a
delegate, and get the inliner to remove the indirect calls. Or use an
alternative private mechanism to build the data.

We're talking about druntime specifically. It is a binary lib. The
inliner won't save you.

Let's define the situation here -- there is a boundary in druntime in across which no inlining can occur. Before the boundary or after the boundary, inlining is fair game.

So for instance, if a druntime object has 3 members it needs to toString in order to satisfy it's own toString, those members will probably all be druntime objects as well. In which case it can optimize those sub-calls.

And let's also not forget that druntime has template objects in it as well, which are ripe for inlining.

This is what I meant.

Would you say that *one* delegate call per object output is OK?

I would say that an uncontrollable virtual call is NEVER okay,
especially in otherwise trivial and such core functions like toString
in druntime. But one is certainly better than many.

I'm trying to get a feel for how painful this has to be :) If we can have one virtual call, it means you can build a mechanism that works with delegates + a more efficient one, by just sinking the result of the efficient one. This means you can work with the existing APIs right now.

This is a typical mechanism that Tango used -- pass in a ref to a dynamic
array referencing a stack buffer. If it needed to grow, just update the
length, and it moves to the heap. In most cases, the stack buffer is enough.
But the idea is to try and minimize the GC allocations, which are
performance killers on the current platforms.

I wouldn't hard-code to overflow to the GC heap specifically. It
should be an API that the user may overflow to wherever they like.

Just keep in mind the clients of this API are on 3 sides:

1. Those who implement the toString call.
2. Those who implement a place for those toString calls to go.
3. Those who wish to put the 2 together.

We want to reduce the burden as much as possible on all of them. We also don't want to require implementing ALL these different toString mechanisms -- I should be able to implement one of them, and all can use it.


I think adding the option of using a delegate is not limiting -- you can
always, on a platform that needs it, implement a alternative protocol that
is internal to druntime. We are not preventing such protocols by adding the
delegate version.

You're saying that some platform may need to implement a further
completely different API? Then no existing code will compile for that
platform. This is madness. We already have more than enough API's.

You just said you don't use FILE *. Why do we have to ensure all pieces of Phobos implement everything you desire when you aren't going to use it? I don't think it's madness to *provide* a mechanism for more efficient (on some platforms) code, and then ask those who are interested to use that mechanism, while not forcing it on all those who aren't. You can always submit a pull request to add it where you need it! But having an agreed upon API is an important first step. So let's get that done.

But on our currently supported platforms, the delegate vs. GC call is soo
much better. I can't see any reason to avoid the latter.

The latter? (the GC?) .. Sorry, I'm confused.


My bad, I meant the former. No wonder you were confused ;)

-Steve

Reply via email to