On Thursday, 2 August 2018 at 16:21:58 UTC, Jonathan Marler wrote:
On Monday, 30 July 2018 at 21:02:56 UTC, Steven Schveighoffer
wrote:
Would it be a valid optimization to have D remove the
requirement for allocation when it can determine that the
entire data structure of the item in question is an rvalue,
and would fit into the data pointer part of the delegate?
Here's what I'm looking at:
auto foo(int x)
{
return { return x + 10; };
}
In this case, D allocates a pointer on the heap to hold "x",
and then return a delegate which uses the pointer to read x,
and then return that plus 10.
However, we could store x itself in the storage of the pointer
of the delegate. This removes an indirection, and also saves
the heap allocation.
Think of it like "automatic functors".
Does it make sense? Would it be feasible for the language to
do this? The type system already casts the delegate pointer to
a void *, so it can't make any assumptions, but this is a
slight break of the type system.
The two requirements I can think of are:
1. The data in question must fit into a word
2. It must be guaranteed that the data is not going to be
mutated (either via the function or any other function). Maybe
it's best to require the state to be const/immutable.
I've had several cases where I was tempted to not use
delegates because of the allocation cost, and simply return a
specialized struct, but it's so annoying to do this compared
to making a delegate. Plus something like this would be
seamless with normal delegates as well (in case you do need a
real delegate).
-Steve
I think the number of cases where you could optimize this is
very small. And the complexity of getting the compiler to
analyze cases to determine when this is possible would be very
large.
In addition, a developer can already do this explicitly if they
want, i.e.
auto foo(int x)
{
static struct DummyStructToMakeFunctionWithDelegateAbi
{
int passthru() const { return cast(int)&this; }
}
DummyStructToMakeFunctionWithDelegateAbi dummyStruct;
auto dg = &dummyStruct.passthru;
dg.ptr = cast(void*)(x + 10); // treat the void* pointer as
an int value
return dg;
}
void main(string[] args)
{
auto dg = foo(32);
import std.stdio;
writefln("dg() = %s", dg());
}
It's definitely ugly but it works. This will print the number
"42" as expected.
This would be a case where DIP1011 extern(delegate) would come
in handy :) i.e.
extern(delegate) int passthru(void* ptr) { return cast(int)ptr;
}
int delegate() foo2(int x)
{
return &(cast(void*)(x + 10)).passthru;
}
Actually, I'll do you one better. Here's a potential library
function for it. I'm calling these types of delegates "value
pointer delegates".
// Assume this is in a library somewhere
auto makeValuePtrDelegate(string valueName, string funcBody, T)(T
value)
{
static struct DummyStruct
{
auto method() const
{
mixin("auto " ~ valueName ~ " = cast(T)&this;");
mixin (funcBody);
}
}
DummyStruct dummy;
auto dg = &dummy.method;
dg.ptr = cast(void*)value;
return dg;
}
auto foo(int x)
{
return makeValuePtrDelegate!("val", q{ return val + 10; })(x);
}
void main(string[] args)
{
auto dg = foo(32);
import std.stdio;
writefln("dg() = %s", dg());
}