Whole Tomato Software Forums
Whole Tomato Software Forums
Main Site | Profile | Register | Active Topics | Members | Search | FAQ
User name:
Password:
Save Password
Forgot your password?

 All Forums
 Visual Assist
 Technical Support
 Find References is very slow and wrong in UE4
 New Topic  Reply to Topic
 Printer Friendly
Author Previous Topic Topic Next Topic  

mparker
New Member

USA
7 Posts

Posted - Nov 15 2023 :  6:45:21 PM  Show Profile  Reply with Quote
This is something my team has been struggling with for months or years, but I couldn't find a similar topic about it. Decided to finally ask, so forgive my inability to pin down when it might have started, but it's "for as long as I can remember" now. We work on a large game project in Unreal Engine 4.27.2 with VS 2022 (currently around 17.6-17.7) and VAX 2502. I made this writeup back in February 2023, when version numbers were a little lower, but reproduced the same results today.

quote:
I have this non-static, non-virtual, non-template function inside a class in an engine plugin we wrote:
bool Advance(float InDeltaTime);

It's a great candidate for FindReferences because it's a generic name, which makes text search hard to wade through, and there's nothing complicated about it. It even has a simple parameter that might disambiguate it from other things called Advance. Let's see how the search went.
The search took nearly 11 minutes without me stopping it early, during which VS froze several times, and I worried it would crash. At one point I swear I saw the progress bar move slightly backwards.
I searched from the declaration in the header, and it took over 4 minutes before the definition in the cpp was found.
Here is the entire list of results that is not from the header and cpp of this class:
P:\MyDepot\MyWorkspace\Engine\Platforms\Win\Source\ThirdParty\MyCompany\Windows Kits\10\Include\10.0.19041.0\um\MsHTML.h
	(168807):    ( (This)->lpVtbl -> Advance(This) )
P:\MyDepot\MyWorkspace\Engine\Plugins\FX\Niagara\Source\Niagara\Private\NiagaraDataInterfaceSpline.cpp
	FindClosestUnitDistanceFromPositionWS (983):    PosZParam.Advance();
	FindClosestUnitDistanceFromPositionWS (999):    PosZParam.Advance();
P:\MyDepot\MyWorkspace\Engine\Source\Runtime\Core\Public\Math\GenericOctree.h
	(15):    for(FOctreeChildNodeRef ChildRef(0);!ChildRef.IsNULL();ChildRef.Advance())
P:\MyDepot\MyWorkspace\Engine\Source\Runtime\Engine\Private\Particles\ParticleSystemRender.cpp
	RenderDebug (6264):    RenderData.Advance();
	FillVertexData (6469):    RenderData.Advance();
P:\MyDepot\MyWorkspace\Engine\Plugins\Experimental\Water\Source\Runtime\Private\NiagaraDataInterfaceWater.cpp
	GetWaterDataAtPoint (226):    WorldX.Advance();
	GetWaterDataAtPoint (227):    WorldY.Advance();
	GetWaterDataAtPoint (228):    WorldZ.Advance();
	GetWaterDataAtPoint (229):    Time.Advance();
P:\MyDepot\MyWorkspace\Engine\Platforms\Win\Source\ThirdParty\MyCompany\Windows Kits\10\Include\10.0.19041.0\um\Mshtmlc.h
	(168807):    ( (This)->lpVtbl -> Advance(This) )

As you can see, literally 0 of these even match the signature of this function!
In the end, I did sift through a text search of 779 matching lines, because I obviously don't trust these results, and indeed the function is only used in the header and the cpp (it's public, so I couldn't have assumed this). It took me a lot less than 11 minutes to do the unpleasant manual option.

My assumption after reading some of the forums and this not being a top issue is that other people must not be seeing this. The PC the search is running on has a 32-core (64 logical core) 3.5 GHz processor, 128 GB of RAM, and both VS and the project directory on M.2 NVMe drives. The solution has been fully parsed by VAX, including indexing of All plugins (not Referenced). The attempt today actually took over 14 minutes, so it's gotten anecdotally worse since 9 months ago. I'd be happy to answer any questions I can about our setup that might lead to this functionality being useful for my team again. Thanks for reading.

feline
Whole Tomato Software

United Kingdom
19023 Posts

Posted - Nov 16 2023 :  08:49:05 AM  Show Profile  Reply with Quote
That's just terrible, apologies for this!

