Escape analysis
Steven Schveighoffer
schveiguy at yahoo.com
Wed Oct 29 08:52:14 PDT 2008
"Sergey Gromov" wrote
> Tue, 28 Oct 2008 23:33:53 -0400, Steven Schveighoffer wrote:
>
>> "Sergey Gromov" wrote
>>> Steven Schveighoffer wrote:
>>>> "Sergey Gromov" wrote
>>>>> An argument to a virtual function call always escapes by default. It
>>>>> may be possible to declare an argument as non-escaping (scope?) and
>>>>> compiler should then enforce non-escaping contract upon any overriding
>>>>> functions.
>>>>
>>>> This is tricky, because most class member functions are virtual, so
>>>> you are forced to litter all your functions with escaping/non-escaping
>>>> syntax. To be accurate you need to define the escape graph in the
>>>> signature, which will be a PITA. What would be worse is to not have a
>>>> way to express the complete graph.
>>>
>>> Not every call to a virtual function is itself virtual, and not every
>>> virtual function cares whether its argument escapes.
>>>
>>> I'd say more: the noscope should be default for all reference types
>>> except delegates because you usually don't care. I agree that having
>>> scope delegates the default is probably the right thing to do, but only
>>> if a compiler can detect violations of this contract.
>>
>> A very very common technique in Tango to save using heap allocation is to
>> declare a static array as a buffer, and then pass that buffer to be used
>> as
>> scratch space in a function (which is possibly virtual).
>>
>> This would be my golden use case that has to not allocate anything and
>> has
>> to work in order for any solution to be viable.
>>
>> Saying all reference types are noscope would prevent this, no?
>
> Allocation only happens when a stack variable reference escapes via a
> delegate. A static array is not a stack variable, therefore the compiler
> doesn't care if it escapes.
A static array declared on the stack absolutely is a stack variable.
An example (from Tango's integer to text converter):
char[] toString (long i, char[] fmt = null)
{
char[66] tmp = void;
return format (tmp, i, fmt).dup;
}
Without the dup, toString returns a pointer to it's own stack. With a full
graph analysis, it can be proven that tmp doesn't escape, but without either
that or some crazy scope scheme, it would either allocate a closure, or fail
to compile. Neither of those options are acceptable.
>>>>> An argument to a function declared as a prototype always escapes by
>>>>> default. It may be possible for the compiler to export some meta-info
>>>>> along with the prototype when a .di file is generated, whether an
>>>>> argument is guaranteed to not escape, or maybe even detailed info
>>>>> about
>>>>> which argument escapes where, to mimic the compile-time meta-info.
>>>>
>>>> No, the di file might not be auto-generated. You also now back to a
>>>> separate import and source file, like C has. I think in order for this
>>>> to
>>>> work, the graph and object code must be stored in the same file that is
>>>> imported.
>>>
>>> There are separate import files. Actually compiler can simply put
>>> scope/noscope for the arguments based upon the meta-data collected
>>> during compilation. If your .di is manually created, you either put
>>> them manually as well, or you don't care.
>>
>> I think the graph has to be complete for this to be usable. Otherwise,
>> it
>> becomes an unused feature. Using .di files is optional. I generally
>> don't
>> use them.
>
> For the incomplete graph to be usable, the compiler must assume the worst
> for nodes with absent meta-info. Therefore if you don't care to provide
> meta-info for your modules, it'll still work, though not as efficiently.
> On the other hand, if you supply .di files with your library and you do
> care enough, or you generate your .di files automatically, the meta-info
> will be present there saving some allocations for the user.
This doesn't cover virtual functions or runtime-determined delegates. I'd
rather just have a separate meta file or have the meta data included in the
object file. What is wrong with that? Why must it be in the .di file? If
the compiler always generates these meta files, then the graph is always
complete.
>
>>>>> The expression graph analysis should be the first step towards safe
>>>>> stack closures.
>>>>
>>>> I would agree with this. But I don't think it's happening in the near
>>>> future. And I hope it's not done through .di files.
>>>
>>> You can limit analysis to a single module for now. This will cover
>>> local function calls, including some local method calls, and I hope
>>> it'll also cover template function calls which means std.algorithm will
>>> work without memory allocation again.
>>
>> Yes, but not class virtual methods or interface methods. These are used
>> quite a bit in Tango. End result, not a lot of benefit.
>
> If those virtual and interface methods are often used with function-local
> delegates as parameters then yes, the benefit wouldn't be that
> significant.
> Are you sure this is the case with Tango?
Any time you use opApply (and opApply is virtual), you are doing this. I
suppose opApply is a special case, and can be failed if you save the
delegate somewhere. But what about being able to pass the delegate to
another virtual function while inside your opApply?
Here is another example from Tango that isn't used via foreach:
final bool putCache (char[] key, IMessage message)
{
void send (IConduit conduit)
{
buffer.setConduit (conduit);
writer.put (ProtocolWriter.Command.Add, name_, key,
message).flush;
}
// return false if the cache server said there's
// already something newer
if (cluster_.cache.request (&send, reader, key))
return false;
return true;
}
cluster_.cache is a class.
-Steve
More information about the Digitalmars-d
mailing list