Loop optimisation

Difference between version 1 and 3 - Previous - Next
[FM] : Here is a proc whose only goal is to "inline" one lmap loops, when its boundaries are known at compile time. <<br>>
In short, it's about the kind of `lmap` we write just for convenience, to shorten the code, because we are lazy to write long command. For instance :
======
proc essai.1 {L} {
   lmap i {1 2 3} {
      lindex $L $i
   }
}
======
The optproc below just creates a proc that is removing the loop totally to produce a body like :
======
list [lindex $L 1] [lindex $L 2] [lindex $L 3]
======
Very simple proof of concept : 
======
proc optproc {name args body} {
    rename lmap _lmap
    proc lmap args {
        set body [lpop args]
        set script {}
        foreach {vars listval} $args {
            if {! string match {$*} $listval} {
                # not a variable : $listval"
                if {! string match {\[*\]} $listval} {
                   # "not a command "
                   # "litteral loop :"
                    set script {list }
                    set len [llength $listval]
                    foreach val $listval {
                        foreach var $vars {
                            set string [string map [list \$$var $val] $body]
                        }
                        append script \ \[$string\]
                    }
                }
            }
        }
        return $script
    }
    set script [eval $body]   
    uplevel [list proc $name $args $script]
    rename lmap {}
    rename _lmap lmap
}
======
And we can use it as :
======
optproc essai.2 {L} {
   lmap i {1 2 3} {
      lindex $L $i
   }
}
======
Then we can measure the difference :
======
set L {1 2 3 4 5 6}
timerate {essai.1 $L}
# 4.991574 µs/# 200337 # 200337 #/sec 999.997 net-ms
timerate {essai.2 $L}
0.445589 µs/# 2244217 # 2244219 #/sec 999.999 net-ms
======
This kind of « convenience lmap » is more than one order of magnitude slower !

Is there any possibility to optimise the lmap code when the loop is known at compile time ?

[APN] The [Sugar] package which is a general macro implementation may interest you.
[FM] [Sugar] looks very interesting and may be one good workaround solution. But the links are dead on the wiki page, so can't check it... Personnally, I prefer this to be natively supported. Very often, i've written things like :
======
foreach e {very long list of elements} {
    Typedlist create $e
}
======
I think Tcl API has everything needed to detect such a situation (ex: func Tcl_WordknowAtCompileTime). If the list values are all known at compile time, rather than creating a loop in bytecode, which will be slower, it should be possible to loop in the C code during the compilation to create the bytecode accordingly. The difficulty is to subst in the body only on the loop variables.
... Some times later...
Or, if ever, maybe a new ensemble command ?
======
inline lmap e {const list} {
....
}
======
So there would be a man page explaining this.