Eduard Zozulya <[email protected]> schrieb:A little faster.
Need union two list as
set l1 "1 2 3"
set l2 "a s d"
Result list as "1 a 2 s 3 d"
This procedure
proc UnionList { l1 l2 } {
foreach e1 $l1 e2 $l2 {
lappend lret $e1 $e2
}
return $lret
}
working very slowly because lists l1 and l2 very big (100000 elements)
How to optimize list merging?Try a dict?
set d [dict create]
foreach e1 $l1 e2 $l2 {
dict set d $e1 $e2
}
HTH
Helmut
Need union two list asTry a dict?
set l1 "1 2 3"
set l2 "a s d"
Result list as "1 a 2 s 3 d"
This procedure
proc UnionList { l1 l2 } {
foreach e1 $l1 e2 $l2 {
lappend lret $e1 $e2
}
return $lret
}
working very slowly because lists l1 and l2 very big (100000 elements)
How to optimize list merging?
Need union two list as
set l1 "1 2 3"
set l2 "a s d"
Result list as "1 a 2 s 3 d"
This procedure
proc UnionList { l1 l2 } {
foreach e1 $l1 e2 $l2 {
lappend lret $e1 $e2
}
return $lret
}
working very slowly because lists l1 and l2 very big (100000 elements)
How to optimize list merging?
Eduard Zozulya <[email protected]> wrote:
How to optimize list merging?The only speedup possibility might be to code a C extension to actually perform the zip, which removes Tcl interpreter overhead, but it would
still have to iterate one by one building up the result, so there is a
lower bound below which the operation simply will not reach performace
wise.
пʼятниця, 13 жовтня 2023 р. о 15:52:12 UTC+3 Helmut Giese пише:
Eduard Zozulya <[email protected]> schrieb:A little faster.
How to optimize list merging?Try a dict?
set d [dict create]
foreach e1 $l1 e2 $l2 {
dict set d $e1 $e2
}
HTH
Helmut
Thank you!
Rich <[email protected]d> wrote:
Eduard Zozulya <[email protected]> wrote:according to another followup in this thread, building a dict gave a
How to optimize list merging?The only speedup possibility might be to code a C extension to actually perform the zip, which removes Tcl interpreter overhead, but it would still have to iterate one by one building up the result, so there is a lower bound below which the operation simply will not reach performace wise.
slight improvement over building a list.
There might be two reasons for that:
- the list originally created was used as a dict afterwards,
so building a dict in first place is of course better,
than creating a list and having it converted to dict.
- maybe the first list contains certain elements a multiple times,
e.g. list1 is {a a a a}, then the dict created would only
contain key "a" with last value of second list. That may
or may not be OP's intention.
It is interesting, I though call by name/ref would be faster, because you don't need to copy so much data in memory, but it was only faster for small strings. And lset is comparable slow.
time0 {8733.72 microseconds per iteration}
time1 {9645.48 microseconds per iteration}
time2 {9336.58 microseconds per iteration}
time3 {9219.38 microseconds per iteration}
time4 {11611.38 microseconds per iteration}
time5 {15875.34 microseconds per iteration}
proc Task {} {
set n 100000
set t 50
set time0 [time {Test0 $n} $t]
set time1 [time {Test1 $n} $t]
set time2 [time {Test2 $n} $t]
set time3 [time {Test3 $n} $t]
set time4 [time {Test4 $n} $t]
set time5 [time {Test5 $n} $t]
}
# std call by value
proc Test0 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set LR [LMergeByVal0 $L1 $L2]
}
proc LMergeByVal0 {L1 L2} {
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# in call by ref
proc Test1 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set LR [LMergeByRef1 L1 L2]
}
proc LMergeByRef1 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# all by ref
proc Test2 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set Lr [list]
LMergeByRef2 L1 L2 LR
}
proc LMergeByRef2 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# result by ref
proc Test3 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set LR [list]
LMergeByRef3 $L1 $L2 LR
}
proc LMergeByRef3 {L1 L2 cbrRes} {
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# by ref and index
proc Test4 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set LR [list]
LMergeByRef4 L1 L2 LR
}
proc LMergeByRef4 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
set LR [list]
set n [llength $L1]
for {set i 0} {$i < $n} {incr i} {
lappend LR [lindex $L1 $i] [lindex $L2 $i]
}
return $LR
}
# use lmap
proc Test5 {n} {
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set LR [LMergeByRef5 L1 L2]
}
proc LMergeByRef5 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
return [lmap a $L1 b $L2 {list $a $b}]
}
proc Get_List1 {n} {
return [lrepeat $n {3 2 5 1 9 4} ]
}
proc Get_List2 {n} {
return [lrepeat $n {A B C d e F} ]
}
Eduard Zozulya wrote:
Need union two list as
set l1 "1 2 3"
set l2 "a s d"
Result list as "1 a 2 s 3 d"
This procedure
proc UnionList { l1 l2 } {
foreach e1 $l1 e2 $l2 {
lappend lret $e1 $e2
}
return $lret
}
working very slowly because lists l1 and l2 very big (100000 elements)
How to optimize list merging?The common name for this is "zipping" lists (see https://wiki.tcl-lang.org/page/lzip) and unfortunately, no matter how
hard you try to 'hide' the fact, ultimately any abstraction will expand
into iterating over both lists one element at a time and creating a
result from each element one pair of elements at a time.
There simply is not any other way to perform the operation.
The only speedup possibility might be to code a C extension to actually perform the zip, which removes Tcl interpreter overhead, but it would
still have to iterate one by one building up the result, so there is a
lower bound below which the operation simply will not reach performace
wise.
On Friday, October 13, 2023 at 5:14:04 PM UTC+2, Rich wrote:abandoning the original lists afterwards.)
Eduard Zozulya wrote:
Need union two list as
set l1 "1 2 3"
set l2 "a s d"
Result list as "1 a 2 s 3 d"
This procedure
proc UnionList { l1 l2 } {
foreach e1 $l1 e2 $l2 {
lappend lret $e1 $e2
}
return $lret
}
working very slowly because lists l1 and l2 very big (100000 elements)
How to optimize list merging?The common name for this is "zipping" lists (see https://wiki.tcl-lang.org/page/lzip) and unfortunately, no matter how
hard you try to 'hide' the fact, ultimately any abstraction will expand into iterating over both lists one element at a time and creating a
result from each element one pair of elements at a time.
There simply is not any other way to perform the operation.
The only speedup possibility might be to code a C extension to actually perform the zip, which removes Tcl interpreter overhead, but it would still have to iterate one by one building up the result, so there is a lower bound below which the operation simply will not reach performace wise.That might be fun. A few constraints should help, e.g. if the original lists won't be used after the merge, then one could keep the reference count of their members. (Imagine how Eduard might be incrementing all counters just to decrement them when
https://core.tcl-lang.org/tips/doc/trunk/tip/625.md should be a good read on the internals of lists in general.
Sorry Helmut, your result was unfair :-) , because you may have worked with one index only. And if you have no idea what data you get, this may dangerous. List creation is now out, done in 'Task', only with call by values, and sorry dict is not thefastest :-), but it is very fast for few indicies. And you did it in one proc, this was not fair either :-)
And as mentioned it seems to be 30% slower
Now the start scenario is the same for each test, I hope :-)
time1 {10133.08 microseconds per iteration}
time2 {10808.5 microseconds per iteration}
time3 {10865.56 microseconds per iteration}
time4 {13078.14 microseconds per iteration}
time5 {20947.82 microseconds per iteration}
time6 {20225.42 microseconds per iteration}
time7 {13771.64 microseconds per iteration}
proc Task {} {
set n 100000
set t 50
# L1 has now uniquid indicines
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set time0 [time {ZIP_Main0 $L1 $L2} $t]
set time1 [time {ZIP_Main1 $L1 $L2} $t]
set time2 [time {ZIP_Main2 $L1 $L2} $t]
set time3 [time {ZIP_Main3 $L1 $L2} $t]
set time4 [time {ZIP_Main4 $L1 $L2} $t]
set time5 [time {ZIP_Main5 $L1 $L2} $t]
set time6 [time {ZIP_Main6 $L1 $L2} $t]
set time7 [time {ZIP_Main7 $L1 $L2} $t]
}
# std call by value
proc ZIP_Main0 {L1 L2} {
set LR [ZIP_Action0 $L1 $L2]
}
proc ZIP_Action0 {L1 L2} {
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# in args by ref
proc ZIP_Main1 {L1 L2} {
set LR [ZIP_Action1 L1 L2]
}
proc ZIP_Action1 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# all by ref
proc ZIP_Main2 {L1 L2} {
set LR [list]
ZIP_Action2 L1 L2 LR
}
proc ZIP_Action2 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# result by ref
proc ZIP_Main3 {L1 L2} {
set LR [list]
ZIP_Action3 $L1 $L2 LR
}
proc ZIP_Action3 {L1 L2 cbrRes} {
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# # by ref and index
proc ZIP_Main4 {L1 L2} {
set LR [list]
ZIP_Action4 L1 L2 LR
}
proc ZIP_Action4 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
set LR [list]
set n [llength $L1]
for {set i 0} {$i < $n} {incr i} {
lappend LR [lindex $L1 $i] [lindex $L2 $i]
}
return $LR
}
# use lmap by val
proc ZIP_Main5 {L1 L2} {
set LR [ZIP_Action5 $L1 $L2]
}
proc ZIP_Action5 {L1 L2} {
return [lmap a $L1 b $L2 {list $a $b}]
}
# use lmap by ref
proc ZIP_Main6 {L1 L2} {
set LR [ZIP_Action6 L1 L2]
}
proc ZIP_Action6 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
return [lmap a $L1 b $L2 {list $a $b}]
}
# use dict with unique ID's in L1
# use lmap by ref
proc ZIP_Main7 {L1 L2} {
set LR [ZIP_Action7 $L1 $L2]
}
proc ZIP_Action7 {L1 L2} {
set d [dict create]
foreach I1 $L1 I2 $L2 {
dict set d $I1 $I2
}
return [set d]
}
proc Get_List1 {n} {
set bl [list]
for {set i 0} {$i < $n} {incr i} {
lappend bl $i
}
return $bl
}
proc Get_List2 {n} {
return [lrepeat $n {A B C d e F} ]
}
On Sunday, October 15, 2023 at 7:28:04 AM UTC-7, Mole Cool wrote:fastest :-), but it is very fast for few indicies. And you did it in one proc, this was not fair either :-)
Sorry Helmut, your result was unfair :-) , because you may have worked with one index only. And if you have no idea what data you get, this may dangerous. List creation is now out, done in 'Task', only with call by values, and sorry dict is not the
And as mentioned it seems to be 30% slower
Now the start scenario is the same for each test, I hope :-)
time1 {10133.08 microseconds per iteration}
time2 {10808.5 microseconds per iteration}
time3 {10865.56 microseconds per iteration}
time4 {13078.14 microseconds per iteration}
time5 {20947.82 microseconds per iteration}
time6 {20225.42 microseconds per iteration}
time7 {13771.64 microseconds per iteration}
proc Task {} {
set n 100000
set t 50
# L1 has now uniquid indicines
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set time0 [time {ZIP_Main0 $L1 $L2} $t]
set time1 [time {ZIP_Main1 $L1 $L2} $t]
set time2 [time {ZIP_Main2 $L1 $L2} $t]
set time3 [time {ZIP_Main3 $L1 $L2} $t]
set time4 [time {ZIP_Main4 $L1 $L2} $t]
set time5 [time {ZIP_Main5 $L1 $L2} $t]
set time6 [time {ZIP_Main6 $L1 $L2} $t]
set time7 [time {ZIP_Main7 $L1 $L2} $t]
}
# std call by value
proc ZIP_Main0 {L1 L2} {
set LR [ZIP_Action0 $L1 $L2]
}
proc ZIP_Action0 {L1 L2} {
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# in args by ref
proc ZIP_Main1 {L1 L2} {
set LR [ZIP_Action1 L1 L2]
}
proc ZIP_Action1 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# all by ref
proc ZIP_Main2 {L1 L2} {
set LR [list]
ZIP_Action2 L1 L2 LR
}
proc ZIP_Action2 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# result by ref
proc ZIP_Main3 {L1 L2} {
set LR [list]
ZIP_Action3 $L1 $L2 LR
}
proc ZIP_Action3 {L1 L2 cbrRes} {
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# # by ref and index
proc ZIP_Main4 {L1 L2} {
set LR [list]
ZIP_Action4 L1 L2 LR
}
proc ZIP_Action4 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
set LR [list]
set n [llength $L1]
for {set i 0} {$i < $n} {incr i} {
lappend LR [lindex $L1 $i] [lindex $L2 $i]
}
return $LR
}
# use lmap by val
proc ZIP_Main5 {L1 L2} {
set LR [ZIP_Action5 $L1 $L2]
}
proc ZIP_Action5 {L1 L2} {
return [lmap a $L1 b $L2 {list $a $b}]
}
# use lmap by ref
proc ZIP_Main6 {L1 L2} {
set LR [ZIP_Action6 L1 L2]
}
proc ZIP_Action6 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
return [lmap a $L1 b $L2 {list $a $b}]
}
# use dict with unique ID's in L1
# use lmap by ref
proc ZIP_Main7 {L1 L2} {
set LR [ZIP_Action7 $L1 $L2]
}
proc ZIP_Action7 {L1 L2} {
set d [dict create]
foreach I1 $L1 I2 $L2 {
dict set d $I1 $I2
}
return [set d]
}
proc Get_List1 {n} {
set bl [list]
for {set i 0} {$i < $n} {incr i} {
lappend bl $i
}
return $bl
}
proc Get_List2 {n} {I've implemented "ZIP" as an abstract list. It is implemented as the command [lweave] which takes 1 or more lists as arguments and returns the zippered (or weaved) list.
return [lrepeat $n {A B C d e F} ]
}
Added the following test procs:
package require lweave
# use lweave
proc ZIP_Main8 {L1 L2} {
set LR [ZIP_Action8 $L1 $L2]
}
proc ZIP_Action8 {L1 L2} {
lweave $L1 $L2
}
proc ZipCheck {0 1 zip} {
set i 0
set errors 0
foreach a $0 b $1 {
if {$a ne [lindex $zip $i]} {
puts "Mismatch($i) $a ne [lindex $zip $i]"
incr errors
}
incr i
if {$b ne [lindex $zip $i]} {
puts "Mismatch($i) $b ne [lindex $zip $i]"
incr errors
}
incr i
}
puts "Errors=$errors"
}
And here are the results. (Note: I'm using a virtual machine running on an older laptop, so the times are about double+ those reported above.)
$ tclsh9.0 weave_test.tcl
Start Task
Errors=0
Errors=0
timeL1->19785 microseconds per iteration
timeL2->1034 microseconds per iteration
time0->31293.82 microseconds per iteration
time1->32768.96 microseconds per iteration
time2->31919.48 microseconds per iteration
time3->31927.18 microseconds per iteration
time4->40238.34 microseconds per iteration
time5->34435.24 microseconds per iteration
time6->34532.44 microseconds per iteration
time7->52551.18 microseconds per iteration
time8->2.88 microseconds per iteration
I will check in the implementation to github in the next couple days, then post a link here.
-Brian
On Tuesday, October 17, 2023 at 9:04:18 PM UTC-7, briang wrote:fastest :-), but it is very fast for few indicies. And you did it in one proc, this was not fair either :-)
On Sunday, October 15, 2023 at 7:28:04 AM UTC-7, Mole Cool wrote:
Sorry Helmut, your result was unfair :-) , because you may have worked with one index only. And if you have no idea what data you get, this may dangerous. List creation is now out, done in 'Task', only with call by values, and sorry dict is not the
And as mentioned it seems to be 30% slower
Now the start scenario is the same for each test, I hope :-)
time1 {10133.08 microseconds per iteration}
time2 {10808.5 microseconds per iteration}
time3 {10865.56 microseconds per iteration}
time4 {13078.14 microseconds per iteration}
time5 {20947.82 microseconds per iteration}
time6 {20225.42 microseconds per iteration}
time7 {13771.64 microseconds per iteration}
proc Task {} {
set n 100000
set t 50
# L1 has now uniquid indicines
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set time0 [time {ZIP_Main0 $L1 $L2} $t]
set time1 [time {ZIP_Main1 $L1 $L2} $t]
set time2 [time {ZIP_Main2 $L1 $L2} $t]
set time3 [time {ZIP_Main3 $L1 $L2} $t]
set time4 [time {ZIP_Main4 $L1 $L2} $t]
set time5 [time {ZIP_Main5 $L1 $L2} $t]
set time6 [time {ZIP_Main6 $L1 $L2} $t]
set time7 [time {ZIP_Main7 $L1 $L2} $t]
}
# std call by value
proc ZIP_Main0 {L1 L2} {
set LR [ZIP_Action0 $L1 $L2]
}
proc ZIP_Action0 {L1 L2} {
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# in args by ref
proc ZIP_Main1 {L1 L2} {
set LR [ZIP_Action1 L1 L2]
}
proc ZIP_Action1 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# all by ref
proc ZIP_Main2 {L1 L2} {
set LR [list]
ZIP_Action2 L1 L2 LR
}
proc ZIP_Action2 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# result by ref
proc ZIP_Main3 {L1 L2} {
set LR [list]
ZIP_Action3 $L1 $L2 LR
}
creation is expensive for such large lists.proc ZIP_Action3 {L1 L2 cbrRes} {
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# # by ref and index
proc ZIP_Main4 {L1 L2} {
set LR [list]
ZIP_Action4 L1 L2 LR
}
proc ZIP_Action4 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
set LR [list]
set n [llength $L1]
for {set i 0} {$i < $n} {incr i} {
lappend LR [lindex $L1 $i] [lindex $L2 $i]
}
return $LR
}
# use lmap by val
proc ZIP_Main5 {L1 L2} {
set LR [ZIP_Action5 $L1 $L2]
}
proc ZIP_Action5 {L1 L2} {
return [lmap a $L1 b $L2 {list $a $b}]
}
# use lmap by ref
proc ZIP_Main6 {L1 L2} {
set LR [ZIP_Action6 L1 L2]
}
proc ZIP_Action6 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
return [lmap a $L1 b $L2 {list $a $b}]
}
# use dict with unique ID's in L1
# use lmap by ref
proc ZIP_Main7 {L1 L2} {
set LR [ZIP_Action7 $L1 $L2]
}
proc ZIP_Action7 {L1 L2} {
set d [dict create]
foreach I1 $L1 I2 $L2 {
dict set d $I1 $I2
}
return [set d]
}
proc Get_List1 {n} {
set bl [list]
for {set i 0} {$i < $n} {incr i} {
lappend bl $i
}
return $bl
}
proc Get_List2 {n} {I've implemented "ZIP" as an abstract list. It is implemented as the command [lweave] which takes 1 or more lists as arguments and returns the zippered (or weaved) list.
return [lrepeat $n {A B C d e F} ]
}
Added the following test procs:
package require lweave
# use lweave
proc ZIP_Main8 {L1 L2} {
set LR [ZIP_Action8 $L1 $L2]
}
proc ZIP_Action8 {L1 L2} {
lweave $L1 $L2
}
proc ZipCheck {0 1 zip} {
set i 0
set errors 0
foreach a $0 b $1 {
if {$a ne [lindex $zip $i]} {
puts "Mismatch($i) $a ne [lindex $zip $i]"
incr errors
}
incr i
if {$b ne [lindex $zip $i]} {
puts "Mismatch($i) $b ne [lindex $zip $i]"
incr errors
}
incr i
}
puts "Errors=$errors"
}
And here are the results. (Note: I'm using a virtual machine running on an older laptop, so the times are about double+ those reported above.)
$ tclsh9.0 weave_test.tcl
Start Task
Errors=0
Errors=0
timeL1->19785 microseconds per iteration
timeL2->1034 microseconds per iteration
time0->31293.82 microseconds per iteration
time1->32768.96 microseconds per iteration
time2->31919.48 microseconds per iteration
time3->31927.18 microseconds per iteration
time4->40238.34 microseconds per iteration
time5->34435.24 microseconds per iteration
time6->34532.44 microseconds per iteration
time7->52551.18 microseconds per iteration
time8->2.88 microseconds per iteration
I will check in the implementation to github in the next couple days, then post a link here.The implementation of "lweave" can be found here: https://github.com/bgriffinfortytwo/abstractlist-toys/commit/94b0f4addc05e8f777833629847e5dcb685375ca
-Brian
Here is the comparative results of lweave. Note that although the create time for the weaved (Zipped) list is significantly faster, iterating over the list is about 4x slower. The memory footprint should be smaller though. Also note that string
tclsh9.0 weave_test.tcl
Start Task
timeL1->21252 microseconds per iteration
timeL2->961 microseconds per iteration
time8->2.96 microseconds per iteration
Z8-representation->value is a lweave with a refcount of 2, object pointer at 0x55ad8d4ee960, internal representation 0x55ad8cfbc200:0x55ad8d4ee7b0, no string representation
dict-keys->169024 microseconds per iteration
After-dict->value is a dict with a refcount of 2, object pointer at 0x55ad8d4ee960, internal representation 0x55ad8d02e720:0x0, string representation "0 {A B C d e ..."
time0->32575.44 microseconds per iteration
time1->32297.28 microseconds per iteration
time2->32364.22 microseconds per iteration
time3->32841.46 microseconds per iteration
time4->41252.14 microseconds per iteration
time5->35948.38 microseconds per iteration
time6->35133.78 microseconds per iteration
time7->55273.42 microseconds per iteration
Errors=0
Z0-check->34604 microseconds per iteration
Errors=0
Z8-check->142120 microseconds per iteration
21075 microseconds per iteration
See weave_test.tcl in the abstractlist-toys project to replay the above results.
This implementation will only work in Tcl 9.0.
-Brian
On Thursday, October 19, 2023 at 2:58:34 AM UTC+2, briang wrote:the fastest :-), but it is very fast for few indicies. And you did it in one proc, this was not fair either :-)
On Tuesday, October 17, 2023 at 9:04:18 PM UTC-7, briang wrote:
On Sunday, October 15, 2023 at 7:28:04 AM UTC-7, Mole Cool wrote:
Sorry Helmut, your result was unfair :-) , because you may have worked with one index only. And if you have no idea what data you get, this may dangerous. List creation is now out, done in 'Task', only with call by values, and sorry dict is not
And as mentioned it seems to be 30% slower
Now the start scenario is the same for each test, I hope :-)
time1 {10133.08 microseconds per iteration}
time2 {10808.5 microseconds per iteration}
time3 {10865.56 microseconds per iteration}
time4 {13078.14 microseconds per iteration}
time5 {20947.82 microseconds per iteration}
time6 {20225.42 microseconds per iteration}
time7 {13771.64 microseconds per iteration}
proc Task {} {
set n 100000
set t 50
# L1 has now uniquid indicines
set L1 [Get_List1 $n ]
set L2 [Get_List2 $n ]
set time0 [time {ZIP_Main0 $L1 $L2} $t]
set time1 [time {ZIP_Main1 $L1 $L2} $t]
set time2 [time {ZIP_Main2 $L1 $L2} $t]
set time3 [time {ZIP_Main3 $L1 $L2} $t]
set time4 [time {ZIP_Main4 $L1 $L2} $t]
set time5 [time {ZIP_Main5 $L1 $L2} $t]
set time6 [time {ZIP_Main6 $L1 $L2} $t]
set time7 [time {ZIP_Main7 $L1 $L2} $t]
}
# std call by value
proc ZIP_Main0 {L1 L2} {
set LR [ZIP_Action0 $L1 $L2]
}
proc ZIP_Action0 {L1 L2} {
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# in args by ref
proc ZIP_Main1 {L1 L2} {
set LR [ZIP_Action1 L1 L2]
}
proc ZIP_Action1 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
return $LR
}
# all by ref
proc ZIP_Main2 {L1 L2} {
set LR [list]
ZIP_Action2 L1 L2 LR
}
proc ZIP_Action2 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# result by ref
proc ZIP_Main3 {L1 L2} {
set LR [list]
ZIP_Action3 $L1 $L2 LR
}
creation is expensive for such large lists.proc ZIP_Action3 {L1 L2 cbrRes} {
upvar $cbrRes LR
foreach I1 $L1 I2 $L2 {
lappend LR $I1 $I2
}
}
# # by ref and index
proc ZIP_Main4 {L1 L2} {
set LR [list]
ZIP_Action4 L1 L2 LR
}
proc ZIP_Action4 {cbrL1 cbrL2 cbrRes} {
upvar $cbrL1 L1
upvar $cbrL2 L2
upvar $cbrRes LR
set LR [list]
set n [llength $L1]
for {set i 0} {$i < $n} {incr i} {
lappend LR [lindex $L1 $i] [lindex $L2 $i]
}
return $LR
}
# use lmap by val
proc ZIP_Main5 {L1 L2} {
set LR [ZIP_Action5 $L1 $L2]
}
proc ZIP_Action5 {L1 L2} {
return [lmap a $L1 b $L2 {list $a $b}]
}
# use lmap by ref
proc ZIP_Main6 {L1 L2} {
set LR [ZIP_Action6 L1 L2]
}
proc ZIP_Action6 {cbrL1 cbrL2} {
upvar $cbrL1 L1
upvar $cbrL2 L2
return [lmap a $L1 b $L2 {list $a $b}]
}
# use dict with unique ID's in L1
# use lmap by ref
proc ZIP_Main7 {L1 L2} {
set LR [ZIP_Action7 $L1 $L2]
}
proc ZIP_Action7 {L1 L2} {
set d [dict create]
foreach I1 $L1 I2 $L2 {
dict set d $I1 $I2
}
return [set d]
}
proc Get_List1 {n} {
set bl [list]
for {set i 0} {$i < $n} {incr i} {
lappend bl $i
}
return $bl
}
proc Get_List2 {n} {I've implemented "ZIP" as an abstract list. It is implemented as the command [lweave] which takes 1 or more lists as arguments and returns the zippered (or weaved) list.
return [lrepeat $n {A B C d e F} ]
}
Added the following test procs:
package require lweave
# use lweave
proc ZIP_Main8 {L1 L2} {
set LR [ZIP_Action8 $L1 $L2]
}
proc ZIP_Action8 {L1 L2} {
lweave $L1 $L2
}
proc ZipCheck {0 1 zip} {
set i 0
set errors 0
foreach a $0 b $1 {
if {$a ne [lindex $zip $i]} {
puts "Mismatch($i) $a ne [lindex $zip $i]"
incr errors
}
incr i
if {$b ne [lindex $zip $i]} {
puts "Mismatch($i) $b ne [lindex $zip $i]"
incr errors
}
incr i
}
puts "Errors=$errors"
}
And here are the results. (Note: I'm using a virtual machine running on an older laptop, so the times are about double+ those reported above.)
$ tclsh9.0 weave_test.tcl
Start Task
Errors=0
Errors=0
timeL1->19785 microseconds per iteration
timeL2->1034 microseconds per iteration
time0->31293.82 microseconds per iteration
time1->32768.96 microseconds per iteration
time2->31919.48 microseconds per iteration
time3->31927.18 microseconds per iteration
time4->40238.34 microseconds per iteration
time5->34435.24 microseconds per iteration
time6->34532.44 microseconds per iteration
time7->52551.18 microseconds per iteration
time8->2.88 microseconds per iteration
I will check in the implementation to github in the next couple days, then post a link here.The implementation of "lweave" can be found here: https://github.com/bgriffinfortytwo/abstractlist-toys/commit/94b0f4addc05e8f777833629847e5dcb685375ca
-Brian
Here is the comparative results of lweave. Note that although the create time for the weaved (Zipped) list is significantly faster, iterating over the list is about 4x slower. The memory footprint should be smaller though. Also note that string
tclsh9.0 weave_test.tcl
Start Task
timeL1->21252 microseconds per iteration
timeL2->961 microseconds per iteration
time8->2.96 microseconds per iteration
Z8-representation->value is a lweave with a refcount of 2, object pointer at 0x55ad8d4ee960, internal representation 0x55ad8cfbc200:0x55ad8d4ee7b0, no string representation
dict-keys->169024 microseconds per iteration
After-dict->value is a dict with a refcount of 2, object pointer at 0x55ad8d4ee960, internal representation 0x55ad8d02e720:0x0, string representation "0 {A B C d e ..."
time0->32575.44 microseconds per iteration
time1->32297.28 microseconds per iteration
time2->32364.22 microseconds per iteration
time3->32841.46 microseconds per iteration
time4->41252.14 microseconds per iteration
time5->35948.38 microseconds per iteration
time6->35133.78 microseconds per iteration
time7->55273.42 microseconds per iteration
Errors=0
Z0-check->34604 microseconds per iteration
Errors=0
Z8-check->142120 microseconds per iteration
21075 microseconds per iteration
See weave_test.tcl in the abstractlist-toys project to replay the above results.
This implementation will only work in Tcl 9.0.
-BrianCool things happen in Tcl 9.0. Looking at the code without any experience with abstract lists, I wonder:
* If "NULL) /* in operation */"[1] is the hook for the "in" operator, then implementing it might be useful (true if "in any list" instead of iterating over the woven list).
* Is there no "iterator" for abstract lists? I.e. would foreach call ObjIndex up to ObjLength times? But I guess taking this further would bloat the interface quite a bit: thinking of the Fibonacci example, the coroutine/iterator approach seems muchmore efficient than repeatedly calculating items.
* Nested/multiple indices are not supported for the time being[2]. I guess that was a design decision to get work done faster - the semantics and implementation would otherwise look quite clear, aren't they?
* I guess I know why lreverse is not implemented (the simple implementation does not account for ribbons of different sizes -> pad with empty strings before reversing?), but the same "error" seems present in lset[3]. Without possibility to test, Iassume from the code: set w [lweave [list 0 2 4] [list 1]]; lset w 5 5; puts $w; # 0 1 2 5 4 "", but expected 0 1 2 "" 4 5
[1] https://github.com/bgriffinfortytwo/abstractlist-toys/commit/94b0f4addc05e8f777833629847e5dcb685375ca#diff-49da01101cfbbe312c2e416d26cf05d2e8cf8168af8c8f98e31079a2801d4160R67
[2] https://github.com/bgriffinfortytwo/abstractlist-toys/commit/94b0f4addc05e8f777833629847e5dcb685375ca#diff-49da01101cfbbe312c2e416d26cf05d2e8cf8168af8c8f98e31079a2801d4160R267
[3] https://github.com/bgriffinfortytwo/abstractlist-toys/commit/94b0f4addc05e8f777833629847e5dcb685375ca#diff-49da01101cfbbe312c2e416d26cf05d2e8cf8168af8c8f98e31079a2801d4160R306
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (0 / 16) |
| Uptime: | 168:14:13 |
| Calls: | 12,096 |
| Calls today: | 4 |
| Files: | 15,003 |
| Messages: | 6,517,822 |