Расширенный интерпретатор TCL в TCL - PullRequest
4 голосов
/ 01 ноября 2011

Я реализовал множество расширений TCL для конкретного инструмента в области формальных методов (расширения реализованы на C, но я не хочу, чтобы решение опиралось на этот факт).Таким образом, пользователи моего инструмента могут использовать TCL для прототипирования алгоритмов.Многие из них представляют собой просто линейный список команд (они мощные), например:

my_read_file f
my_do_something a b c
my_do_something_else a b c

Теперь меня интересует время.Можно изменить сценарий так, чтобы он получал:

puts [time [my_read_file f] 1] 
puts [time [my_do_something a b c] 1] 
puts [time [my_do_something_else a b c] 1] 

Вместо этого я хочу определить процедуру xsource, которая выполняет сценарий TCL и время получения / записи для всех моих команд.Какой-то профилировщик.Я написал наивную реализацию, в которой основная идея заключается в следующем:

 set f [open [lindex $argv 0] r]
 set inputLine ""
 while {[gets $f line] >= 0} {
   set d [expr [string length $line] - 1]
   if { $d >= 0 } {
     if { [string index $line 0] != "#" } {
       if {[string index $line $d] == "\\"} {
         set inputLine "$inputLine [string trimright [string range $line 0 [expr $d - 1]]]"
       } else {
         set inputLine "$inputLine $line"
         set inputLine [string trimleft $inputLine]
         puts $inputLine
         puts [time {eval $inputLine} 1]
       }
       set inputLine ""
     }
   }
 }

. Он работает для линейного списка команд и даже допускает комментарии и команды в несколько строк.Но он терпит неудачу, если пользователь использует операторы if, циклы и определение процедур.Можете ли вы предложить лучший подход?Это должен быть чистый скрипт TCL с как можно меньшим количеством расширений.

Ответы [ 3 ]

5 голосов
/ 01 ноября 2011

Один из способов сделать то, что вы просите, - это использовать трассировки выполнения .Вот скрипт, который может сделать это:

package require Tcl 8.5

# The machinery for tracking command execution times; prints the time taken
# upon termination of the command. More info is available too (e.g., did the
# command have an exception) but isn't printed here.
variable timerStack {}
proc timerEnter {cmd op} {
    variable timerStack
    lappend timerStack [clock microseconds]
}
proc timerLeave {cmd code result op} {
    variable timerStack
    set now [clock microseconds]
    set then [lindex $timerStack end]
    set timerStack [lrange $timerStack 0 end-1]
    # Remove this length check to print everything out; could be a lot!
    # Alternatively, modify the comparison to print more stack frames.
    if {[llength $timerStack] < 1} {
        puts "[expr {$now-$then}]: $cmd"
    }
}

# Add the magic!
trace add execution source enterstep timerEnter
trace add execution source leavestep timerLeave
# And invoke the magic, magically
source [set argv [lassign $argv argv0];set argv0]
# Alternatively, if you don't want argument rewriting, just do:
# source yourScript.tcl

Тогда вы бы назвали это так (при условии, что вы поместили его в файл с именем timer.tcl):

tclsh8.5 timer.tcl yourScript.tcl

Имейте в виду, что этот сценарий имеет значительные накладные расходы, поскольку он запрещает многие стратегии оптимизации, которые обычно используются.Это не будет иметь большого значения для тех случаев, когда вы делаете реальный кусок кода в своем собственном C-коде, но когда в Tcl много циклов, вы заметите многое.

2 голосов
/ 01 ноября 2011

Вы можете обернуть ваши команды, которые вы хотите измерить.И называйте обертки точно так же, как и исходные (переименовывая оригинальные пробы раньше).После этого, когда инструментальная команда выполняется, она фактически выполняет оболочку, которая выполняет оригинальную процедуру и измеряет время выполнения.Пример ниже (Tcl 8.5).

proc instrument {procs} {
  set skip_procs {proc rename instrument puts time subst uplevel return}
  foreach p $procs {
    if {$p ni $skip_procs} {
      uplevel [subst -nocommands {
        rename $p __$p
        proc $p {args} {
          puts "$p: [time {set r [__$p {*}\$args]}]"
          return \$r
        }
      }]
    }
  }
}

proc my_proc {a} {
  set r 1
  for {set i 1} {$i <= $a} {incr i} {
    set r [expr {$r * $i}]
  }
  return $r
}

proc my_another_proc {a b} {
  set r 0
  for {set i $a} {$i <= $b} {incr i} {
    incr r $i
  }
  return $r
}

instrument [info commands my_*]

puts "100 = [my_proc 100]"
puts "200 = [my_proc 100]"
puts "100 - 200 = [my_another_proc 100 200]"
1 голос
/ 01 ноября 2011

Возможно, вы захотите взглянуть на команду «информация завершена».Он может сказать вам, выглядит ли то, что вы накопили до сих пор, с точки зрения наиболее распространенных синтаксических маркеров Tcl.Он будет иметь дело с вводом команды, который может быть распределен по нескольким физическим строкам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...