Re: Optimization of a sorting algorithm
Posted: Fri Sep 29, 2023 8:14 am
I m again on ! I m digging...
1) addresses bug
2) unable to select a map element by address
1) addresses bug
2) unable to select a map element by address
http://www.purebasic.com
https://www.purebasic.fr/english/
Hello Nic,NicTheQuick wrote: Thu Sep 28, 2023 8:51 pm Can you explain in more detail the value range that `Entry` can have? ... etc

Code: Select all
"Button 1" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 2" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 3" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 4" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 5" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 6" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 7" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 8" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
"Button 9" -> (Gadget ID, Reference Gadget, X, Y, Widht, Height ... etc)
etc.Code: Select all
[...]
"Button 7" -> (3950003483, "Button 8", ... etc)
"Button 8" -> (3950003484, "Button 9", ... etc)
"Button 9" -> (3950003485, "", #Win_Center_Hor, #Win_Center_Vert, ... etc)
Q3: Why is Entry 500 at the end not before Entry 400 or even before Entry 300?Entry: 100 - Ref to: 0
Entry: 700 - Ref to: 0
Entry: 800 - Ref to: 700
Entry: 600 - Ref to: 800
Entry: 300 - Ref to: 600
Entry: 200 - Ref to: 300
Entry: 400 - Ref to: 0
Entry: 500 - Ref to: 600
Can't you just give the map entries a sequential number at creation time and sort on that?Kurzer wrote: Fri Sep 29, 2023 12:34 pm But this also means that the elements in the map are not sorted (you cannot sort a map). I only need sorted access to this map in one place in the whole module. And that is, in the function that realigns and/or resizes all the gadgets on a resize event.
Code: Select all
SelectMapElement(myMap(), *elementAddress)Code: Select all
structure grid
array x.d(0) ; pointed by position indices
array y.d(0)
endStructure
structure gridTree
*first
*previous
*parent
*next
*last
*grid.grid
array x.i(0) ; indices pointed by position
array y.i(0)
array w.d(0) ; pointed by size
array h.d(0)
endStructure
structure uiObject
gadget.i
gadgetType.i
*gridTree
position.i
size.i
endstructureThis would mean that the user of my module has to consider the order of the gadgets himself. This is exactly what I wanted to avoid and equip the module with the ability to automatically put the gadget-entries in the gadget management map in an optimal order internally.wilbert wrote: Fri Sep 29, 2023 4:21 pm Can't you just give the map entries a sequential number at creation time and sort on that?
It looks like you only reference elements which are created in the past.
How do you envision this in concrete terms?wilbert wrote: Fri Sep 29, 2023 4:21 pm Another idea might be a LinkedHashMap which is a combination of hash table and linked list.
Code: Select all
Structure MyStruct
Entry.i
RefEntry.i
Done.i
EndStructure
NewList StructList.MyStruct()
AddElement(StructList()) : StructList()\Entry = 100
AddElement(StructList()) : StructList()\Entry = 200 : StructList()\RefEntry =300
AddElement(StructList()) : StructList()\Entry = 300 : StructList()\RefEntry =600
AddElement(StructList()) : StructList()\Entry = 400
AddElement(StructList()) : StructList()\Entry = 500 : StructList()\RefEntry =600
AddElement(StructList()) : StructList()\Entry = 600 : StructList()\RefEntry =800
AddElement(StructList()) : StructList()\Entry = 700
AddElement(StructList()) : StructList()\Entry = 800 : StructList()\RefEntry =700
; SortStructuredList(StructList(), #PB_Sort_Ascending, OffsetOf(MyStruct\RefEntry), TypeOf(MyStruct\RefEntry))
ForEach StructList()
Debug "Entry: " + Str(StructList()\Entry) + " - Ref to: " + Str(StructList()\RefEntry)
Next
Debug "----------------------------"
NewMap Reference.i()
j = ListSize(StructList()) - 1
For i = 0 To j
SelectElement(StructList(), i)
EntryKey.s = Str(StructList()\Entry)
If Not StructList()\RefEntry
MoveElement(StructList(), #PB_List_First)
Debug "Entry " + EntryKey + " moved to front of list"
Else
RefEntryKey.s = Str(StructList()\RefEntry)
If Not Reference(RefEntryKey)
Reference(RefEntryKey) = @StructList()
EndIf
If Reference(EntryKey)
MoveElement(StructList(), #PB_List_Before, Reference(EntryKey))
Debug "Entry " + EntryKey + " moved before referenced entry"
Else
Debug "Entry " + EntryKey + " not moved"
EndIf
EndIf
Next
FreeMap(Reference())
Debug ""
Debug "--------------" + Str(ProcessCounter) + " entries processed ----------------------------"
Debug ""
ForEach StructList()
Debug "Entry: " + Str(StructList()\Entry) + " - Ref to: " + Str(StructList()\RefEntry)
Next
Wilbert wrote:Edit:
I seem to have made a mistake.
It works for the test list but I could also construct a list where it fails.
Code: Select all
[0] record the place before the first entry in the list, and stores it in "alpha"
[1] read the next entry (so, on start, it is the first entry)
[2] Set it as status "already read"
[3] has it reference ? Yes : Go to (8)
[4] Is the handling mode on ? No : Go to (7)
[5] Set the alpha place as current.
[6] Set the handling mode off. Go to (1)
[7] Record the list place in alpha. Go to (1)
[8] Set the handling mode on
[9] Record place to beta.
[10] Set the referring entry as current
[11] Has the entry already read ? No : Go to (13)
[12] Yes ? Set the beta entry as current. Go to (1)
[13] Move the entry before beta, and consider this entry moved as current. Go to (2)I absolutely do not share these concerns. Your example you linked and also my code (WinHandler) run absolutely smooth and flicker free for me. See the GIF screennrecording below.Olli wrote: Fri Sep 29, 2023 9:08 pm But... Now, kurzer, you open our eyes with a concrete example, and the UI goal, I hesitate to continue to this optimization (more than 200 lines to try to execute quicker than your own clear algo).
I do not like this point of view : it is not by miss of own source code. But it is how Windows executes your interface displaying update. Even on best computers, it flickers. I have an example optimized enough : I use only arrays ! Plus, these arrays are fixed ! (in this example, it is a 3*3 cases array, the quickest data object we can use...)
You can look the testIt is ugly : it flickers...
Um, absolutely no.Olli wrote: Fri Sep 29, 2023 9:08 pm It is on a level, where I ask myself if, when you want to update window elements positions, if it is not better to recreate a whole hidden window, and swap the two windows (old versus new), without glittering animation.


edit: Jes... Again a mistake... A "Set done" before a question "Is done?"...Code: Select all
Structure entry
value.i
*ref
done.i
EndStructure
Structure entryList
List e.entry()
EndStructure
Procedure entryListSortLinks(*this.entryList)
Protected hnd ; HaNDling status flag
Protected alpha, beta ; pointers
With *this\e()
FirstElement(*this\e() )
Repeat
If \ref
If hnd = 0
alpha = *this\e()
hnd = 1
EndIf
beta = *this\e()
\done = 1
ChangeCurrentElement(*this\e(), \ref)
If \done
ChangeCurrentElement(*this\e(), beta)
\done = 1
Else
MoveElement(*this\e(), #PB_List_Before, beta)
\done = 1
PreviousElement(*this\e() ) ; "Continue" statement cannot bypass "Until" test ! Forced to go back ! ! !
Continue
EndIf
Else
If hnd
hnd = 0
ChangeCurrentElement(*this\e(), alpha)
EndIf
\done = 1
EndIf
Until NextElement(*this\e() ) = 0
EndWith
EndProcedureCode: Select all
Procedure hash(*a.unicode, r = 0)
While *a\u
r >> 1 ! ($2000 * ((r & 1) ! 1) ) ! *a\u
*a + 2
Wend
ProcedureReturn r
EndProcedure
Debug hash(@"hello")
Debug hash(@"homemade")
Debug hash(@"map")
Dim _map($3FFF)
_map(hash("100") ) = 600
_map(hash("200") ) = 300Code: Select all
[ Crt = Ref ] ; CuRrenT element = REFerencyCode: Select all
ChangeCurrentElement(*this\e(), \ref)Code: Select all
ChangeCurrentElement(*this\e(), _map(hash(\ref) ) )Code: Select all
Debug hash(@"177")
Debug hash(@"190")Debug Output wrote:[21:16:05]
[21:16:05] [Debug] V1: 46320 microseconds.
[21:16:05] [Debug] V2: 46322 microseconds.
[21:16:05] [Debug] V3: 870 microseconds.
Code: Select all
EnableExplicit
Structure MyStruct
Entry.s
RefEntry.s
Sort.i
EndStructure
Global NewList StructList.MyStruct()
Global NewMap StructMap.MyStruct()
Define i, StartTime, Duration
; -----------------------------------
; Procedure for high resolution time measurement, thanks go to Axolotl
; https://www.purebasic.fr/english/viewtopic.php?p=589086#p589086
Procedure.i ElapsedMicroseconds()
Static frequency.q
Protected count.q
If frequency = 0
If QueryPerformanceFrequency_(@frequency)
; frequency / 10000 ; .. divide frequency by 10000 for tenths of a millisecond
; frequency / 1000 ;:: in milli seconds
; frequency / 1000 ;:: in micro seconds
frequency / 1000000 ;:: in micro seconds
EndIf
EndIf
If QueryPerformanceCounter_(@count)
ProcedureReturn count / frequency
Else
ProcedureReturn 0
EndIf
EndProcedure
; The sorting procedures
Procedure Sortlist_V1()
Protected i, j, SearchStart = 0, SearchEnd = ListSize(StructList()) - 1, *Entry
Protected.s RefEntry
For i = SearchStart To SearchEnd - 1
SelectElement(StructList(), i)
If StructList()\RefEntry <> ""
*Entry = @StructList()
RefEntry = StructList()\RefEntry
For j = i + 1 To SearchEnd
SelectElement(StructList(), j)
If StructList()\Entry = RefEntry
If StructList()\RefEntry <> ""
i - 1
EndIf
MoveElement(StructList(), #PB_List_Before, *Entry)
Break
EndIf
Next
EndIf
Next
EndProcedure
Procedure Sortlist_V2()
Protected i, j, SkipEntries, SearchStart = 0, SearchEnd = ListSize(StructList()) - 1, *Entry
Protected.s RefEntry
For i = SearchStart To SearchEnd - 1
SelectElement(StructList(), i)
If StructList()\RefEntry <> ""
*Entry = @StructList()
RefEntry = StructList()\RefEntry
For j = i + 1 To SearchEnd
SelectElement(StructList(), j)
If StructList()\Entry = RefEntry
If StructList()\RefEntry <> ""
i - 1
SkipEntries + 1
Else
i + SkipEntries
SkipEntries = 1
EndIf
MoveElement(StructList(), #PB_List_Before, *Entry)
Break
EndIf
Next
EndIf
Next
EndProcedure
Procedure SortList_V3()
Protected Sortvalue = 0
#Blocksize = 1000
; We work through the list from beginning to end using the following logic:
; 1) We sort the list in ascending order by RefEntry, so that all entries without RefEntry are at the beginning
; 2) We go through the list once from beginning to end to get the sorted element names
; These represent the keys of the identical map. In this loop, only the entries of the map are processed.
; a) If the map entry does *not* reference any other entry and field sort = 0:
; - The map entry gets in the field Sort = Sortvalue
; - Sortvalue = Sortvalue + #Blocksize (1000)
; b) If the map entry *does* reference any other entry:
; - If the referenced entry in the map has no Sortvalue yet:
; - The referenced map entry gets in the field Sort = Sortvalue
; - Sortvalue = Sortvalue + #Blocksize (1000)
; - After this:
; - If the map entry referencing the other element does not yet have a Sortvalue:
; - The map entry gets in the field Sort = Sortvalue
; - Sortvalue = Sortvalue + #Blocksize (1000)
; c) If the referenced(!) entry in the map already has a sortvalue:
; - The entry that references it gets the Sortvalue + 1 from the referenced entry
; - Sortvalue = Sortvalue + #Blocksize (1000)
; 3) We sort the list in ascending order by the field sort
; 1)
SortStructuredList(StructList(), #PB_Sort_Ascending, OffsetOf(MyStruct\RefEntry), TypeOf(MyStruct\RefEntry))
With StructList()
; 2)
ForEach StructList()
; 2b)
If \RefEntry <> ""
If StructMap(\RefEntry)\Sort = 0
StructMap(\RefEntry)\Sort = Sortvalue
Sortvalue + #Blocksize
If StructMap(\Entry)\Sort = 0
StructMap(\Entry)\Sort = Sortvalue
Sortvalue + #Blocksize
EndIf
; 2c)
Else
StructMap(\Entry)\Sort = StructMap(\RefEntry)\Sort + 1
Sortvalue + #Blocksize
EndIf
; 2a)
ElseIf StructMap(\Entry)\Sort = 0
StructMap(\Entry)\Sort = Sortvalue
Sortvalue + #Blocksize
EndIf
Next
ForEach StructList()
\Sort = StructMap(\Entry)\Sort
Next
EndWith
; 3)
SortStructuredList(StructList(), #PB_Sort_Ascending, OffsetOf(MyStruct\Sort), TypeOf(MyStruct\Sort))
EndProcedure
; -----------------------------------
; Initializing the list and map
Procedure Init()
Protected i
ClearList(StructList())
ClearMap(StructMap())
; We create a list of n entries to get enough runtime for a time measurement.
For i = 1 To 1000 Step 10
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+1)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+2) : StructList()\RefEntry = "gadget_" + Str(i+5)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+3) : StructList()\RefEntry = "gadget_" + Str(i+4)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+4) : StructList()\RefEntry = "gadget_" + Str(i+2)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+5)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+6) : StructList()\RefEntry = "gadget_" + Str(i+8)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+7)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+8) : StructList()\RefEntry = "gadget_" + Str(i+7)
AddElement(StructList()) : StructList()\Entry = "gadget_" + Str(i+9)
Next i
; Now we create a map with the same content as the list
ForEach StructList()
StructMap(StructList()\Entry)\Entry =StructList()\Entry
StructMap(StructList()\Entry)\RefEntry =StructList()\RefEntry
Next
; All steps up to here can be realized in my use case by the code area responsible for registering a gadget in my module.
; Therefore this code part must not be included in the time measurement.
EndProcedure
; -----------------------------------
; START
Init()
; Debug ""
; Debug "*** List - Unsorted:"
; ForEach StructList()
; Debug "Entry: " + StructList()\Entry + " - Ref to: " + StructList()\RefEntry + " - Sort: " + Str(StructList()\Sort)
; Next
; Debug "----------------------------"
If 1=1
Init()
StartTime = ElapsedMicroseconds()
SortList_V1()
Duration = ElapsedMicroseconds() - StartTime
Debug "V1: " + Str(Duration) + " microseconds."
EndIf
If 1=1
Init()
StartTime = ElapsedMicroseconds()
SortList_V2()
Duration = ElapsedMicroseconds() - StartTime
Debug "V2: " + Str(Duration) + " microseconds."
EndIf
If 1=1
Init()
StartTime = ElapsedMicroseconds()
SortList_V3()
Duration = ElapsedMicroseconds() - StartTime
Debug "V3: " + Str(Duration) + " microseconds."
EndIf
; Debug ""
; Debug "*** Finally list - Sorted by Sort:"
; ForEach StructList()
; Debug "Entry: " + StructList()\Entry + " - Ref to: " + StructList()\RefEntry + " - Sort: " + Str(StructList()\Sort)
; Next
Code: Select all
EnableExplicit
Structure UIControlAnchorInfo
; Control which the relevant one depends on layout for.
*parent.UIControl
; First of the controls which depend on the relevant one for layout.
*firstChild.UIControl
; Next sibling of the relevant control within its layer.
*nextSibling.UIControl
; Previous sibling of the relevant control within its layer.
*prevSibling.UIControl
; Put other anchor/relative-positioning info here...
EndStructure
Structure UIControl
name.s
anchoring.UIControlAnchorInfo
*window.UIWindow
; Put other control data here.
EndStructure
Structure UIWindow
Map controlsByName.UIControl()
root.UIControl
EndStructure
; This procedure would assumingly accept additional arguments specifying how anchoring is
; to be done.
Procedure AnchorControl(*control.UIControl, *anchorParent.UIControl)
Protected *currentParent.UIControl
Protected *prevSibling.UIControl
Protected *nextSibling.UIControl
; Before anything, do safety checking and ensure acyclicity.
; Catch the most trivial self:self case.
If *control = *anchorParent
DebuggerError("Attempted to produce control cycle with self: " + *control\name + " <-> " + *control\name)
End
EndIf
; Walk the tree to prevent making *control parented by one if its own children.
; This could instead be handled by restructuring the tree to invert the relationship.
If *anchorParent
*currentParent = *anchorParent\anchoring\parent
While *currentParent
If *currentParent = *control
; You could store the path walked to produce a better message here.
DebuggerError("Attempted to produce control cycle with self: " + *control\name + " <-> " + *control\name + ", via: " + *anchorParent\name)
End
EndIf
*currentParent = *currentParent\anchoring\parent
Wend
Else
; Default to window root if no anchor parent it set.
*anchorParent = *control\window\root
EndIf
; Handle un-linkage when a parent is already present.
*currentParent = *control\anchoring\parent
If *currentParent
; Bail early if attempting to anchor to the current anchor target.
If *currentParent = *anchorParent
ProcedureReturn *control
EndIf
; Detach from siblings.
*prevSibling.UIControl = *control\anchoring\prevSibling
*nextSibling.UIControl = *control\anchoring\nextSibling
If *prevSibling
*prevSibling\anchoring\nextSibling = *nextSibling
Else
; If no prior sibling exists, then this node was the head of its layer, in which
; case the next sibling should take its place.
*currentParent\anchoring\firstChild = *nextSibling
EndIf
If *nextSibling
*nextSibling\anchoring\prevSibling = *prevSibling
EndIf
EndIf
; Previous relations have been unlinked. Assign the new state.
*control\anchoring\parent = *anchorParent
*control\anchoring\prevSibling = #Null
*nextSibling = *anchorParent\anchoring\firstChild
*anchorParent\anchoring\firstChild = *control
*control\anchoring\nextSibling = *nextSibling
If *nextSibling
*nextSibling\anchoring\prevSibling = *control
EndIf
ProcedureReturn *control
EndProcedure
Procedure.i CreateControl(*window.UIWindow, name.s)
Protected *control.UIControl = FindMapElement(*window\controlsByName(), name)
; Treat reused control name as fatal error.
; Up to you to do something else.
If *control
DebuggerError("Attempted to reuse control name: " + name)
End
EndIf
*control = AddMapElement(*window\controlsByName(), name)
*control\window = *window
*control\name = name
; Anchor to root.
AnchorControl(*control, #Null)
ProcedureReturn *control
EndProcedure
; Procedure for high resolution time measurement, thanks go to Axolotl
; https://www.purebasic.fr/english/viewtopic.php?p=589086#p589086
Procedure.i ElapsedMicroseconds()
Static frequency.q
Protected count.q
If frequency = 0
If QueryPerformanceFrequency_(@frequency)
; frequency / 10000 ; .. divide frequency by 10000 for tenths of a millisecond
; frequency / 1000 ;:: in milli seconds
; frequency / 1000 ;:: in micro seconds
frequency / 1000000 ;:: in micro seconds
EndIf
EndIf
If QueryPerformanceCounter_(@count)
ProcedureReturn count / frequency
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure Init_Tree(*window.UIWindow)
Protected i
ClearMap(*window\controlsByName())
; We create a list of n entries to get enough runtime for a time measurement.
For i = 1 To 1000 Step 10
; Create all the controls.
CreateControl(*window, "gadget_" + Str(i))
CreateControl(*window, "gadget_" + Str(i + 1))
CreateControl(*window, "gadget_" + Str(i + 2))
CreateControl(*window, "gadget_" + Str(i + 3))
CreateControl(*window, "gadget_" + Str(i + 4))
CreateControl(*window, "gadget_" + Str(i + 5))
CreateControl(*window, "gadget_" + Str(i + 6))
CreateControl(*window, "gadget_" + Str(i + 7))
CreateControl(*window, "gadget_" + Str(i + 8))
CreateControl(*window, "gadget_" + Str(i + 9))
; Assign refs.
; 2 ⟶ 5
AnchorControl(*window\controlsByName("gadget_" + Str(i+2)), *window\controlsByName("gadget_" + Str(i+5)))
; 3 ⟶ 4
AnchorControl(*window\controlsByName("gadget_" + Str(i+3)), *window\controlsByName("gadget_" + Str(i+4)))
; 4 ⟶ 2
AnchorControl(*window\controlsByName("gadget_" + Str(i+4)), *window\controlsByName("gadget_" + Str(i+2)))
; 6 ⟶ 8
AnchorControl(*window\controlsByName("gadget_" + Str(i+6)), *window\controlsByName("gadget_" + Str(i+8)))
; 8 ⟶ 7
AnchorControl(*window\controlsByName("gadget_" + Str(i+8)), *window\controlsByName("gadget_" + Str(i+7)))
Next i
EndProcedure
Procedure FlattenControlTree(*control.UIControl, List *outControls.UIControl())
Protected *currentControl.UIControl = *control\anchoring\firstChild
While *currentControl
AddElement(*outControls())
*outControls() = *currentControl
FlattenControlTree(*currentControl, *outControls())
*currentControl = *currentControl\anchoring\nextSibling
Wend
EndProcedure
Procedure PrintPrettyTree(*control.UIControl, depth.i = 0)
If Not depth
PrintN("Root(name = " + #DQUOTE$ + *control\name + #DQUOTE$ + ")")
Else
PrintN(Space(depth * 4) + *control\name)
EndIf
Protected *currentControl.UIControl = *control\anchoring\firstChild
While *currentControl
PrintPrettyTree(*currentControl, depth + 1)
*currentControl = *currentControl\anchoring\nextSibling
Wend
EndProcedure
If Not OpenConsole()
MessageRequester("Fatal Error", "Failed to open console!", #PB_MessageRequester_Error)
End
EndIf
Define window.UIWindow
Define startTime, elapsedTimeInit, elapsedTimeFlatten
startTime = ElapsedMicroseconds()
Init_Tree(window)
elapsedTimeInit = ElapsedMicroseconds() - startTime
PrintN("# controls: " + MapSize(window\controlsByName()))
PrintN("==================================================")
; Flatten tree into a new list.
; ═══════════════════════════════════════════════════════
; There's no need to sort it, since controls are sorted
; as they are inserted.
; Normally, when rendering, you'd walk the tree without
; this copying and simply render in proper order.
NewList *orderedControlsFlat.UIControl()
startTime = ElapsedMicroseconds()
FlattenControlTree(window\root, *orderedControlsFlat())
elapsedTimeFlatten = ElapsedMicroseconds() - startTime
; Print flattened list.
ForEach *orderedControlsFlat()
If *orderedControlsFlat()\anchoring\parent <> window\root
PrintN("Entry: " + *orderedControlsFlat()\name + " via: " + *orderedControlsFlat()\anchoring\parent\name)
Else
PrintN("Entry: " + *orderedControlsFlat()\name)
EndIf
Next
PrintN("==================================================")
; Print structured tree.
; ═══════════════════════════════════════════════════════
PrintPrettyTree(window\root)
PrintN("==================================================")
; Print time taken.
; ═══════════════════════════════════════════════════════
PrintN("Time to init tree: " + elapsedTimeInit + "µs")
PrintN("Time to flat tree: " + elapsedTimeFlatten + "µs")
Input()
Code: Select all
==================================================
Time to init tree: 356µs
Time to flat tree: 34µs
Code: Select all
Time to init: 419 microseconds
V1: 2460 microseconds.
V2: 1119 microseconds.
V3: 186 microseconds.
Ahah, I wouldn't go so far as "expert"
This seems like a side-effect of the debugger having a much greater performance impact as the number of source lines increase. The DAG approach will have a lot more time spent hitting each line (and updating debugger info) since much of the logic is contained within itself rather than PB library functions like SortStructuredList(…).Kurzer wrote: Mon Oct 02, 2023 8:21 am Second, the DAG solution on my machine is about nine times slower than the V3 sort.
DAG: Init: 6579 uS + flatten: 1629 uS = 8208 uS
V3: 870 uS
Absolutely – your approach already seems quite fast given many gadgets! I wouldn't use the more complex DAG approach unless it seemed really necessary to cure a bottleneck.Kurzer wrote: Mon Oct 02, 2023 8:21 am I'm not sure yet if I need a DAG solution for my module, because except for the sorting everything else is completely done (the anchoring, the reading and saving of the gadget contents etc.). I would have to rebuild almost the whole module if I would change the gadget management to DAG.
That's right! (I should've put that in parentheses, since woah Google does turn up loads of stuff for it)Kurzer wrote: Mon Oct 02, 2023 8:21 am * There are so many terms where DAG stands for, I hope I found the correct one: "Directed Acyclic Graph" - LOL![]()