简体   繁体   中英

Recursive procedure calls in TCL

I have a tcl environment made up of many tcl files and procedures all under the same namespace. One of that procedure is developed so that it recall itself recursively according to the input parameters. The synopsys is:

myproc *subcmd arg1*

where:

  • subcmd is a subcommand
  • arg1 is a register

arg1 may be a normal register or the all register which lead to submit the subcmd subcommand to the overall allowed regiters. The reporting subcommand are get_status/get_delta/get_mask and the worst case is represented by the get_value subcommand which recall in succession the previous subcommand. If the register all is provided, the procedure level stack is the following:

myproc get_value all
   myproc get_value reg1
      myproc get_status reg1
      myproc get_delta reg1
      myproc get_mask reg1
   myproc get_value reg2
      myproc get_status reg2
      myproc get_delta reg2
      myproc get_mask reg2
   ...
   myproc get_value reg(n-1)
      myproc get_status reg(n-1)
      myproc get_delta reg(n-1)
      myproc get_mask reg(n-1)
   myproc get_value reg(n)
      myproc get_status reg(n)
      myproc get_delta reg(n)
      myproc get_mask reg(n)

This way I get a slow execution of the undermost execution. I tryed to browse on internet to get information on how increase the performance without results. A possible solution seems to be the apply command (parents of lambda function) but I guess it is too simple for my purpose and that other means may satisfy my problem. Any suggest?

At the moment I recall the procedure itself with the inner statement:

eval {myproc *subcmd* arg1}

Edit log and additional information

I've modified the code so that the trace is faithful to the real one. Moreover I post some slices of the source code so you can see how the procedures are called effectively:

# this is the call statement for 'all' register.
# exp_opt1_list holds reg1, reg2, ..., reg(n)
# the other parameter are not worth to explain, they are parsed above
# *callprocname* is a procedure that return back the name of the call procedure which reside 1 level above. Practically this procedure is shared by identical procedures at upper level which differ only for the lists of registers. So callprocname gives the name of the upper procedure that originally called that core procedure
foreach opt1_scan $exp_opt1_list($sub_cmd) {
    if {($opt1  == "all") && $opt1_scan != "all" } {
       set scan_result_list [string tolower [callprocname]] $sub_cmd $tag1 $tag2 $tag3 $opt1_scan $local_verbose -loop]
       [...]
    }
}

The lines where the follow up get_* reside are the following (plus a clear_delta that clean up the register):

if {[string equal -nocase GET_VALUE $sub_cmd]} {
    set result [list]
    lappend result [eval [list [string tolower [callprocname]] get_status $tag1 $tag2 $tag3 $opt1 $local_verbose]]
    lappend result [eval [list [string tolower [callprocname]] get_delta  $tag1 $tag2 $tag3 $opt1 $local_verbose]]
                   eval [list [string tolower [callprocname]] clr_delta  $tag1 $tag2 $tag3 $opt1 $operation_verbose]
    lappend result [eval [list [string tolower [callprocname]] get_mask   $tag1 $tag2 $tag3 $opt1 $local_verbose]]
}

As the comment within the first code trunk state, there are many nested procedure though most of them are trivial or in other word are not computationally demanding. As a matter of fact there are a lot of level that the structure involves, let's say about 6 levels (service + core) because everything start from service level which calls the core and if the all register is involved the service level is called again directly from the core, so we have service -> core -> service -> core and so on.

Is this rough explanation sufficient?

you almost guessed how my code work, but I modify it in order to get it closer to mine.


Does your code look anything like this?

proc myproc {subcmd reg} {
    set n 5
    switch -- $subcmd {
        get_value {
            if {$reg eq "all"} {
               for {set i 1} {$i < $n} {incr i} {
                    myproc get_status reg$i
                    myproc get_delta reg$i
                    myproc get_mask reg$i
               }
            }
        }
        get_status {
            if {$reg eq "all"} {
                for {set i 1} {$i < $n} {incr i} {
                    myproc get_status reg$i
                }
            } else {
                # do what get_status does with a single register
            }
        }
        get_delta {
            if {$reg eq "all"} {
                for {set i 1} {$i < $n} {incr i} {
                    myproc get_delta reg$i
                }
            } else {
                # do what get_delta does with a single register
            }
        }
        get_mask {
            if {$reg eq "all"} {
                for {set i 1} {$i < $n} {incr i} {
                    myproc get_mask reg$i
                }
            } else {
                # do what get_mask does with a single register
            }
        }
    }
}

I'm asking because we really need get an idea of what your code looks like to be able to advise you.

A call trace for the above code:

myproc get_value all
    myproc get_status reg1
    myproc get_delta reg1
    myproc get_mask reg1
    myproc get_status reg2
    myproc get_delta reg2
    myproc get_mask reg2
    myproc get_status reg3
    myproc get_delta reg3
    myproc get_mask reg3
    myproc get_status reg4
    myproc get_delta reg4
    myproc get_mask reg4

BTW I've tried to "profile" my code with [time] and it just seems that it slowness is ascribed to be for nested calls...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM