• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Latest patch doesn't inline?

Status
Not open for further replies.
Level 15
Joined
Nov 30, 2007
Messages
1,202
I'm sitting on the latest wc3 patch and when testing my code methods that should be inlined are not for me, where as for Overfrost who sits on older patch it seem to work fine. Could someone confirm this?

I've also tested this by removing the use of table and i hit on the OP limit much later, if the method inlined properly there would be no difference between using Table and not using Table in this regard.

Since Overfrost didn't believe me when I said that Table doesn't inline I now have proof that method operators don't inline, or am I reading this result wrong? (I didn't actually look at Table but the principle is the same).

JASS:
        method operator[] takes integer index returns $TYPE$
            return Load$VAR$(HashRecyler.ht , this, index)
        endmethod

        method operator[]= takes integer index, $TYPE$ element returns nothing
            call Save$VAR$(HashRecyler.ht , this, index, element)
        endmethod
private method reverseInner takes integer first, integer last returns nothing
            if first < last then
                // this section should be inlined
                set other = this[first]
                set this[first] = this[last]
                set this[last] = other
                call .reverseInner(first + 1, last - 1)
            endif
        endmethod

        method reverse takes nothing returns nothing
            //call reverseInner(0, .length)
            set up = 0
            set down = .length - 1
            loop
                exitwhen down <= up
                set other = this[up]
                set this[up] = this[down]
                set this[down] = other
                set up = up + 1
                set down = down - 1
            endloop
        endmethod

Translates into:

JASS:
 // Setter
 function s__IntArrayList__getindex takes integer this,integer index returns integer
            return LoadInteger(s__HashRecyler_ht, this, index)
  endfunction
 
    // Getter
    function s__IntArrayList__setindex takes integer this,integer index,integer element returns nothing
        call SaveInteger(s__HashRecyler_ht, this, index, element)
    endfunction


 function s__IntArrayList_reverseInner takes integer this,integer first,integer last returns nothing
            if first < last then
                set s__IntArrayList_other=s__IntArrayList__getindex(this,first)
                call s__IntArrayList__setindex(this,first, s__IntArrayList__getindex(this,last))
                call s__IntArrayList__setindex(this,last, s__IntArrayList_other)
                call s__IntArrayList_reverseInner(this,first + 1 , last - 1)
            endif
  endfunction

  function s__IntArrayList_reverse takes integer this returns nothing
            //call reverseInner(0, .length)
            set ArrayList__up=0
            set ArrayList__down=s__IntArrayList__get_length(this) - 1
            loop
                exitwhen ArrayList__down <= ArrayList__up
                set s__IntArrayList_other=s__IntArrayList__getindex(this,ArrayList__up)
                call s__IntArrayList__setindex(this,ArrayList__up, s__IntArrayList__getindex(this,ArrayList__down))
                call s__IntArrayList__setindex(this,ArrayList__down, s__IntArrayList_other)
                set ArrayList__up=ArrayList__up + 1
                set ArrayList__down=ArrayList__down - 1
            endloop
  endfunction
This doesn't look like inlining to me.

Which also explains why using method operators over directly calling the hashtable reaches the OP limit much sooner.

It's your compiler mate, probably your settings. Here's what happens for me.

JASS:
scope Test initializer Init

private function Init takes nothing returns nothing
    local IntArrayList list = IntArrayList.create()
    call list.push(0xF)
    call list.push(0xFF)
    set list[0] = list.last()
    set list[list.length - 1] = list.first()
endfunction

endscope
Compiled to:
JASS:
//===========================================================================
// scope Test begins

function Test___Init takes nothing returns nothing
    local integer list= (s__HashRecyler_alloc()) // INLINED!!
    call s__IntArrayList_push(list,0xF)
    call s__IntArrayList_push(list,0xFF)
    call SaveInteger(s__HashRecyler_ht, (list), (0), ( s__IntArrayList_last(list))) // INLINED!!
    call SaveInteger(s__HashRecyler_ht, (list), ((LoadInteger(s__HashRecyler_ht, (list), ArrayList___SIZE_INDEX)) - 1), ( (LoadInteger(s__HashRecyler_ht, (list), 0)))) // INLINED!!