In the Find References Results window, what icons are shown at the start of the result lines? I am guessing some of these results have a question mark icon, which means that VA isn't able to properly resolve the code, and is just guessing that these might be matching results.

You can filter out these results via right clicking in the results window and toggling:

Display unknown/guess hits (G)

in the context menu.

Is this function actually called anywhere in your code? I am assuming it is, but want to confirm that assumption.

Are the results from the header file and matching cpp file, when they appear, correct? Or are you also seeing problems there?

For the speed, my normal plan would be to change the Find References settings to exclude the Unreal Engine project, but that isn't going to work since you are working inside the UE project, so I assume you need to search the entire UE project. Is that correct?

Since Find References is designed to be syntax aware, it is slower than a simple text search. Normally its still fast enough, and the accuracy makes up for the slowness, but that isn't working well here.

zen is the art of being at one with the two'ness
Go to Top of Page

mparker
New Member

USA
7 Posts

Posted - Nov 16 2023 :  11:53:39 AM  Show Profile  Reply with Quote
I appreciate the quick response, and I'm glad to hear that's not expected.

I can confirm all of the spurious results in the window have a question mark icon. The declaration in the header and the definition in the cpp both have hollow purple cubes. It is called in one place in the cpp, which has a white dot-inside-circle, and there is one comment with the function name in it, which is a green open circle. I can also confirm that disabling "Display unknown/guess hits" removes all the question mark ones. My concern there would be, can those ever be false "negatives"? As in, I don't want to miss any references that are *correct* guesses.

