-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance of Cursor's CursorChildren (CXCursor.VisitChildren) #260
Comments
I've since realized that the fix is on the C# side, so here's a proof of concept solution ( In our application this cuts the total time to parse and traversing an entire AST via Cursor.Children by 40%, which demonstrates how significant of a penalty private class CursorChildrenClientData
{
public List<Cursor> Cursors;
public TranslationUnit TranslationUnit;
}
private static unsafe CXCursorVisitor CursorChildrenDelegate = delegate ( CXCursor child, CXCursor parent, void *pClientData )
{
GCHandle cursorsHandle = GCHandle.FromIntPtr( new IntPtr( pClientData ) );
CursorChildrenClientData clientData = (CursorChildrenClientData)cursorsHandle.Target;
Cursor orCreate = TranslationUnit_GetOrCreate.Value( clientData.TranslationUnit, child );
clientData.Cursors.Add( orCreate );
return CXChildVisitResult.CXChildVisit_Continue;
};
private static IntPtr CursorChildrenDelegatePointer = Marshal.GetFunctionPointerForDelegate( CursorChildrenDelegate );
private static unsafe IReadOnlyList<Cursor> CursorChildrenFastNoCache( Cursor cursor )
{
CursorChildrenClientData clientData = new CursorChildrenClientData();
clientData.Cursors = new List<Cursor>();
clientData.TranslationUnit = cursor.TranslationUnit;
GCHandle clientDataHandle = GCHandle.Alloc( clientData );
clang.visitChildren( cursor.Handle, CursorChildrenDelegatePointer, GCHandle.ToIntPtr( clientDataHandle ).ToPointer() );
clientDataHandle.Free();
return clientData.Cursors;
} |
Happy to contribute a fix which will be even faster on .NET 5+ :) |
Sorry for the delayed response, I thought I had already responded to this one. I'm happy to take performance fixes here, including by caching delegates where appropriate. This is likely a case that might also benefit from using function pointers directly, but the requisite |
Have a fix up in #272. It saves some time, but other things like getting the Spelling profiled as significantly hotter on my box so I fixed those as well. There's likely a few things that could be done differently/better here as separate work items. Initial thoughts include reducing the number of uncached P/Invokes that are "hot" and switching over to a form of "value lazy" to reduce allocations/indirections. However, we're already doing a pretty good job of caching things and there ends up being some cost here regardless given how Clang exposes everything. |
When profiling our application that uses ClangSharp, I've noticed that the
Cursor
class'sCursorChildren
lazy property evaluation takes a significant amount of time on profiles, specifically inside of theCXCursor.VisitChildren
call.It looks like 75% of that time is being spent inside of
MarshalNative::GetFunctionPointerForDelegateInternal
(of which nearly all of the time is inside ofCOMDelegate::ConvertToCallback
).Given the fact that this is likely to be a hotspot across many applications, is there an opportunity to avoid such a penalty by avoiding using the more "generic"
CXCursor.VisitChildren
in lieu of a custom native method that is P/Invoked to populate aList
of the immediate children of a givenCursor
using a native function (therefore avoiding interop of the otherwise-uninteresting delegate)?If that's feasible, am I correct in my understanding of the project hierarchy here that such code would live within the libClangSharp project?
Thanks,
Daniel Jennings
Valve
The text was updated successfully, but these errors were encountered: