On Tuesday, 18 July 2017 at 16:57:30 UTC, Ali Çehreli wrote:
> As for performance, I have a first result:
> https://github.com/jll63/methods.d/blob/master/benchmarks/source/benchmarks.d#L122
> but I still have to implement the "first argument
optimization". I am
> working on it.

I could use some explanation for the results but I can for the blog article. ;)

I pit a method-based call against its equivalent using virtual functions. First calling a virtual function via a base class is pitted against a method with one virtual parameter. Then the same but calling via an interface. Lastly, I compare double dispatch with a method with two virtual arguments. I use std.datetime.comparingBenchmark, which reports the result as time(base)/time(target). So open methods are a bit slower than ordinary virtual function calls but not that much. In the meantime I have applied a second optimization for unary methods and this brings them within 33% of an ordinary, compiler implemented vfunc call. Which is OK because the situation is highly artificial. If the function does anything, the difference will be imperceptible.

I am more annoyed by double dispatch beating binary methods. I will have to look at the assembler, but it may be that the index pointer is too far from the object. To begin the real work, I need to fetch that pointer form an object. Currently it is stored in ClassInfo.deallocator, so I have to 1/ fetch the vptr 2/ fetch the ClassInfo* 3/ fetch 'deallocator'. What happens next depends on the arity.

Any chance of Walter giving me a pointer in the vtable? Aside the ClassInfo*? Or at least a pointer in ClassInfo, or reassign the deallocator when it is eventually retired?

It's not surprising that ldc (and gdc) can be much better than dmd in optimization.

I would like to try gdc but it conflicts with ldc2 - you know, the "alias __va_list = __va_list_tag;" issue. I found suggestions via google but nothing worked for me so far.

By the way, I'm in awe of your D skills in such a short time!

Thanks :) I found out that D was much more natural, "predictable" than C++. The most cryptic error messages happened when I forgot the "!", IIRC.

I'm sure there are parts of the code that can be cleaned up but it's taking advantage of many powerful features of the language. I still think the usage can be made easier but I'm not sure yet. I hope others will take a look at the code and come up with an easier interface. Perhaps they are all needed but I'm thinking about the need for forward declaration, the need for the underscore prefix, etc.

(in reverse order)

Regarding the prefix, it is needed to prevent overload resolution from trumping dynamic dispatch - see here: https://github.com/jll63/methods.d/blob/master/examples/whytheunderscore/source/app.d Allowing the same name would necessitate compiler support.

As for the the forward declaration - I don't think it is possible to dispense with it. All open methods systems I know of have it (defgeneric in CLOS, defmulti in Clojure, Stroustrup's proposal...). Consider:

class A { }
class B : A { }
class X : B { }
class Y : B { }

@method void _foo(virtual!X x) { ... }
@method void _foo(virtual!Y x) { ... }

What is the base method? foo(B)? foo(A)? It may well be the latter. Also don't forget that the complete specialization set is known, at the earliest, at link time. If you (arbitrarily) pick foo(B), another module may introduce a B or an A specialization.

As for suggestions and advise, they are very welcome :) already got a couple of PRs. Here are the remaining questions on my mind:

- the module/package name: I am pretty much set on openmethods though...

- throw an exception if a method is not define for the argument set, or ambiguous: having big doubts about this. We want the possibility of @nothrow methods, don't we? So I will probably call a delegate via a pointer (a la C++) which, by default, will abort().

- the method prefix: hesitating between just _ or maybe m_ ???

- replace version(explain) with debug levels?

Reply via email to