00001 -*- mode: text; fill-column: 70; indent-tabs-mode: nil -*- 00002 $Id: README 4939 2005-06-28 07:16:30Z zhanshi $ 00003 00004 ------------------------------------ 00005 README for C++/Tcl scripting engine 00006 ------------------------------------ 00007 00008 NOTE: If you're reading this file as a flat text file, you'll notice 00009 that there is some additional markup, which is used to generate an 00010 HTML version of this file with the doxygen tool. Nevertheless, this 00011 file should remain readable as plain text. 00012 00013 /** \page howto-scripting Introduction to the C++/Tcl scripting engine 00014 00015 This is an introduction to a scripting engine that allows the C++ 00016 objects in the iLab Neuromorphic Vision Toolkit to be manipulated 00017 through a tcl scripting interface. 00018 00019 Table of contents: 00020 00021 - \ref fortheimpatient 00022 - \ref overview 00023 - \ref neuroscript 00024 - \ref walkthrough 00025 00026 <!--############################################################--><hr> 00027 00028 \section fortheimpatient 1. For the impatient 00029 00030 For the impatient: 00031 00032 - the main program for the script interpreter is bin/invt, source 00033 code in Script/invt.C (underneath src/ directory) 00034 - additional script interface code is in src/Script 00035 - infrastructure code for the c++/tcl scripting engine is drawn from 00036 the groovx (http://ilab.usc.edu/rjpeters/groovx/) project, and 00037 sits in src/rutz, src/nub, and src/tcl 00038 - there is a script implementation of ezvision in bin/ezvision.tcl, 00039 which you can run with bin/invt: "./bin/invt bin/ezvision.tcl 00040 --args --that --you --normally --pass --to --ezvision" 00041 - this script implementation (ezvision.tcl) passes both the short and 00042 long ezvision test suties 00043 00044 <!--############################################################--><hr> 00045 00046 \section overview 2. Overview 00047 00048 In saliency/ezvision.tcl is a translation of ezvision.C into 00049 script. The script version passes the ezvision test suite (do "make 00050 bin/invt; cd tests; ./test_scriptvision_blackbox.tcl") and the long 00051 movies test suite ("make bin/invt; cd tests; 00052 ./testlong_script_movies_blackbox.tcl"). It's less than 2x slower 00053 than the full C++ ezvision -- short ezvision test takes 13s with the 00054 C++ version vs. 23s with the tcl script (77% slower), while the long 00055 ezvision movies test takes 326s in C++ and 363s in Tcl (11% slower). 00056 00057 This system is based on some of the infrastructure from "GroovX" 00058 (http://ilab.usc.edu/rjpeters/groovx/). The imported source from 00059 groovx is in src/rutz (utility stuff in "rutz::" namespace), src/nub 00060 (object base class plus smart pointers in "nub::" namespace), and 00061 src/tcl (facilities for exposing c++ code through tcl scripts). 00062 00063 The only changes that are required to existing invt code are (1) 00064 ModelComponent derives from a new base class, nub::object, and (2) 00065 we use a new smart ptr type (nub::soft_ref) instead of SharedPtr to 00066 hold ModelComponent derivatives. More info below. In any case, the 00067 scripting layer is not intrusive in the sense that none of the core 00068 code (Image, Neuro, Devices, Media, etc.) has to know anything about 00069 scripting or scriptability, except that we have to use the right 00070 smart pointer types. The syntax for using the smart pointers 00071 themselves is identical between SharedPtr and nub::soft_ref. 00072 00073 The script interface for invt is defined in src/Script. The main 00074 program is in Script/invt.C -- it basically gives a list of the 00075 script modules that we want to load, and then calls off to some 00076 library in src/tcl to load those packages and then start looping, 00077 processing user input. 00078 00079 Then there are the script modules in src/Script. For now, we have: 00080 00081 - Script/ImageScript.C 00082 - Script/ImageScript.H 00083 - Script/MediaScript.C 00084 - Script/MediaScript.H 00085 - Script/ModelScript.C 00086 - Script/ModelScript.H 00087 - Script/NeuroScript.C 00088 - Script/NeuroScript.H 00089 00090 For each C++ class that we want to export, we define a 00091 Classname_Init() function that sets up the script commands for that 00092 class. These *_Init() functions are what we enumerate in the main 00093 program. However, it is also possible to dynamically load shared 00094 libraries containing *_Init() functions into a running tcl 00095 program. That means that we could have true plug-ins, where new code 00096 never has to be linked into the main program, it just gets loaded at 00097 runtime. 00098 00099 <!--############################################################--><hr> 00100 00101 \section neuroscript 3. Script interface example: NeuroScript 00102 00103 Look at Script/NeuroScript.H. There we have Brain_Init() and 00104 Stdbrain_Init() (note that the functions are extern "C" and have the 00105 first letter capitalized, and all the rest lowercase -- this is 00106 important to allow dynamic loading -- that way if you tell tcl a 00107 package name, it can deterministically figure out what the name of 00108 the *_Init() function should be, and find it with dlsym()). Then 00109 there are also some functions for installing component types into 00110 the object factory, so that within a script we can create objects 00111 from their class name. 00112 00113 On to Script/NeuroScript.C. 00114 00115 Here's an overview of how the object-management system works. We use 00116 intrusive reference counting -- the ref count is within the object 00117 itself; we inject this into the ModelComponent hierarchy by having 00118 ModelComponent derive from nub::object. Because the reference count 00119 is intrusive, we don't have to worry about constructing multiple 00120 independent smart pointers from the same object, like we do with 00121 SharedPtr -- in this case, they always share the same reference 00122 count. We have nub::ref and nub::soft_ref smart pointer classes, 00123 nub::ref is "strong", meaning that its pointee can never be 00124 null. nub::soft_ref allows null pointees. Each nub::object gets a 00125 unique nub::uid when it is created. When we construct a nub::ref or a 00126 nub::soft_ref from an nub::object, we have the option to insert that 00127 nub::object into the nub::objectdb, which is essentially a 00128 std::map<nub::uid, soft_ref<nub::object>>. That is, it allows us to 00129 retrieve an object given just its nub::uid. This is how we expose objects 00130 to the tcl script -- via their nub::uid. So we are essentially using 00131 integer handles to the objects. 00132 00133 (BTW, I also have the ability to do weak pointers -- like weak 00134 references in Java -- that don't prevent their pointee from being 00135 destroyed. Instead, the weak pointers just silently become null when 00136 their pointee goes away -- so there's no risk of dangling pointers, 00137 and the weak pointers can be used to allow safe "back-pointers" up 00138 the object hierarchy without creating a strong-reference cycle that 00139 would prevent the entire cycle of objects from being destroyed.) 00140 00141 OK, skip down to Brain_Init() in Script/NeuroScript.C. We start 00142 a package with GVX_PKG_CREATE(). We inherit_pkg("ModelComponent") to 00143 bring in the script commands that were defined for ModelComponent 00144 (so we get "start" etc., see Modecomponent_Init() in 00145 Script/ModelScript.C to see what is exposed). We 00146 def_basic_type_cmds(), which allows us in script to find all current 00147 Brain objects, count them, query whether some object is a Brain or 00148 not. We registerComponentCreator<Brain>(), which puts a function 00149 into the nub::obj_factory (nub/objfactory.h) that knows how to 00150 create Brain objects. The creator function knows how to find a 00151 global ModelManager that is defined in ModelScript.C, and use that 00152 ModelManager to create the Brain. 00153 00154 Then we start getting into the pkg->def()'s. Here is where we are 00155 defining the script commands. The pkg->def() takes 4 params: (1) the 00156 name of the script command, (2) a human-readable string describing 00157 the arguments that the command takes, (3) the pointer-to-member or 00158 function address of the C++ function we want to make scriptable, and 00159 (4) SRC_POS, which is just a wrapper around __FILE__ and __LINE__ 00160 that lets us figure out, from inside a Tcl script, where a 00161 particular script wrapper was defined in the C++; that can be 00162 helpful for debugging. 00163 00164 What happens with pkg->def() is that we set up a big template thingy 00165 that gets the tcl script call as input (just a list of tcl objects), 00166 figures out how to convert those to native C++ types, then calls the 00167 C++ function, then converts the result back to Tcl. The command also 00168 does other nice stuff like ensure that the number of input arguments 00169 is correct, etc. 00170 00171 The c++ <-> tcl conversions are handled by a family of overloads 00172 that know how to do the individual conversions (basically like our 00173 convertToString() and convertFromString()). E.g., there is a 00174 converter from nub::ref->Tcl that returns the int value of the 00175 object's nub::uid, and a convert from Tcl->nub::ref that gets an int, 00176 then looks up an object with that nub::uid in the nub::objectdb, and 00177 casts it to the desired type. 00178 00179 Most of the time, the builtin c++ <-> tcl conversions will do the 00180 right thing, and it's possible to do a pkg->def() with just the 00181 address of an existing member or free function. Other times, it 00182 makes more sense to write a thin c++ wrapper, that does some more 00183 intelligent c++/tcl translation, that gets passed to pkg->def() 00184 instead. See for example brainEvolve() in Script/NeuroScript.C, 00185 which wraps Brain::evolve() so that instead of returning values 00186 through non-const reference parameters (which doesn't translate well 00187 into a script language with primarily value semantics), we just 00188 return all the return values in a tcl list, which we can unpack on 00189 the script side (see ezvision.tcl). 00190 00191 BTW, casting brings up the issue of exceptions -- each script 00192 command call is internally wrapped in a try/catch block (see 00193 tcl::command_group::invoke_raw in tcl/commandgroup.cc around line 00194 382), so if any exceptions are raised while tcl is calling back to 00195 c++, the command machinery will catch the exception and translate 00196 into a human-readable error message on the script interpreter 00197 console (i.e., the program doesn't abort). 00198 00199 00200 <!--############################################################--><hr> 00201 00202 \section walkthrough 4. Walkthrough with bin/invt 00203 00204 Here's an annotated session transcript. You can play along by doing 00205 "make bin/invt", then run "./bin/invt -nw" (the -nw means 00206 "nowindow"). The "./bin/invt 1>>>" is the prompt. You can use 00207 readline/history commands just like in bash (i.e., up-arrow to get 00208 previous commands, Esc-Del to kill the word behind the cursor, 00209 etc.). You can also use tab completion, but the only thing it will 00210 complete is filenames (i.e. it doesn't know how to complete script 00211 command names). Here goes: 00212 00213 \code 00214 00215 [iLab9 19:51 7687]$ ./bin/invt -nw 00216 00217 ########################################################################### 00218 ### iLab C++ Neuromorphic Vision Toolkit 3.1 (Jun 27 2005) ### 00219 ########################################################################### 00220 ### Copyright (c) 2001-2005 iLab and the Univ. of Southern California ### 00221 ### <http://ilab.usc.edu> ### 00222 ### Copyright (c) 1998-2004 Rob Peters and Caltech ### 00223 ### <http://ilab.usc.edu/rjpeters/groovx/> ### 00224 ### iLab C++ Neuromorphic Vision Toolkit is free software, covered by ### 00225 ### the GNU General Public License, and you are welcome to change it ### 00226 ### and/or distribute copies of it under certain conditions. ### 00227 ########################################################################### 00228 00229 startup time (tcl+tk) 0.009s (user) 0.002s (sys) 0.010s (wall) 00230 startup time ( iNVT) 0.030s (user) 0.000s (sys) 0.030s (wall) 00231 00232 \endcode 00233 00234 Let's find all current objects 00235 00236 \code 00237 ./bin/invt 1>>> Obj::find_all 00238 1 00239 \endcode 00240 00241 OK, there is one object, and its nub::uid is 1. Now let's see what 00242 its type is: 00243 00244 \code 00245 ./bin/invt 2>>> Obj::type [Obj::find_all] 00246 ModelManager 00247 \endcode 00248 00249 Cool, it's the global ModelManager that is constructed in 00250 Modelmanager_Init(). Let's see what params/subcomponents it 00251 currently has. We do "ModelManager::printout 1", where 1 is the 00252 nub::uid of the ModelManager. 00253 00254 \code 00255 ./bin/invt 3>>> ModelManager::printout 1 00256 model: ShowHelpMessage = true 00257 model: DebugMode = false 00258 model: UsingFPE = false 00259 model: TestMode = false 00260 model: TextLogFile = 00261 model: LoadConfigFile = 00262 model: SaveConfigFile = 00263 \endcode 00264 00265 In "real" code, we'd assign 1 to a variable with "set mm 00266 [ModelManager::find_all]", and then do "ModelManager::printout 00267 $mm". Let's do that now: 00268 00269 \code 00270 ./bin/invt 4>>> set mm [ModelManager::find_all] 00271 1 00272 ./bin/invt 5>>> Obj::type $mm 00273 ModelManager 00274 \endcode 00275 00276 Now let's try to "printout" an invalid nub::uid: 00277 00278 \code 00279 ./bin/invt 6>>> ModelManager::printout 3 00280 rutz::error caught at src/tcl/commandgroup.cc:442: 00281 ModelManager::printout: at src/nub/refdetail.h:243: 00282 attempted to access invalid object in soft_ref<ModelComponent> 00283 \endcode 00284 00285 OK, it caught our invalid access attempt. The source file line 00286 numbers show where the exception was caught 00287 (src/tcl/commandgroup.cc:442) and where it was thrown 00288 (src/nub/refdetail.h:243) 00289 00290 Let's try to make a new object. First we try to create an object of 00291 type "blah". 00292 00293 \code 00294 ./bin/invt 7>>> new blah 00295 rutz::error caught at src/tcl/tclcommandgroup.cc:442: 00296 new: at src/rutz/factory.h:224: 00297 known keys are: 00298 Brain 00299 InputFrameSeries 00300 OutputFrameSeries 00301 StdBrain 00302 unknown object type 'blah' 00303 \endcode 00304 00305 OK, it doesn't know anything about 'blah', but it tells us the types 00306 that it does know about. These are the ones that are in the 00307 nub::obj_factory, in which we registered them with 00308 registerComponentCreator(). 00309 00310 Now let's make a brain, and capture its nub::uid in a variable 00311 called 'mybrain' 00312 00313 \code 00314 ./bin/invt 8>>> set mybrain [new StdBrain] 00315 2 00316 ./bin/invt 9>>> puts "the value of mybrain is $mybrain" 00317 the value of mybrain is 2 00318 \endcode 00319 00320 OK, its nub::uid is 2. Now if we ask to find all Brain objects: 00321 00322 \code 00323 ./bin/invt 9>>> StdBrain::find_all 00324 2 00325 ./bin/invt 10>>> Brain::find_all 00326 2 00327 \endcode 00328 00329 We get our StdBrain back from StdBrain::find_all. Note that 00330 Brain::find_all also finds our StdBrain even though StdBrain!=Brain, 00331 because StdBrain is a derived type of Brain. 00332 00333 \code 00334 ./bin/invt 10>>> Obj::type $mybrain 00335 StdBrain 00336 \endcode 00337 00338 There's its type. Now let's do Obj::find_all again: 00339 00340 \code 00341 ./bin/invt 10>>> Obj::find_all 00342 1 2 3 4 5 6 7 8 9 10 11 12 13 00343 \endcode 00344 00345 and get the object types: 00346 00347 \code 00348 ./bin/invt 11>>> Obj::type [Obj::find_all] 00349 ModelManager StdBrain RetinaConfigurator SaccadeControllerConfigurator 00350 VisualCortexConfigurator SaliencyMapConfigurator 00351 TaskRelevanceMapConfigurator AttentionGuidanceMapConfigurator 00352 WinnerTakeAllConfigurator SimulationViewerConfigurator 00353 InferoTemporalConfigurator GistEstimatorConfigurator ShapeEstimator 00354 \endcode 00355 00356 So there are all of the subcomponents of the StdBrain object that we 00357 just constructed. 00358 00359 Let's play around with our StdBrain some more. Query some of its 00360 param values: 00361 00362 \code 00363 ./bin/invt 12>>> StdBrain::param $mybrain IORtype 00364 Auto 00365 \endcode 00366 00367 Now let's get a different param value -- and note that this is a 00368 good chance to make sure you're using the readline history editing 00369 feature of bin/invt: hit the up-arrow to retrieve the previous 00370 command (the one with 'IORtype'), then hit Esc-Delete to kill the 00371 'IORtype' word, then type 'UseRandom' and hit Enter: 00372 00373 \code 00374 ./bin/invt 13>>> StdBrain::param $mybrain UseRandom 00375 true 00376 \endcode 00377 00378 Now, we can also change its UseRandom value 00379 00380 \code 00381 ./bin/invt 14>>> StdBrain::param $mybrain UseRandom 0 00382 1 00383 \endcode 00384 00385 (Note that the '1' returned here is the 'bool' value indicating 00386 whether a param named 'UseRandom' was found). Now let's re-query and 00387 see that our change took effect: 00388 00389 \code 00390 ./bin/invt 15>>> StdBrain::param $mybrain UseRandom 00391 false 00392 \endcode 00393 00394 OK, its value is now 'false'. Now let's try to give a bogus param 00395 value: 00396 00397 \code 00398 ./bin/invt 16>>> StdBrain::param $mybrain UseRandom 3 00399 StringConversions::convertFromString: Bogus format: 3 -- IGNORED 00400 1 00401 \endcode 00402 00403 Let's make a "typo" and query a non-existent param: 00404 00405 \code 00406 ./bin/invt 16>>> StdBrain::param $mybrain nosuchparam 00407 [Brain]::getModelParamString: No parameter named 'nosuchparam' -- IGNORING 00408 <unknown> 00409 \endcode 00410 00411 We can use the "?" function to get usage information about a command: 00412 00413 \code 00414 ./bin/invt 18>>> ? Brain::param 00415 Brain::param resolves to ::ModelComponent::param 00416 ::ModelComponent::param objref paramname (argc=[3..3]) 00417 ::ModelComponent::param objref paramname paramvalue (argc=[4..4]) 00418 (defined at src/Script/ModelScript.C:118) 00419 \endcode 00420 00421 Here we see that Brain::param has two overloads, one that returns 00422 the current value, and one that sets a new value. The proper 00423 overload is selected at runtime based on the argument count. 00424 00425 We can apply "?" to itself: 00426 00427 \code 00428 ./bin/invt 18>>> ? ? 00429 ? resolves to ::? 00430 ::? cmd_name (argc=[2..2]) 00431 (defined at src/tcl/tclpkg-misc.cc:156) 00432 \endcode 00433 00434 We can use "?" to see (1) which package originated the command, and 00435 (2) where it is defined. Here we see that Brain has inherited 00436 subCompByName from ModelComponent, and that it is defined in 00437 src/Script/ModelScript.C: 00438 00439 \code 00440 ./bin/invt 19>>> ? Brain::subCompByName 00441 Brain::subCompByName resolves to ::ModelComponent::subCompByName 00442 ::ModelComponent::subCompByName objref tagname (argc=[3..3]) 00443 (defined at src/Script/ModelScript.C:108) 00444 \endcode 00445 00446 We can query if an object is a (subclass of) a particular type: 00447 00448 \code 00449 ./bin/invt 17>>> ModelComponent::is $mybrain 00450 1 00451 00452 ./bin/invt 18>>> StdBrain::is $mm 00453 0 00454 \endcode 00455 00456 Try to get modules out of Brain: 00457 00458 \code 00459 ./bin/invt 20>>> StdBrain::module $mybrain Retina 00460 0 00461 ./bin/invt 21>>> StdBrain::module $mybrain VisualCortex 00462 0 00463 \endcode 00464 00465 OK, those returned null objects because the brain hasn't been 00466 start()ed yet. 00467 00468 Now let's try to get a invalid module type (note that this is an 00469 example of what happens with an LFATAL, which throws an exception, 00470 which is caught and reported to the script interpreter): 00471 00472 \code 00473 ./bin/invt 22>>> StdBrain::module $mybrain Frobnaz 00474 Brain::module: Invalid module type Frobnaz -- ABORT 00475 -- ABORT. 00476 exception of unknown type caught at src/tcl/commandgroup.cc:442: 00477 StdBrain::module: 00478 00479 ./bin/invt 23>>> StdBrain::hasModule $mybrain Frobnaz 00480 Brain::hasModule: Invalid module type Frobnaz -- ABORT 00481 -- ABORT. 00482 exception of unknown type caught at src/tcl/commandgroup.cc:442: 00483 StdBrain::hasModule: 00484 \endcode 00485 00486 Play around with our ModelManager some more: 00487 00488 \code 00489 ./bin/invt 25>>> ModelManager::numSubComp $mm 00490 0 00491 ./bin/invt 26>>> ModelManager::tagName $mm 00492 model 00493 ./bin/invt 27>>> ModelManager::descriptiveName $mm 00494 iLab Neuromorphic Vision Toolit 00495 \endcode 00496 00497 Add our StdBrain to the ModelManager: 00498 00499 \code 00500 ./bin/invt 28>>> ModelManager::addSubComponent $mm $mybrain 00501 0 00502 \endcode 00503 00504 and now see how many subcomponents it has (you can use up-arrow to 00505 retrieve the identical call that we did 3 or 4 steps back): 00506 00507 \code 00508 ./bin/invt 29>>> ModelManager::numSubComp $mm 00509 1 00510 \endcode 00511 00512 Get the StdBrain back out of the ModelManager: 00513 00514 \code 00515 ./bin/invt 31>>> ModelManager::subCompByIndex $mm 0 00516 2 00517 \endcode 00518 00519 Try to get an out-of-bounds subcomponent: 00520 00521 \code 00522 ./bin/invt 32>>> ModelManager::subCompByIndex $mm 123 00523 [iLab Neuromorphic Vision Toolit]::subComponent: 00524 iLab Neuromorphic Vision Toolit: request for 00525 subcomponent 123 but I have only 1 -- FATAL 00526 -- ABORT. 00527 exception of unknown type caught at src/tcl/commandgroup.cc:442: 00528 ModelManager::subCompByIndex: 00529 \endcode 00530 00531 See what's in the model manager now: 00532 00533 \code 00534 ./bin/invt 33>>> ModelManager::printout $mm 00535 model: ShowHelpMessage = true 00536 model: DebugMode = false 00537 model: UsingFPE = false 00538 model: TestMode = false 00539 model: TextLogFile = 00540 model: LoadConfigFile = 00541 model: SaveConfigFile = 00542 model.Brain: IORtype = Auto 00543 model.Brain: UseRandom = false 00544 model.Brain: FOAradius = -1 00545 model.Brain: FoveaRadius = -1 00546 model.Brain: SimulationTimeStep = 0.0001 00547 model.Brain: LevelSpec = 0-0,0-0,0 00548 model.Brain: BrainBoringDelayInMs = 200 00549 model.Brain: BrainBoringSMmv = 3 00550 model.Brain: BrainMaxWinMv = 5 00551 model.Brain: BlankBlink = false 00552 model.Brain: BrainSaveObjMask = false 00553 model.Brain: BrainTooManyShifts = 0 00554 model.Brain: BrainTooMuchTime = 0 00555 model.Brain: BrainSaveWinnerFeatures = false 00556 model.Brain.ShapeEstimator: ShapeEstimatorMode = FeatureMap 00557 model.Brain.ShapeEstimator: ShapeEstimatorSmoothMethod = Gaussian 00558 model.Brain.RetinaConfigurator: RetinaType = Std 00559 model.Brain.SaccadeControllerConfigurator: SaccadeControllerType = None 00560 model.Brain.VisualCortexConfigurator: VisualCortexType = None 00561 model.Brain.SaliencyMapConfigurator: SaliencyMapType = None 00562 model.Brain.TaskRelevanceMapConfigurator: TaskRelevanceMapType = None 00563 model.Brain.AttentionGuidanceMapConfigurator: AttentionGuidanceMapType = None 00564 model.Brain.WinnerTakeAllConfigurator: WinnerTakeAllType = None 00565 model.Brain.SimulationViewerConfigurator: SimulationViewerType = None 00566 model.Brain.InferoTemporalConfigurator: InferoTemporalType = None 00567 model.Brain.GistEstimatorConfigurator: GistEstimatorType = None 00568 \endcode 00569 00570 change the ModelManager's tag name: 00571 00572 \code 00573 ./bin/invt 34>>> ModelManager::tagName $mm "someOtherName" 00574 ./bin/invt 35>>> ModelManager::tagName $mm 00575 someOtherName 00576 \endcode 00577 00578 Now, after all that, here's a shorthand syntax. For objects that 00579 have a tcl package, we can use "-> $obj cmdname" as shorthand for 00580 "<type>::cmdname $obj", where <type> is whatever is returned from 00581 "Obj::type $obj". 00582 00583 That is, "-> $mm printout " is equivalent to "ModelManager::printout 00584 $mm", because [Obj::type $mm] is "ModelManager": 00585 00586 \code 00587 ./bin/invt 36>>> -> $mm tagName 00588 someOtherName 00589 ./bin/invt 37>>> -> $mm tagName "someNewName" 00590 someOtherName 00591 ./bin/invt 36>>> -> $mm tagName 00592 someNewName 00593 \endcode 00594 00595 Finally, when we're done: 00596 00597 \code 00598 ./bin/invt 39>>> exit 00599 \endcode 00600 00601 After we exit the interpreter, it leaves behind a prof.out file 00602 showing (1) average microseconds per call, (2) number of calls, (3) 00603 self microseconds, and (4) self+child microseconds, for each of the 00604 script commands (do "sort -n +2 prof.out | tail -20") to sort by 00605 total self microseconds. Here's one example left behind after 00606 running tests/test_scriptvision_blackbox.tcl, showing that 00607 Brain::evolve was called 1799 times, most of the time was spent in 6 00608 calls to Brain::input, etc. 00609 00610 \code 00611 $ sort -n +2 prof.out | tail -20 00612 291 1 291 291 tcl/ModelManager::extraArgs 00613 123 3 369 369 tcl/ModelComponent::addSubComponent 00614 242 2 484 484 tcl/FrameSeries::fileStem 00615 246 2 492 492 tcl/Image::byte 00616 1281 1 1281 1281 tcl/ModelManager::find_all 00617 3843 8 1741 30745 tcl/::-> 00618 689 6 4139 4139 tcl/InputFrameSeries::readRGB 00619 1955 3 5866 5866 tcl/Obj::new 00620 5986 1 5986 5986 tcl/ModelComponent::start 00621 1346 6 8080 8080 tcl/Brain::saveResults 00622 6 1794 12075 12075 tcl/FrameSeries::isMultiframe 00623 21874 1 21874 21874 tcl/ModelManager::parseCommandLine 00624 6 3598 23255 23255 tcl/FrameSeries::didDisplayFrames 00625 7 3599 27831 27831 tcl/FrameSeries::update 00626 49 1799 88937 88937 tcl/Brain::evolve 00627 162231 6 973388 973388 tcl/Brain::input 00628 \endcode 00629 00630 */