Regarding TObjectPtr performance regression

When 5.0 first introduced TObjectPtr, the guidance was that it would compile down to raw pointer access in Shipping builds to ensure identical performance.

Evidenced by the following quote from the documentation for 5.0:

UE5 introduces TObjectPtr, a template-based, 64-bit pointer system, as an optional replacement for raw object pointers in editor builds. This system adds dynamic resolution and access tracking in editor builds, while performing identically to raw pointers in non-editor builds.

In recent versions of the engine, this guarantee seems to have been silently dropped.

The introduction of incremental reachability analysis adds code to call ConditionallyMarkAsReachable, regardless of whether incremental GC is turned on.

This causes assembly instructions comparable to the following to be generated inline every time a TObjectPtr or TSubclassOf is assigned:

cmp         byte ptr [UE::GC::GIsIncrementalReachabilityPending],r14b
je          _____+16Fh
mov         rcx,qword ptr [rbx+458h]
test        rcx,rcx
je          _____+16Fh
call        UE::GC::MarkAsReachable

In our game, this adds several megabytes to our compiled binary size, putting pressure on the instruction cache and branch predictor.

Even though incremental GC is experimental and not even supported outside of single-threaded servers, right now every game is paying for the feature even when not enabled, due to `UE_OBJECT_PTR_GC_BARRIER` being unconditionally set to `1` in `ObjectPtr.h`.

My questions are the following:

- It would be nice if this code was automatically compiled out when incremental GC is off, instead of us having to randomly find out about the define during profiling.

- This seems to unconditionally apply to every TSubclassOf as well, even when used as a parameter/local variable and GC barriers are unnecessary.

- Inlining the check to `GIsIncrementalReachabilityPending` makes sense, but it might be worth moving the null object check into `MarkAsReachable` itself, which would reduce the binary size cost of the repeatedly inlined code.

Hello!

I logged a bug report so the engine team investigates the problem. You will soon be able to follow its state here: https://issues.unrealengine.com/issue/UE\-386406

For now, you can work around the problem by defining the macro in the target rules of the runtime:

//In Project.Target.cs
 
public class ProjectTarget : TargetRules
{
	public ProjectTarget( TargetInfo Target) : base(Target)
	{
		GlobalDefinitions.Add("UE_LOG_INCLUDE_SOURCE_LOCATION=0");
	}
}

Regards,

Martin