endfunction

// scope Test ends
Just to clarify, I used your lib without any changes.
 
I had this problem so I went to the JassHelper drop-down menu in the Trigger Editor and checked the box for optimizations. Once I had the optimizations running on save, then everything was inlining again.

Does this work for you?

I was using the standard Patch 1.30.4 World Editor with no modifications.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
I had this problem so I went to the JassHelper drop-down menu in the Trigger Editor and checked the box for optimizations. Once I had the optimizations running on save, then everything was inlining again.

Does this work for you?

I was using the standard Patch 1.30.4 World Editor with no modifications.

I've tested again and made sure the optimizations was toggled on there is still no difference. Here is an example if you want to test for yourself:

JASS:
scope Sorting initializer Init

    globals
        private constant integer NUM_UNITS = 1000  // CHANGE THIS
        private constant boolean USE_TABLE = true  // CHANGE THIS
        private Table table
        private hashtable ht = InitHashtable()
        private integer up
        private integer down
        private trigger t
        private unit pivot
        private unit other
    endglobals

 
    private function quicksort takes integer first, integer last returns nothing
        if first < last then
            set up = first
            set down = last
            set pivot = table.unit[first]
            loop
                loop
                    set other = table.unit[up]
                    exitwhen up >= last or TriggerEvaluate(t)
                    set up = up + 1
                endloop
                loop
                    set other = table.unit[down]
                    exitwhen  not TriggerEvaluate(t)
                    set down = down - 1
                endloop
                exitwhen up >= down
                set other = table.unit[up]
                set table.unit[up] = table.unit[down]
                set table.unit[down] = other
            endloop
            set other = table.unit[first]
            set table.unit[first] = table.unit[down]
            set table.unit[down] = other
            call quicksort(first, down - 1)
            call quicksort(down + 1, last)
            endif
    endfunction
 
    private function quicksort2 takes integer first, integer last returns nothing
            if first < last then
                set up = first
                set down = last
                set pivot = LoadUnitHandle(ht , 0, first)
                loop
                    loop
                        set other = LoadUnitHandle(ht , 0, up)
                        exitwhen up >= last or TriggerEvaluate(t)
                        set up = up + 1
                    endloop
                    loop
                        set other = LoadUnitHandle(ht , 0, down)
                        exitwhen  not TriggerEvaluate(t)
                        set down = down - 1
                    endloop
                    exitwhen up >= down
                    set other = LoadUnitHandle(ht , 0, up)
                    call SaveUnitHandle(ht, 0, up, LoadUnitHandle(ht, 0, down))
                    call SaveUnitHandle(ht, 0, down, other)
                endloop
                set other = LoadUnitHandle(ht , 0, first)
                call SaveUnitHandle(ht, 0, first, LoadUnitHandle(ht, 0, down))
                call SaveUnitHandle(ht, 0, down, other)
                call quicksort2(first, down - 1)
                call quicksort2(down + 1, last)
                endif
    endfunction
 
    function Reorder takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i == NUM_UNITS
            static if USE_TABLE then
                set pivot = table.unit[i]
            endif
            static if not USE_TABLE then
                set pivot = LoadUnitHandle(ht, 0, i)
            endif
            call SetUnitX(pivot, i*15 - 1500)
            if i > 1000 then
                call SetUnitY(pivot, 500)
            else
                call SetUnitY(pivot, 0)
            endif
            set i = i + 1
        endloop
    endfunction
 
    function Sort takes nothing returns nothing
        if USE_TABLE then
            call quicksort(0, NUM_UNITS - 1)
        else
            call quicksort2(0, NUM_UNITS - 1)
        endif
        call ExecuteFunc("Reorder")
    endfunction

    private function compare takes nothing returns boolean
        return GetPlayerId(GetOwningPlayer(pivot)) < GetPlayerId(GetOwningPlayer(other))
    endfunction
 
    private function Init takes nothing returns nothing
        local integer i = 0
        set table = Table.create()
        loop
            exitwhen i == NUM_UNITS
            static if USE_TABLE then
                set table.unit[i] = CreateUnit(Player(GetRandomInt(0, 23)), 'hpea', 0, 0, 0)
            endif
            static if not USE_TABLE then
                call SaveUnitHandle(ht, 0, i, CreateUnit(Player(GetRandomInt(0, 23)), 'hpea', 0, 0, 0))
            endif
            set i = i + 1
        endloop
        set t = CreateTrigger()
        call TriggerAddCondition(t, Condition(function compare))
        call ExecuteFunc("Sort")
    endfunction