This is an aside, but I had no idea there was a context menu in that window that had options not shown as buttons... I also had assumed it was searching both the engine and game projects, but now realize it was only searching the engine project for that amount of time. It was a surprising behavior to search current project rather than whole solution (I can see why, just was never obvious to me before. I'd probably know that if I used this every day.)

The results from the header and cpp are correct, and all show appropriate code in the tooltips as well (except the comment, maybe the tooltips filter out the comments so there's an empty space where the comment block would be).

And yes, we definitely need to be able to search the UE4 project itself. We have one team that largely works on engine plugins and another that largely works in a game project, and I can see where a game project function won't be called in the engine so there's no need to search it. I'm on that engine plugin team, so no such relief for us. If anything, I need to be searching the game project as well. :)

One thing to note, while the search is running, the status bar(?) in the bottom left of VS where it describes something that's happening rapidly alternates between "Ready" and "VA: Creating instance of template :SomeTemplateClass<SomeOtherClass>". Nothing is in the Background Tasks window if I actually click the icon there. Whatever that is indicating may explain the whole problem, but I would still need to know if doing that is necessary or configurable.
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
19023 Posts

Posted - Nov 16 2023 :  1:18:34 PM  Show Profile  Reply with Quote
Comments should be shown in the results list as well, but they can be turned On and Off via:

Display comment and string hits (M)

in the context menu. If your system is set to show comments, but they are not showing up, then that's a bug.

If it helps, there is a "Find References in File" command, which does a syntax aware find, but just for the current file. But I am not sure how helpful that would be for you.

For guess hits missing valid code, it is possible. It should be very rare, unless you are doing things that are parser cannot understand. If you do have an example of that happening I would be more than happy to look at it, to try and find a fix, or at least get a bug report in for it.

For the search its self, I started a test Find References on all projects on UE 4.27 here, and it is definitely slow. Partly though that is simply due to the large amount of code that has to be parsed and searched. We have a database of symbol definitions, so jumping to where something is defined is fast, we just look it up. But we don't attempt to make a database of every use of every symbol, so we have to actually scan and parse your code to populate the Find References Results list.

If you are working on a game using UE then the advice would be to turn Off searching all projects, so the Find References does not search inside the engine its self, but yes, that doesn't help in this situation. I assume since the plugin is being used widely, you want to search the entire engine? Or do you only need to search a smaller segment? We would need to make a code change for a smaller search range, but depending on how well defined that would be, I could certainly put in a feature request.

One thing I can suggest is cloning a Find References Results window once the find has finished. There is a toolbar button for this. This way you don't risk loosing the results it took quite a while to find, since that would be deeply unhelpful.

For the template instances, since VA doesn't get to compile the code, it has to try and expand the templates its self when parsing the code, so it can work out if the symbol being referenced is the correct type or not. It's not showing up in the background task window, since it is a VA parser process, but we are giving you some feedback on the status bar so you aren't completely clueless as to what we are doing, and why the search is taking a while.

zen is the art of being at one with the two'ness
Go to Top of Page

mparker
New Member

USA
7 Posts

Posted - Nov 16 2023 :  6:57:02 PM  Show Profile  Reply with Quote
For the comment, what I meant was there is a list entry for a comment (which is correct), but the tooltip that shows the code snippet when you hover over the list entry appears to itself filter out comments. So when you hover over a comment, the snippet that's shown in the tooltip doesn't actually show the reference. Minor issue unrelated to the actual results.

I don't have any example of guesses that should have been parsed as non-guesses, so that gives me some faith I can disable showing those and still be confident in the results.

Unfortunately, Find References in File and a theoretical smaller segment don't really align with the usage we're looking for. It happened to be in this case that the function was only declared in the header, implemented in the cpp, and called once in the cpp, so here it wasn't being "used widely" at all, but I don't know that until I search. Often that's exactly what I'm trying to find out.

I have 2 kind of ideas here, and obviously I don't work on VA so I have no idea how feasible they are. The first one is that this function is in a class that's being exported to other modules in Unreal via the MYMODULE_API macro inbetween "class" and "MyClassName". This macro could also be present on specific functions rather than the entire class to export things selectively. Now, assuming module dependencies are set up correctly, any other module that includes something from this module needs to declare it as a dependency in its *.Build.cs file. So maybe it would be possible to first check if that macro is on the function or class, and if so, go find all the modules that depend on this one, and only consider all the files in this module and those modules for possible hits. If it's not exported with that macro, then the search could be limited to only the current module. If that logic or something close to it is a sound principle, it would massively cut down the number of files to search. This module is depended on by 3 other engine modules, 1 game module, and 4 game plugin modules, which is comparatively very small. I realize this grows in complexity as I could do this operation from one of those modules and you have to find its origin module first, but it still seems worth investigating.

The other idea is related to the template instances. I don't have visibility into how many templates are instanced or how VA decides on that, but I would want to look into limiting that to only things that are possible matches. So if I'm looking for references to a member function called Advance, I wouldn't need to instance thousands of templates (probably), it would just need to find calls to any function called Advance (ideally in the subset of files from idea #1), and if they're being called as the member of a template class instantiation, try and back up to where I can find if the type in that template is the type I'm looking for. Basically doing it in reverse instead of forward. There are only 779 matching lines in the whole solution, and potentially only a small number of those are ever being called by a template instance of anything.

Again, I realize how it sounds to say "I would simply do this" when I'm sure there are huge challenges and obstacles in every idea. But I don't think anyone on my team could return to using this functionality without a ballpark 100x increase in speed (about 10 second searches or less), so my suggestions are attempts at ways to do 1/100th of the work, or less. I also understand if it's not possible, though I am hopeful something can be done. I imagine many Unreal devs working in the engine layer are suffering from the same issue.
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
19023 Posts

Posted - Nov 17 2023 :  11:17:35 AM  Show Profile  Reply with Quote
The comments, can you please try a Find Reference in File on the function name in this very simple test case, to see what happens with tooltips and comments? For me the comment lines also show tooltips, which are centred on the comment, so showing the comment in code context:

// name of function is "simpleFindRefFuncInComment"
void simpleFindRefFuncInComment()
{
	// now we are inside of the function called simpleFindRefFuncInComment - check tooltips on find references results
}


For how VA handles templates, I would certainly hope we are only checking templates that potentially lead to a result we need to check, but I don't know that for a fact.

Checking the dependencies is interesting. I know basically nothing about the structure of the Engine myself, but I can see how this would help to limit the scope of searching for plugins considerably. Is the engine also composed of modules like this, or is more of one giant block?

Doing a file search for "*.build.cs" files across my UE 4.27 directory tree is giving me 1,302 files, so it certainly suggests the engine can be viewed as lots of modules.

I have just opened 6 random header files from UE 4.27, just to look, and 2 of the header files have an _API macro between the class keyword and class name, but the other 4 don't. So this idea is only going to help so far. Am I correct in assuming that all plugins follow this pattern though, so this would at least help for plugin developers?

I agree that the run time for a Find References across the entire engine is just crazy, so I can see why you would never bother. Without some well defined way to shrink right down the volume of code to check, I am not sure what we can really do about this though. A syntax aware search basically has to parse everything, which just isn't a good plan with this much code.

zen is the art of being at one with the two'ness
Go to Top of Page

mparker
New Member

USA
7 Posts

Posted - Nov 17 2023 :  12:52:19 PM  Show Profile  Reply with Quote
For the comment, that one indeed does show perfectly fine in the tooltip. The one I was seeing that didn't show is part of a pretty large /* comment block */, so I tried taking your comment above the function name and just making it longer. When I make it this long, it stops showing at all in the tooltip.
/* name of function is "simpleFindRefFuncInComment"
 * 
 * 
 * 
 *
 */


For the modules thing, I'm not an Epic developer so you would need to do some research to verify what I'm saying, but yes, to my knowledge the entire engine (though not necessarily its Programs) and all plugins and game projects are organized as collections of modules. The latest documentation on this is here, and there's a dropdown in the top-left to look at versions back to 4.27 if there are any changes: https://docs.unrealengine.com/5.3/en-US/unreal-engine-modules/. Here are some key parts:

"All projects and plugins have their own primary module by default, however, you can define other modules outside of these to organize your code."
"List your module as a dependency in the Build.cs file for any module that will need to use it. This may include the Build.cs file for your project's primary module."
"All modules require a [ModuleName].Build.cs file placed in the module's root directory for the Unreal build system to recognize them."

Then this part is important for understanding which modules can see code from other modules:
"You should use the PublicDependencyModuleNames list if you use the classes from a module publicly, such as in a public .h file. This will make it possible for other modules that depend on your module to include your header files without issues.
You should put a module's name in the PrivateDependencyModuleNames list if they are only used privately, such as in .cpp files. Private dependencies are preferred wherever possible, as they can reduce your project's compile times."

Worth noting that ModuleRules.cs also has things like DynamicallyLoadedModuleNames that may need to be checked as well.

My understanding of the _API macro is less solid, but I believe it's used to export functions to other modules when their dependencies are set up via the Public/PrivateDependencyModuleNames (static linking). I think that the macro can be applied per function, or when it's applied to a class name, it applies to all the (public?) functions. Unsure on public member variables. But it means that in theory, a class that lacks the macro on itself or any of its functions can only be used within its own module, which reduces the search possibilities drastically (also assuming no modules list it as a dynamically loaded dependency, which wouldn't require the macro). So it's a good case when the macro is missing, if I understand correctly.

In the example I started the post with, I mentioned the module containing the class containing the function I was looking for is depended on by 8 modules, so 9 including itself. It has the macro on the class, so the function is exported. No modules depend on it dynamically (I don't think we do that almost at all...). So a search of all the Build.cs files for this module name would give 9 modules to search in the whole solution (including all projects), rather than... looks like 1,812 modules on my end.

I hope there's some possibility that sounds doable, or like a worthwhile investigation. I would be happy to test such a feature for you, though I'm sure your internal testing on something like the Lyra sample game would flush out all the accuracy and speed concerns when trying to find engine-level types.
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
19023 Posts

Posted - Nov 21 2023 :  11:52:56 AM  Show Profile  Reply with Quote
The comment tooltip bug is unexpected, but easy to reproduce now I know what to look for. I have put in a bug report for this:

case=163870

I need to do a bit of reading on the modules, but this sounds like it is at least worth reading up on. We have updated VA to check for referenced plugins, so this would be a logical extension. It is a question of how complex this will get, but I would at least like to try to put together a clear feature request to be considered.

zen is the art of being at one with the two'ness
Go to Top of Page

mparker
New Member

USA
7 Posts

Posted - Nov 21 2023 :  1:22:40 PM  Show Profile  Reply with Quote
I'm glad to hear it! If there are any more questions about the structure and scope of our project or our common use cases, I'm happy to answer what I can. Otherwise, I have notifications on for replies here, so I look forward to anything you have to share (though I know it may be a while). Feel free to email me as well, I'm happy to test any proposed changes on my end.
Go to Top of Page

feline
Whole Tomato Software

United Kingdom
19023 Posts

Posted - Dec 05 2023 :  11:29:00 AM  Show Profile  Reply with Quote
Apologies for the delay, I got back to this finally, and the documentation on UE modules does suggest this is a useful way to limit the search scope. Obviously there will be times where this won't help at all, since you basically need to search everything, but if we can limit the search a lot, it would make the find a lot faster.

So I have put in a feature request to see what our developers think of this, but it sounds like it will take a bit of work, so no promises. Still, it could help quite a lot in some situations:

case=163938

zen is the art of being at one with the two'ness
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
 Printer Friendly
Jump To:
© 2023 Whole Tomato Software, LLC Go To Top Of Page
Snitz Forums 2000