Optimizing by compiler

Discussions about Coding and Scripting
User avatar
Barbie
Godlike
Posts: 3225
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Optimizing by compiler

Post by Barbie »

I noticed that if a class is defined in a project in an UC file but never used as a class within the source code in another project file, the compiler left it out. I stumbled over that by assigning that class via SetPropertyText():
MapPatcher code for map "MH-Incursion+":

Code: Select all

if (SpecialControlCannon.GetPropertyText("Fire_ProjectileClass") == "Class'UnrealI.RazorBlade'") // #=5
	if (class'SharedCode'.static.SetPropertyTextEx(SpecialControlCannon, "Fire_ProjectileClass", "Class'MapPatcherSvr.RazorBladeSB'", CRespectCase))
		PatchCount++;
It does NOT work in runtime when 'RazorBladeSB' is not used as a class in the source code. If I just add local RazorBladeSB WhatEver;, the replacement in runtime works.
(SetPropertyTextEx() is the same as SetPropertyText() but returns FALSE if assigning didn't work.)
"If Origin not in center it be not in center." --Buggie
Buggie
Godlike
Posts: 3449
Joined: Sat Mar 21, 2020 5:32 am

Re: Optimizing by compiler

Post by Buggie »

Compiler not optimize anything. In fact compiler pretty simple and not do any optimizations at all.
Even do all casts even which not need. Only in 469e start some changes about that - compiler remove unnecessary casts for constants and put final constant into code.

What described is linker optimization.
Linker in game load only things, which linked in some way. So before assign something via text, which not mention before, you need make dynamic load for this class.

You can do own wrapper which try set text value with check after, if it turn into none, then dynamic load object and try again.
User avatar
Barbie
Godlike
Posts: 3225
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Optimizing by compiler

Post by Barbie »

Buggie wrote: Fri Nov 22, 2024 9:19 am So before assign something via text, which not mention before, you need make dynamic load for this class.
Thanks for answer. It also works - as described above - when I add an unused local variable of that type.
"If Origin not in center it be not in center." --Buggie
Buggie
Godlike
Posts: 3449
Joined: Sat Mar 21, 2020 5:32 am

Re: Optimizing by compiler

Post by Buggie »

Compiler not optimize anything. So unused local variable still create dependency. And force linker to load specified class.
That how "preload" in UT2004 work - they simple list used classes in function, which never called.

Unused function with "preload" in name is better approach, since not trigger compiler warning about unused local variable.
User avatar
sektor2111
Godlike
Posts: 6539
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Optimizing by compiler

Post by sektor2111 »

Bump here with something which you might want to see...
Buggie is right somehow... but optimizing the code might deliver different results...

If you guys could not manage XC_Engine, it's not my loss, it's yours. XC_Engine has a clocking system where you can check the performance for executions. It was fascinating to see how people were following the SLOWEST solution and claiming that code was "changed for speed".
Let me see a foreach AllActors C++ native iterator compared to a linked list in UScript using 3 methods: "for" cycle, "While" and "do-until" like here:

Code: Select all

class Paths_Reporter expands Mutator;

function Mutate(string MutateString, PlayerPawn Sender)
{
	local navigationpoint n;
	local float Time[2];
	local int np;

	if ( MutateString ~= "nodes" )
	{
		class'XC_CoreStatics'.static.Clock(Time);
		foreach AllActors ( class 'NavigationPoint', n )
			np++;
		log("Foreach Interator counted "$np$" nodes in"@class'XC_CoreStatics'.static.UnClock(Time)@"seconds.");
			np = 0;
		class'XC_CoreStatics'.static.Clock(Time);
		for ( n = Level.NavigationPointlist; n != None; n = n.nextNavigationPoint )
			np++;
		log("For Cycle counted "$np$" nodes in"@class'XC_CoreStatics'.static.UnClock(Time)@"seconds.");
			np = 0;
		class'XC_CoreStatics'.static.Clock(Time);
			n = Level.NavigationPointlist;
		while (n != None)
		{
			np++;
			n = n.nextNavigationPoint;
		}
		log("WHILE Linked list counted "$np$" nodes in"@class'XC_CoreStatics'.static.UnClock(Time)@"seconds.");
			np = 0;
		class'XC_CoreStatics'.static.Clock(Time);
			n = Level.NavigationPointlist;
		do
		{
			np++;
			n = n.nextNavigationPoint;
		}
		until ( n == None );
		log("DO-UNTIL Linked list counted "$np$" nodes in"@class'XC_CoreStatics'.static.UnClock(Time)@"seconds.");
		np = 0;
	}

	if ( NextMutator != None )
		NextMutator.Mutate(MutateString, Sender);
}
And bellow I'm going to show results for 180 Navigation Nodes, 562 Navigation Nodes and 751 Navigation Nodes - comparing some UT 440 with UT 469d...
PerfCounter wrote: Performance Clocking Tests

UT 440
ScriptLog: Foreach Interator counted 180 nodes in 0.000015 seconds.
ScriptLog: For Cycle counted 180 nodes in 0.000026 seconds.
ScriptLog: WHILE Linked list counted 180 nodes in 0.000012 seconds.
ScriptLog: DO-UNTIL Linked list counted 180 nodes in 0.000011 seconds.

ScriptLog: Foreach Interator counted 562 nodes in 0.000027 seconds.
ScriptLog: For Cycle counted 562 nodes in 0.000084 seconds.
ScriptLog: WHILE Linked list counted 562 nodes in 0.000042 seconds.
ScriptLog: DO-UNTIL Linked list counted 562 nodes in 0.000038 seconds.

ScriptLog: Foreach Interator counted 751 nodes in 0.000025 seconds.
ScriptLog: For Cycle counted 751 nodes in 0.000100 seconds.
ScriptLog: WHILE Linked list counted 751 nodes in 0.000044 seconds.
ScriptLog: DO-UNTIL Linked list counted 751 nodes in 0.000040 seconds.

UT 469d

ScriptLog: Foreach Interator counted 180 nodes in 0.000016 seconds.
ScriptLog: For Cycle counted 180 nodes in 0.000027 seconds.
ScriptLog: WHILE Linked list counted 180 nodes in 0.000014 seconds.
ScriptLog: DO-UNTIL Linked list counted 180 nodes in 0.000014 seconds.

ScriptLog: Foreach Interator counted 562 nodes in 0.000028 seconds.
ScriptLog: For Cycle counted 562 nodes in 0.000080 seconds.
ScriptLog: WHILE Linked list counted 562 nodes in 0.000036 seconds.
ScriptLog: DO-UNTIL Linked list counted 562 nodes in 0.000035 seconds.

ScriptLog: Foreach Interator counted 751 nodes in 0.000028 seconds.
ScriptLog: For Cycle counted 751 nodes in 0.000103 seconds.
ScriptLog: WHILE Linked list counted 751 nodes in 0.000049 seconds.
ScriptLog: DO-UNTIL Linked list counted 751 nodes in 0.000046 seconds.
In ONLY ONE case UT 469d delivered a faster result using linked list. Mutator is using the same compilation, nothing was changed during testing stage.

Go figure out amazing codes like:

Code: Select all

for (P = Level.PawnList; P != None; P = P.nextpawn)
and

Code: Select all

for (N = Level.NavigationPointlist; N != None; N = N.nextNavigationPoint)
happily embedded in BotPack and some MonsterHunt variants as being used "for speed". These "FOR" cycles are just a mystical performance improvement - it's exactly the opposite direction - the slowest execution time compared even to other UScript solutions. The most entertaining part, compiler has nothing to do with these "coding" habits. Look well at 751 case: "do-until" vs "for" cycle it's 2 times different and UT widely uses "Level.PawnList" in "FOR" cycles for all sort of actions + Level.NavigationPointlist, yes, it's "BotPack.u".
Perhaps you understand that rewriting main functions are helping you more than using stock functions and calls to such a super-class.

Switching gear:
Someone pointed me that using more codes in "CheckReplacement" will result in a loss of performance. I'll bet on 10$ that codes can be different written and then... execution will be faster than in stock codes using the same "Compiler" which is restrictive to variables changed X times in C++ but UScript should not touch that "treasure" calling them "CONSTANTS" - it's a dumb rule for the sake of having rules and pain.