endscope

With table it can sort 1300 while writing to the hastable directly it can sort 1900 units. If the inlining was happening correctly this difference wouldn't be there or be minimal.
 
Last edited:
So, in order to ensure mine was inlining, I did not do these performance metrics. I simply looked at the output code, and whether or not it had replaced some functions with other functions.

Have you looked at the output JASS? Later tonight when I get home I can confirm the inlining takes place by posting the before and after output of a sample script.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
So, in order to ensure mine was inlining, I did not do these performance metrics. I simply looked at the output code, and whether or not it had replaced some functions with other functions.

Have you looked at the output JASS? Later tonight when I get home I can confirm the inlining takes place by posting the before and after output of a sample script.
Example output is in the first post, I use Ladiks MPQ for that if that is any problem? If I didn't have none-inlined code I wouldn't have done the performance metric ^^

Here is the above:
JASS:
// scope Sorting begins


   
 function Sorting___quicksort takes integer first,integer last returns nothing
        if first < last then
            set Sorting___up=first
            set Sorting___down=last
            set Sorting___pivot=s__Table___units__getindex(s__Table__get_unit(Sorting___table),first)
            loop
                loop
                    set Sorting___other=s__Table___units__getindex(s__Table__get_unit(Sorting___table),Sorting___up)
                    exitwhen Sorting___up >= last or TriggerEvaluate(Sorting___t)
                    set Sorting___up=Sorting___up + 1
                endloop
                loop
                    set Sorting___other=s__Table___units__getindex(s__Table__get_unit(Sorting___table),Sorting___down)
                    exitwhen not TriggerEvaluate(Sorting___t)
                    set Sorting___down=Sorting___down - 1
                endloop
                exitwhen Sorting___up >= Sorting___down
                set Sorting___other=s__Table___units__getindex(s__Table__get_unit(Sorting___table),Sorting___up)
                call s__Table___units__setindex(s__Table__get_unit(Sorting___table),Sorting___up, s__Table___units__getindex(s__Table__get_unit(Sorting___table),Sorting___down))
                call s__Table___units__setindex(s__Table__get_unit(Sorting___table),Sorting___down, Sorting___other)
            endloop
            set Sorting___other=s__Table___units__getindex(s__Table__get_unit(Sorting___table),first)
            call s__Table___units__setindex(s__Table__get_unit(Sorting___table),first, s__Table___units__getindex(s__Table__get_unit(Sorting___table),Sorting___down))
            call s__Table___units__setindex(s__Table__get_unit(Sorting___table),Sorting___down, Sorting___other)
            call Sorting___quicksort(first , Sorting___down - 1)
            call Sorting___quicksort(Sorting___down + 1 , last)
            endif
    endfunction
   
 function Sorting___quicksort2 takes integer first,integer last returns nothing
            if first < last then
                set Sorting___up=first
                set Sorting___down=last
                set Sorting___pivot=LoadUnitHandle(Sorting___ht, 0, first)
                loop
                    loop
                        set Sorting___other=LoadUnitHandle(Sorting___ht, 0, Sorting___up)
                        exitwhen Sorting___up >= last or TriggerEvaluate(Sorting___t)
                        set Sorting___up=Sorting___up + 1
                    endloop
                    loop
                        set Sorting___other=LoadUnitHandle(Sorting___ht, 0, Sorting___down)
                        exitwhen not TriggerEvaluate(Sorting___t)
                        set Sorting___down=Sorting___down - 1
                    endloop
                    exitwhen Sorting___up >= Sorting___down
                    set Sorting___other=LoadUnitHandle(Sorting___ht, 0, Sorting___up)
                    call SaveUnitHandle(Sorting___ht, 0, Sorting___up, LoadUnitHandle(Sorting___ht, 0, Sorting___down))
                    call SaveUnitHandle(Sorting___ht, 0, Sorting___down, Sorting___other)
                endloop
                set Sorting___other=LoadUnitHandle(Sorting___ht, 0, first)
                call SaveUnitHandle(Sorting___ht, 0, first, LoadUnitHandle(Sorting___ht, 0, Sorting___down))
                call SaveUnitHandle(Sorting___ht, 0, Sorting___down, Sorting___other)
                call Sorting___quicksort2(first , Sorting___down - 1)
                call Sorting___quicksort2(Sorting___down + 1 , last)
                endif
    endfunction
   
 function Reorder takes nothing returns nothing
  local integer i= 0
        loop
            exitwhen i == Sorting___NUM_UNITS
