Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Jesse Wursten
    started a topic -timeit- now on SSC: A same-line timer

    -timeit- now on SSC: A same-line timer

    Dear all

    -timeit- is now available on SSC, thanks as usual to Kit Baum. It is a simple wrapper program that replaces
    Code:
    timer on 1
    reg y x [note: any command here works, this is just an example]
    timer off 1
    by

    Code:
    timeit 1: reg y x
    In my opinion its main advantage is that your code can still look clean even with timers - the timer on/off lines made everything look super messy in my opinion, making it harder to debug or share code.

    Currently the timer will keep running if your command returns an error. I will hopefully fix this in a future version, though suggestions on how to do this are welcome.

  • Jesse Wursten
    replied
    Nice catch. This should be fixed in the version on its way to SSC now. I've also attached it here.
    Attached Files

    Leave a comment:


  • Joro Kolev
    replied
    -timeit- returns an error when there is empty space before the semicolon.

    E.g.,

    Code:
    . clear
    
    . timer clear
    
    . set obs 10
    number of observations (_N) was 0, now 10
    
    . timeit 1 : gen u = runiform()
    = invalid name
    r(198);
    
    .
    This is not an end-of-the-world problem, but it is a bit inconsistent with usual Stata behaviour where additional empty spaces typically do not cause issues. I do use empty spaces a lot to improve readability of code.

    Leave a comment:


  • Jesse Wursten
    replied
    Hmm, you seem to be correct. I should be able to fix this.

    EDIT: I think I fixed it. Updated version is en route to SSC, also attached below.
    Attached Files
    Last edited by Jesse Wursten; 27 Nov 2018, 03:19.

    Leave a comment:


  • Santiago Cantillo
    replied
    Your right, I tried that and it worked. But if the name of the do file has spaces it won't work.
    Code:
     
     timeit 1: do "sleep 2000.do"
    produces type mismatch "r(109)".
    I also received this error message when trying to merge a file that had spaces in the name.
    Perhaps the issue occurs when doing any operation involving file names with spaces?

    Leave a comment:


  • Jesse Wursten
    replied
    Originally posted by Santiago Cantillo View Post
    Hi Jesse Wursten . Thanks very much for your program.
    It just occured to me that it would also be really helpful if one could use timeit before running external do files
    (e.g. timeit 1 dofile1 : do foo.do).
    Code:
    timeit 1: do sleep2000.do
    That worked for me?

    Leave a comment:


  • Santiago Cantillo
    replied
    Hi Jesse Wursten . Thanks very much for your program.
    It just occured to me that it would also be really helpful if one could use timeit before running external do files
    (e.g. timeit 1 dofile1 : do foo.do).

    Leave a comment:


  • Jesse Wursten
    replied
    A new version of timeit is now available on ssc. There were three major changes.
    1. You can now calculate incremental timers. By default, timer list only gives you the total runtime of a particular timer, the timeit command also produces the runtime of the most recent iteration.
    2. Timeit now adds the relevant runtime values to r(), that is, if you type timeit 1 somename: cmd, r() will now contain the results from cmd, as well as r(t1), r(somename) and r(delta_t1). This means you can immediately process the runtimes in whichever fashion you desire.
    3. Bugfixes. The timeit command was having issues with quotes in the cmd and struggling to retain the right results in r(). The latter is surprisingly difficult. Now however, r() will contain whatever was in there before if your command did not alter r(), and whatever your command put in r() if it did do so. (As well as the timer results).
    If you use this command and get an unexpected result, please let me know. I have tried to subject it to very ludicrous commands and so far it's handled all of them, but the list is obviously not complete (e.g. I missed factor notations initially).

    Leave a comment:


  • Jesse Wursten
    replied
    Bug fix: -timeit- no longer returns an error when you use factor variable syntax (i. and so on).

    PS: If some user programmer reads this to figure out why their program is throwing errors on i. and so on, in my case it was due to the version statement, which messes with the factor variables if set to something lower than version 11.
    Last edited by Jesse Wursten; 27 Dec 2016, 14:53.

    Leave a comment:


  • Jesse Wursten
    replied
    Updates above are now on ssc, thanks as usual to the relentless Kit Baum!

    Leave a comment:


  • Jesse Wursten
    replied
    I've tried to incorporate all the suggestions.

    1. You can now specify a name as in timeit # name: cmd. The timer will be stored in scalar name.
    2. Extensive syntax checking, with automated suggestions covering most cases (if I missed any, please let me know)
    3. Any return results present will now persist through the timeit (some of the checks flush r() )
    4. The program will give a warning (but continue) if a timer is already running
    I do this by first checking the timer (e.g. r(t#) from timer list) and then checking it again after a timer off # command. If the timer was active, these times will diverge.
    5. The program will not respond to using a timer that was used before. This imo should be possible. E.g. using this in a loop.
    6. The timer will stop if the command leads to an error, or if the user presses break.

    Any more suggestions? Always welcome!

    PS: daniel klein I'm not entirely sure what you mean with the last point (capturing the rc code).
    Attached Files

    Leave a comment:


  • daniel klein
    replied
    In which situation would ("`theTimer'" == ":") be true? The second condition flags when the user didn't type a colon, right?
    If the user types

    Code:
    timeit : whatever
    omitting the timer number. Of course, the timer command later in the code will choke on this, so this check is not vital. One could think about allowing this syntax and merely listing the time instead of storing it in a timer. Something like

    Code:
    set rmsg on
    whatever
    set rmsg off
    3. I think the option to store the timer results is an interesting one. I suppose the easiest way to do this is to allow something along the lines of
    Code:
    timeit 5, name("fifthTimer"): cmd
    Code-wise that complicates things, but it's not insurmountable.
    Options seem a good idea, especially if you also want the user to explicitly specify to override a timer, that is already in use, or likewise to continue running this timer. An optional name could also be parsed as an optional second argument

    Code:
    timeit # [ name ] : cmd
    which would be easier/quicker to parse.

    4. I'm not entirely sure what you mean by multiple commands? You can prefix the same timeit command (e.g. timeit 2) to as many lines as you want, it will keep counting.
    I was thinking into the direction of, e.g., mi xeq, with syntax like

    Code:
    timeit # : cmd1 ; cmd2 [...]
    5. I'm not sure how to check if a timer is already running - timer off never returns an error and I can't think of another way to check.
    This is indeed a little effort. Here is how I would do it

    Code:
    tempname r_results
    _return hold `r_results'
    quietly timer list `theTimer'
    if (r(t`theTimer') != .) {
        display as text "timer `theTimer' already in use"
    }
    _return restore `r_results'
    6. What's the use of the nobreak? I looked it up in the help files and it seems to stop the "BREAK" command from working. It would nice to make the timer stop on -break-, but that doesn't seem to be happening with this code?
    Actually, this is bug. I want Stata to stop the timer no matter what. If I code

    Code:
    capture noisily ...
    timer off #
    I am not a hundred percent sure that Stata will run the second line of code, if the break key is pressed. That is why I enclose the commands in nobreak. However, I want to still allow the user to break the command. Therefore I should have coded

    Code:
    nobreak {
        timer on `theTimer'
            capture noisily break `0'
        timer off `theTimer'
    }
    Note the break after noisily, which temporarily turns the break key on. Even better would be capturing the return code, so I could add more stuff, including other capture prefixes, before the program ends.

    Code:
    nobreak {
        timer on `theTimer'
            capture noisily break `0'
            local Rc = _rc
        timer off `theTimer'
    }
    
    exit `Rc'
    Of course, this is, strictly speaking, no longer equivalent to

    Code:
    timer on #
    cmd
    timer off #
    because Stata needs to assign the return code to local Rc, which will take a minimum amount of time.

    Best
    Daniel
    Last edited by daniel klein; 15 Aug 2016, 09:10.

    Leave a comment:


  • Jesse Wursten
    replied
    Originally posted by daniel klein View Post
    How about

    Code:
    *! version 1.0.1 15aug2016
    program define timeit
    version 10
    
    gettoken theTimer 0 : 0 , parse(":")
    gettoken theColon 0 : 0 , parse(":")
    if ("`theTimer'" == ":") | ("`theColon'" != ":") {
    error 198
    }
    
    nobreak {
    timer on `theTimer'
    capture noisily `0'
    timer off `theTimer'
    }
    
    exit _rc
    end
    Note that the previous code would choke on

    Code:
    timeit 2 :reg y x
    if there was no blank space after the colon.

    You could add more features, like checking if a specified timer number is already in use or letting the user specify a (scalar) name, where the results are to be stored, or support multiple commands, or ... Whatever you find convenient. Just a few thoughts.

    Best
    Daniel
    First of all, thanks for the feedback!

    1. -capture noisily- seems to be what I was looking for (I was aware of capture, but without the noisily it suppresses output).
    2.
    Code:
        if ("`theTimer'" == ":") | ("`theColon'" != ":") {
            error 198
        }
    In which situation would ("`theTimer'" == ":") be true? The second condition flags when the user didn't type a colon, right?

    3. I think the option to store the timer results is an interesting one. I suppose the easiest way to do this is to allow something along the lines of
    Code:
    timeit 5, name("fifthTimer"): cmd
    Code-wise that complicates things, but it's not insurmountable.

    4. I'm not entirely sure what you mean by multiple commands? You can prefix the same timeit command (e.g. timeit 2) to as many lines as you want, it will keep counting.

    5. I'm not sure how to check if a timer is already running - timer off never returns an error and I can't think of another way to check.

    6. What's the use of the nobreak? I looked it up in the help files and it seems to stop the "BREAK" command from working. It would nice to make the timer stop on -break-, but that doesn't seem to be happening with this code?

    Thanks again!

    Leave a comment:


  • daniel klein
    replied
    How about

    Code:
    *! version 1.0.1 15aug2016
    program define timeit
        version 10
        
        gettoken theTimer 0 : 0 , parse(":")
        gettoken theColon 0 : 0 , parse(":")
        if ("`theTimer'" == ":") | ("`theColon'" != ":") {
            error 198
        }
        
        nobreak {
            timer on `theTimer'
            capture noisily `0'
            timer off `theTimer'
        }
        
        exit _rc
    end
    Note that the previous code would choke on

    Code:
    timeit 2 :reg y x
    if there was no blank space after the colon.

    You could add more features, like checking if a specified timer number is already in use or letting the user specify a (scalar) name, where the results are to be stored, or support multiple commands, or ... Whatever you find convenient. Just a few thoughts.

    Best
    Daniel

    Leave a comment:

Working...
X