//#             static if Sorting___USE_TABLE then 
                    set Sorting___pivot=s__Table___units__getindex(s__Table__get_unit(Sorting___table),i)
//#             endif 
//#             static if not Sorting___USE_TABLE then 
//#                 set Sorting___pivot = LoadUnitHandle(Sorting___ht, 0, i)
//#             endif
            call SetUnitX(Sorting___pivot, i * 15 - 1500)
            if i > 1000 then
                call SetUnitY(Sorting___pivot, 500)
            else
                call SetUnitY(Sorting___pivot, 0)
            endif
            set i=i + 1
        endloop
    endfunction
   
 function Sort takes nothing returns nothing
        if Sorting___USE_TABLE then
            call Sorting___quicksort(0 , Sorting___NUM_UNITS - 1)
        else
            call Sorting___quicksort2(0 , Sorting___NUM_UNITS - 1)
        endif
        call ExecuteFunc("Reorder")
    endfunction

 function Sorting___compare takes nothing returns boolean
        return GetPlayerId(GetOwningPlayer(Sorting___pivot)) < GetPlayerId(GetOwningPlayer(Sorting___other))
    endfunction 
   
 function Sorting___Init takes nothing returns nothing
  local integer i= 0
        set Sorting___table=s__Table_create()
        loop
            exitwhen i == Sorting___NUM_UNITS
//#             static if Sorting___USE_TABLE then 
                    call s__Table___units__setindex(s__Table__get_unit(Sorting___table),i, CreateUnit(Player(GetRandomInt(0, 23)), 'hpea', 0, 0, 0))
//#             endif 
//#             static if not Sorting___USE_TABLE then 
//#                 call SaveUnitHandle(Sorting___ht, 0, i, CreateUnit(Player(GetRandomInt(0, 23)), 'hpea', 0, 0, 0))
//#             endif
            set i=i + 1
        endloop
        set Sorting___t=CreateTrigger()
        call TriggerAddCondition(Sorting___t, Condition(function Sorting___compare))
        call ExecuteFunc("Sort")
    endfunction 

   

// scope Sorting ends
function InitCustomTriggers takes nothing returns nothing
    //Function not found: call InitTrig_HashRecyler()
    //Function not found: call InitTrig_ArrayListTable()
    //Function not found: call InitTrig_ArrayListExtension()
    //Function not found: call InitTrig_Table()
    //Function not found: call InitTrig_Sort()
endfunction
 
Last edited:
Level 6
Joined
Jan 9, 2019
Messages
102
Overfrost who sits on older patch it seem to work fine.
How can you state that I'm sitting on an older patch mate? Patch 1.30.3 is not that old. My test-map that is literally only for testing function inlining is maintained in v1.30.3. And as I said previously somewhere else, it's your compiler/settings.

Want some proof?
JASS:
library Tester initializer Init requires Table

private function Init takes nothing returns nothing
	local Table tab = Table.create()
	set tab[0] = 1
	set tab[1] = tab[0]
endfunction

endlibrary
Compiled to:
JASS:
//library Tester:

function Tester__Init takes nothing returns nothing
 local integer tab= s__Table_create()
	call SaveInteger(Table__ht, (tab), (0), ( 1)) // INLINED!!
	call SaveInteger(Table__ht, (tab), (1), ( (LoadInteger(Table__ht, (tab), (0))))) // INLINED!!
endfunction


//library Tester ends
 
Status
Not open for further replies.
Top