m |
m |
||
(28 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{mirc title|Optimization}} | {{mirc title|Optimization}} | ||
− | mIRC is not considered a fast language and, more often than not, the easiest implementation is not the fastest. | + | mIRC Script Language is an interpreted language - which means that mIRC has to work out what each statement means each and every time it is executed. Consequently it is not considered a fast language and, more often than not, the easiest implementation is not the fastest. |
+ | |||
+ | That said, PCs today are far more powerful than when mIRC first introduced its scripting language, and unless your script is processing large number of messages or large files, then performance is less of an issue than it used to be. On the other hand, the maintainability of your script is also important, so eliminating duplicate code using common aliases & identifiers is also beneficial even if it introduces some minor overheads. | ||
The following tips will help to increase the execution speed of a script. Most will have a very marginal speed advantage and may not be worth consideration outside of long-running script blocks such as loops. | The following tips will help to increase the execution speed of a script. Most will have a very marginal speed advantage and may not be worth consideration outside of long-running script blocks such as loops. | ||
+ | == Use built-in functionality == | ||
+ | Because mIRC interprets each statement every time it executes it, reducing the number of statements executed is the easiest way to improve the performance of your scripts. By contrast, once mIRC has worked out what the statement is, then the execution is done by mIRCs compiled code. You can often replace several statements or even entire loops by clever use of mIRCs broad range of built-in functionality. Here are some examples of common ways to reduce the number of statements: | ||
+ | |||
+ | === Use {{mIRC|Token Manipulation}} to break strings into pieces === | ||
+ | If you want to get the first part of a string up to (say) the first full-stop, don't use {{mirc|$pos}} to find the position of this character, and then use {{mirc|$left}} or {{mirc|$right}} to get the relevant sub-string. Instead use mIRCs {{mIRC|Token Manipulation}} functionality: <code>$gettok(%string,1,$asc(.))</code> to get the first sentence and <code>$gettok(%string,2-,$asc(.))</code> to get the remainder of the paragraph. | ||
+ | |||
+ | ''Benchmark:'' | ||
+ | <source lang="mIRC">/benchmark -ai100000 $gettok(Sentence. Sentence.,1,$asc(.));$left(Sentence. Sentence.,$pos(Sentence. Sentence.,.))</source> | ||
+ | |||
+ | === Use {{mirc|wildcard|Wildcard-Matching}} === | ||
+ | {{mirc|wildcard|Wildcard-matching}} allows you to see if your string matches a wildcard template containing fixed text and wildcard-match characters ?, * and &. | ||
+ | |||
+ | * "?" match any character | ||
+ | * "*" match 0 or more characters | ||
+ | * " & " match any word | ||
+ | |||
+ | So if you want to see if a string contains the word "fat" then you can use: | ||
+ | <source lang="mIRC">if (* fat * isin %string) echo -a I am not fat!!</source> | ||
+ | |||
+ | Wildcard-matches can be used in {{mirc|/if}} statements, but also in {{mirc|$wildtok|token manipulation}}, {{mirc|$hfind|hash tables}}, {{mirc|$fline|custom windows}}, {{mIRC|on events|ON events}}, {{mirc|$var|variables}}, etc. etc. Even if you cannot narrow it down to a single item, using wildcard-matches to reduce substantially the number of iterations of a loop is very beneficial. | ||
− | == | + | So for example, if we want to find a particular line in a custom window, rather than: |
+ | <source lang="mIRC"> | ||
+ | var %i $line(@custwin,0) | ||
+ | while (%i) { | ||
+ | var %l = $line(@custwin,%i) | ||
+ | if (The cat sat on the * iswm %l) echo -a I have found what the cat sat on. %l | ||
+ | dec %i | ||
+ | }</source> | ||
+ | the following code loops far fewer times and is therefore much quicker: | ||
+ | <source lang="mIRC"> | ||
+ | var %match The cat sat on the *, %i $fline(@custwin,%match,0) | ||
+ | while (%i) { | ||
+ | var %l = $fline(@custwin,%match,%i) | ||
+ | echo -a I have found what the cat sat on. %l | ||
+ | dec %i | ||
+ | }</source> | ||
− | + | === Use regular expressions === | |
+ | Regular expressions are a very powerful text search and replacement tool even if they have a significant learning curve. | ||
− | + | Again, regular expressions can be used in {{mirc|$hfind|hash tables}}, {{mIRC|on events|ON events}}, etc. etc. | |
− | |||
− | . | ||
− | + | === Use hash tables === | |
− | + | If you have a list of wildmatches you want to search for, store them in a hash table and use {{mirc|$hfind}} so that with one statement mIRC loops through the wildmatches to find one which is a match for your string. | |
− | |||
− | == | + | == Optimise to help mIRC be faster == |
+ | Some mIRC Scripting Language constructs are faster than others... | ||
+ | |||
+ | === Organization === | ||
+ | Aliases at the top of script files have faster access speeds than if placed at the bottom of the script. Furthermore, aliases in scripts at the top of the script-order have faster access speeds than those at the end. | ||
+ | |||
+ | === Alias Bypassing === | ||
+ | When calling any command, mIRC will attempt to find a scripted version prior to looking for a native equivalent. This functionality can be bypassed by prefixing commands with <code>!</code>. $identifiers do not require anything because built-in identifier always have priority, the $~ construct does not speed anything, it only prevents an identifier to look for custom aliases if it's not a built-in. | ||
+ | |||
+ | For example, this bypasses mIRC looking for a scripted <code>echo</code> alias: | ||
+ | <syntaxhighlight lang="mirc">/!echo -a Message</syntaxhighlight> | ||
+ | |||
+ | ''Benchmark:'' | ||
+ | <syntaxhighlight lang="mirc">/benchmark -aci100000 !set -l %a 2;set -l %a 1</syntaxhighlight> | ||
+ | |||
+ | === Conditions === | ||
Best to worst: | Best to worst: | ||
− | <syntaxhighlight lang="mirc">if (condition) command | + | <syntaxhighlight lang="mirc"> |
+ | if (condition) command | ||
if condition { command } | if condition { command } | ||
if (condition) { command }</syntaxhighlight> | if (condition) { command }</syntaxhighlight> | ||
+ | ''Benchmark:'' | ||
+ | <syntaxhighlight lang="mirc">/benchmark -aci100000 if (0 == 1) var %a 1;if 0 == 1 { var %a 1 };if (0 == 1) { var %a 1 }</syntaxhighlight> | ||
− | == if | + | === {{mirc|/if}} vs. {{mirc|$iif}}() === |
− | $iif() is much slower than using an if-else statement. When $iif() is | + | <code>$iif()</code> is much slower than using an if-else statement. When <code>$iif()</code> is encounter it is first rearranged into an if-else statement and the result is evaluated. |
Best to worst: | Best to worst: | ||
+ | <syntaxhighlight lang="mirc">if (condition) var %result = condition_true_value | ||
+ | else var %result = condition_false_value</syntaxhighlight> | ||
<syntaxhighlight lang="mirc">var %result = condition_false_value | <syntaxhighlight lang="mirc">var %result = condition_false_value | ||
− | if (condition) var %result = condition_true_value | + | if (condition) var %result = condition_true_value</syntaxhighlight> |
+ | <syntaxhighlight lang="mirc">var %result = $iif(condition, condition_true_value, condition_false_value)</syntaxhighlight> | ||
− | if (condition) var %result = condition_true_value | + | ''Benchmark:'' |
− | else var %result = condition_false_value | + | <syntaxhighlight lang="mirc">/benchmark -aci100000 if (condition) var %result = condition_true_value | else var %result = condition_false_value;var %result = condition_false_value | if (condition) var %result = condition_true_value;var %result = $iif(condition, condition_true_value, condition_false_value)</syntaxhighlight> |
− | + | === {{mirc|/tokenize}} & $n vs. {{mirc|$gettok}}() === | |
+ | For successive<sup>2</sup> calls against the same data, it is faster to use <code>/tokenize</code> and <code>$n</code> over <code>$gettok()</code>. | ||
+ | <syntaxhighlight lang="mirc">;; faster than using $gettok(a b c, 1, 32) $gettok(a b c, 2, 32) | ||
+ | tokenize 32 a b c | ||
+ | echo -a $1 $2</syntaxhighlight> | ||
− | + | <sup>2</sup>: even with just two references against the same input tokenizing is faster than using $gettok | |
− | |||
− | + | === [ ] vs. $() vs. $eval === | |
− | == [] | ||
Best to worst: | Best to worst: | ||
− | <syntaxhighlight lang="mirc">[ eval_statement ] | + | <syntaxhighlight lang="mirc"> |
+ | [ eval_statement ] | ||
$(eval_statement, 2) | $(eval_statement, 2) | ||
$eval(eval_statement, 2)</syntaxhighlight> | $eval(eval_statement, 2)</syntaxhighlight> | ||
+ | |||
+ | ''Benchmark:'' | ||
+ | <syntaxhighlight lang="mirc"> | ||
+ | //set %test.eval = VALUE | ||
+ | //echo -a [] %test. [ $+ eval ] | ||
+ | //echo -a $ $($+(%,test.,eval),2) | ||
+ | //echo -a $ $+ eval $eval($+(%,test.,eval),2) | ||
+ | /benchmark -ai100000 %test. [ $+ eval];$($+(%,test.,eval),2);$eval($+(%,test.,eval),2)</syntaxhighlight> | ||
+ | |||
+ | === %x= vs. /var vs. /set === | ||
+ | |||
+ | There are multiple ways to set a global or local variable. Best to worse these are: | ||
+ | |||
+ | ==== Global variables ==== | ||
+ | <syntaxhighlight lang="mirc"> | ||
+ | %x = VALUE | ||
+ | set %x VALUE | ||
+ | var -g %x VALUE | ||
+ | var -g %x = VALUE | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ''Benchmark:'' | ||
+ | <syntaxhighlight lang="mirc">/benchmark -aci100000 %x = 1;/set %x 1;/set %x = 1;/var -g %x 1;/var -g %x = 1</syntaxhighlight> | ||
+ | |||
+ | ==== Local variables ==== | ||
+ | <syntaxhighlight lang="mirc"> | ||
+ | set -l %x VALUE | ||
+ | var %x VALUE | ||
+ | var %x = VALUE | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ''Benchmark:'' | ||
+ | <syntaxhighlight lang="mirc">/benchmark -aci100000 /set -l %x 1;/set -l %x = 1;/var %x 1;/var %x = 1</syntaxhighlight> | ||
+ | |||
+ | == Long running loops == | ||
+ | mIRC is '''not''' multi-threaded - any script runs to completion before mIRC does any other activities i.e. before running any other scripts or processing any messages from IRC servers or processing any actions made by the user. Depending on your version of mIRC, Windows may also start to think that mIRC has become non-responsive and offer to terminate it. | ||
+ | |||
+ | If you want to run a long running looping script (say to load a large file into hash tables) '''without mIRC locking up''', there is a technique you can use. Instead of a single loop like this (which loops a million times before ending): | ||
+ | <syntaxhighlight lang="mirc"> | ||
+ | alias loop { | ||
+ | set %ticks $ticks | ||
+ | var %i 1000000 | ||
+ | while (%i) { | ||
+ | ; do stuff | ||
+ | dec %i | ||
+ | } | ||
+ | echo -a Loop took $calc($ticks - %ticks) $+ ms to execute. | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | you might instead want to break the loop into (say) a thousand chunks of a thousand iterations and run the next chunk using a timer: | ||
+ | <syntaxhighlight lang="mirc"> | ||
+ | alias loop { | ||
+ | set %ticks $ticks | ||
+ | chunk 1000 | ||
+ | } | ||
+ | alias chunk { | ||
+ | var %i 1000 | ||
+ | while (%i) { | ||
+ | ; do stuff | ||
+ | dec %i | ||
+ | } | ||
+ | if ($1 > 1) .timer 1 0 chunk $calc($1 - 1) | ||
+ | else echo -a Loop took $calc($ticks - %ticks) $+ ms to execute. | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | As you can see, in this example each chunk does 1,000 loops, and then sets a timer to run the next chunk. The number of iterations per chunk will depend on the CPU load of the stuff in the loop - you may need to experiment to determine the optimum number of iterations per chunk (bearing in mind that it may run on PCs of different CPU powers). Clearly you want to keep the iterations small enough that mIRC continues to feel responsive, but you don't want to slow it up by having too many small chunks being queued on the timer. | ||
+ | |||
+ | On a Core i7 laptop, the first example takes c. 10.8s during which mIRC is unresponsive, and the second takes c. 19.8s but remains very responsive to e.g. keystrokes - however there is an overhead of c. 9s for 1,000 chunks. If one million loops takes 11s, then a chunk of a thousand loops will take c. 11ms which is a lot smaller than needed to keep mIRC responsive. If we assume that to keep mIRC feeling responsive we need to process other activities (say) every 100ms, this suggests that we should be able to run 100 chunks of 10,000 iterations rather than 1,000 of 1,000, and by doing this reduce the overhead of chunking by a factor of 10, which should give us a run time of c. 11.7s - and when I tried it mIRC still felt pretty responsive e.g. to keystrokes, and it did indeed take c. 11.7s to run. | ||
+ | |||
+ | '''Summary:''' In this simple example, by chunking in this way I was able to keep mIRC responsive during a loop which would normally lock-up mIRC for over 10s with an overhead of <10% - a definite win. | ||
+ | |||
+ | == Benchmark utility == | ||
+ | |||
+ | The benchmark examples above are designed to work with the following script. Type "/benchmark" to get help. | ||
+ | |||
+ | <syntaxhighlight lang="mirc"> | ||
+ | alias benchmark { | ||
+ | if ($show) { | ||
+ | if ($0 == 0) { | ||
+ | echo -cag action $str(-,80) | ||
+ | echo 4 -ag /benchmark -aciN string1[;string2[;string3 …]] | ||
+ | echo -ag $crlf | ||
+ | echo -cag info The default number of iterations is 10000. | ||
+ | echo -cag info -i switch defines a different number of iterations. | ||
+ | echo -cag info -c tests as a command, not a string | ||
+ | echo -cag info -a applies the same switches to all strings | ||
+ | echo -ag $crlf | ||
+ | echo -cag action Examples: | ||
+ | echo -cag info /benchmark $ $+ replace(a,a,b) | ||
+ | echo -cag info /benchmark -i5000 $ $+ replace(a,a,b) | ||
+ | echo -cag info /benchmark -i5000 $ $+ replace(a,a,b);-i5000 $ $+ regsubex(a,a,b) | ||
+ | echo -cag info /benchmark -ai5000 $ $+ replace(a,a,b);$ $+ regsubex(a,a,b) | ||
+ | echo -ag $crlf | ||
+ | echo -cag info /benchmark -c var % $+ a 1;-c set -l % $+ a 1 | ||
+ | echo -cag info /benchmark -ci20000 var % $+ a 1;-ci20000 set -l % $+ a 1 | ||
+ | echo -cag info /benchmark -aci20000 var % $+ a 1;set -l % $+ a 1 | ||
+ | echo -cag action $str(-,80) | ||
+ | return | ||
+ | } | ||
+ | echo -cag action Benchmark $1- | ||
+ | if ($switch($1,a)) { | ||
+ | var %o $iif($remove($1,a) != -,$v1) | ||
+ | tokenize 32 $2- | ||
+ | } | ||
+ | tokenize 59 $1- | ||
+ | var %a 1 | ||
+ | while ($(,%o $ $+ %a) != %o) { | ||
+ | scon 0 .benchmark $!v1 | ||
+ | inc %a | ||
+ | } | ||
+ | return | ||
+ | } | ||
+ | var %benchmark $iif($switch($1,i) isnum,$v1,10000) | ||
+ | var %b %benchmark,%s $1 | ||
+ | if ($switch(%s)) tokenize 32 $2- | ||
+ | tokenize 32 $iif(!$switch(%s,c),noop) $1- | ||
+ | var %t $ticks | ||
+ | while (%benchmark) { | ||
+ | scon 0 $1- | ||
+ | dec %benchmark | ||
+ | } | ||
+ | var %e $ticks - %t | ||
+ | var %c = $switch(%s,c) | ||
+ | echo 3 -ag %b iterations of the $iif(%c,command,string) $qt($iif(%c,$1-,$2-)) took %e $+ ms | ||
+ | return | ||
+ | :error | ||
+ | echo -cag info $error | ||
+ | echo 4 -ag Could not complete benchmark test for $qt($iif($1 != noop,$1) $2-) | ||
+ | reseterror | ||
+ | } | ||
+ | |||
+ | alias -l switch { | ||
+ | if (-?* !iswm $1) return $false | ||
+ | if (!$regex($1,/([a-zA-Z](?:".+?"|[^a-zA-Z]+)?)/g)) return $false | ||
+ | if ($2 isnum && $2 > $regml(0)) return $false | ||
+ | if ($2 = $null) return $true | ||
+ | if ($2 isnum) { | ||
+ | var %m $regml($2),%s $left(%m,1),%p $noqt($mid(%m,2)) | ||
+ | if ($2 = 0) return $regml(0) | ||
+ | if (%m != $null) { | ||
+ | if ($regex($prop,/^exists?$/i)) return $true | ||
+ | if ($regex($prop,/^contents?$/i)) return $iif(%p,%p,$false) | ||
+ | return %s | ||
+ | } | ||
+ | return $false | ||
+ | } | ||
+ | var %x 1 | ||
+ | while ($regml(%x)) { | ||
+ | var %m $v1,%s $left($v1,1),%p $noqt($mid($v1,2)) | ||
+ | if ($2 === %s) { | ||
+ | if ($prop != sname) return $iif(%p,%p,$true) | ||
+ | elseif ($prop = sname) return %s | ||
+ | } | ||
+ | inc %x | ||
+ | } | ||
+ | return $false | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | [[Category:mIRC|optimization]] |
Latest revision as of 06:44, 23 February 2023
mIRC Script Language is an interpreted language - which means that mIRC has to work out what each statement means each and every time it is executed. Consequently it is not considered a fast language and, more often than not, the easiest implementation is not the fastest.
That said, PCs today are far more powerful than when mIRC first introduced its scripting language, and unless your script is processing large number of messages or large files, then performance is less of an issue than it used to be. On the other hand, the maintainability of your script is also important, so eliminating duplicate code using common aliases & identifiers is also beneficial even if it introduces some minor overheads.
The following tips will help to increase the execution speed of a script. Most will have a very marginal speed advantage and may not be worth consideration outside of long-running script blocks such as loops.
Contents
Use built-in functionality[edit]
Because mIRC interprets each statement every time it executes it, reducing the number of statements executed is the easiest way to improve the performance of your scripts. By contrast, once mIRC has worked out what the statement is, then the execution is done by mIRCs compiled code. You can often replace several statements or even entire loops by clever use of mIRCs broad range of built-in functionality. Here are some examples of common ways to reduce the number of statements:
Use Token Manipulation to break strings into pieces[edit]
If you want to get the first part of a string up to (say) the first full-stop, don't use $pos to find the position of this character, and then use $left or $right to get the relevant sub-string. Instead use mIRCs Token Manipulation functionality: $gettok(%string,1,$asc(.))
to get the first sentence and $gettok(%string,2-,$asc(.))
to get the remainder of the paragraph.
Benchmark:
/benchmark -ai100000 $gettok(Sentence. Sentence.,1,$asc(.));$left(Sentence. Sentence.,$pos(Sentence. Sentence.,.))
Use Wildcard-Matching[edit]
Wildcard-matching allows you to see if your string matches a wildcard template containing fixed text and wildcard-match characters ?, * and &.
- "?" match any character
- "*" match 0 or more characters
- " & " match any word
So if you want to see if a string contains the word "fat" then you can use:
if (* fat * isin %string) echo -a I am not fat!!
Wildcard-matches can be used in /if statements, but also in token manipulation, hash tables, custom windows, ON events, variables, etc. etc. Even if you cannot narrow it down to a single item, using wildcard-matches to reduce substantially the number of iterations of a loop is very beneficial.
So for example, if we want to find a particular line in a custom window, rather than:
var %i $line(@custwin,0) while (%i) { var %l = $line(@custwin,%i) if (The cat sat on the * iswm %l) echo -a I have found what the cat sat on. %l dec %i }
the following code loops far fewer times and is therefore much quicker:
var %match The cat sat on the *, %i $fline(@custwin,%match,0) while (%i) { var %l = $fline(@custwin,%match,%i) echo -a I have found what the cat sat on. %l dec %i }
Use regular expressions[edit]
Regular expressions are a very powerful text search and replacement tool even if they have a significant learning curve.
Again, regular expressions can be used in hash tables, ON events, etc. etc.
Use hash tables[edit]
If you have a list of wildmatches you want to search for, store them in a hash table and use $hfind so that with one statement mIRC loops through the wildmatches to find one which is a match for your string.
Optimise to help mIRC be faster[edit]
Some mIRC Scripting Language constructs are faster than others...
Organization[edit]
Aliases at the top of script files have faster access speeds than if placed at the bottom of the script. Furthermore, aliases in scripts at the top of the script-order have faster access speeds than those at the end.
Alias Bypassing[edit]
When calling any command, mIRC will attempt to find a scripted version prior to looking for a native equivalent. This functionality can be bypassed by prefixing commands with !
. $identifiers do not require anything because built-in identifier always have priority, the $~ construct does not speed anything, it only prevents an identifier to look for custom aliases if it's not a built-in.
For example, this bypasses mIRC looking for a scripted echo
alias:
/!echo -a Message
Benchmark:
/benchmark -aci100000 !set -l %a 2;set -l %a 1
Conditions[edit]
Best to worst:
if (condition) command if condition { command } if (condition) { command }
Benchmark:
/benchmark -aci100000 if (0 == 1) var %a 1;if 0 == 1 { var %a 1 };if (0 == 1) { var %a 1 }
/if vs. $iif()[edit]
$iif()
is much slower than using an if-else statement. When $iif()
is encounter it is first rearranged into an if-else statement and the result is evaluated.
Best to worst:
if (condition) var %result = condition_true_value else var %result = condition_false_value
var %result = condition_false_value if (condition) var %result = condition_true_value
var %result = $iif(condition, condition_true_value, condition_false_value)
Benchmark:
/benchmark -aci100000 if (condition) var %result = condition_true_value | else var %result = condition_false_value;var %result = condition_false_value | if (condition) var %result = condition_true_value;var %result = $iif(condition, condition_true_value, condition_false_value)
/tokenize & $n vs. $gettok()[edit]
For successive2 calls against the same data, it is faster to use /tokenize
and $n
over $gettok()
.
;; faster than using $gettok(a b c, 1, 32) $gettok(a b c, 2, 32) tokenize 32 a b c echo -a $1 $2
2: even with just two references against the same input tokenizing is faster than using $gettok
[ ] vs. $() vs. $eval[edit]
Best to worst:
[ eval_statement ] $(eval_statement, 2) $eval(eval_statement, 2)
Benchmark:
//set %test.eval = VALUE //echo -a [] %test. [ $+ eval ] //echo -a $ $($+(%,test.,eval),2) //echo -a $ $+ eval $eval($+(%,test.,eval),2) /benchmark -ai100000 %test. [ $+ eval];$($+(%,test.,eval),2);$eval($+(%,test.,eval),2)
%x= vs. /var vs. /set[edit]
There are multiple ways to set a global or local variable. Best to worse these are:
Global variables[edit]
%x = VALUE set %x VALUE var -g %x VALUE var -g %x = VALUE
Benchmark:
/benchmark -aci100000 %x = 1;/set %x 1;/set %x = 1;/var -g %x 1;/var -g %x = 1
Local variables[edit]
set -l %x VALUE var %x VALUE var %x = VALUE
Benchmark:
/benchmark -aci100000 /set -l %x 1;/set -l %x = 1;/var %x 1;/var %x = 1
Long running loops[edit]
mIRC is not multi-threaded - any script runs to completion before mIRC does any other activities i.e. before running any other scripts or processing any messages from IRC servers or processing any actions made by the user. Depending on your version of mIRC, Windows may also start to think that mIRC has become non-responsive and offer to terminate it.
If you want to run a long running looping script (say to load a large file into hash tables) without mIRC locking up, there is a technique you can use. Instead of a single loop like this (which loops a million times before ending):
alias loop { set %ticks $ticks var %i 1000000 while (%i) { ; do stuff dec %i } echo -a Loop took $calc($ticks - %ticks) $+ ms to execute. }
you might instead want to break the loop into (say) a thousand chunks of a thousand iterations and run the next chunk using a timer:
alias loop { set %ticks $ticks chunk 1000 } alias chunk { var %i 1000 while (%i) { ; do stuff dec %i } if ($1 > 1) .timer 1 0 chunk $calc($1 - 1) else echo -a Loop took $calc($ticks - %ticks) $+ ms to execute. }
As you can see, in this example each chunk does 1,000 loops, and then sets a timer to run the next chunk. The number of iterations per chunk will depend on the CPU load of the stuff in the loop - you may need to experiment to determine the optimum number of iterations per chunk (bearing in mind that it may run on PCs of different CPU powers). Clearly you want to keep the iterations small enough that mIRC continues to feel responsive, but you don't want to slow it up by having too many small chunks being queued on the timer.
On a Core i7 laptop, the first example takes c. 10.8s during which mIRC is unresponsive, and the second takes c. 19.8s but remains very responsive to e.g. keystrokes - however there is an overhead of c. 9s for 1,000 chunks. If one million loops takes 11s, then a chunk of a thousand loops will take c. 11ms which is a lot smaller than needed to keep mIRC responsive. If we assume that to keep mIRC feeling responsive we need to process other activities (say) every 100ms, this suggests that we should be able to run 100 chunks of 10,000 iterations rather than 1,000 of 1,000, and by doing this reduce the overhead of chunking by a factor of 10, which should give us a run time of c. 11.7s - and when I tried it mIRC still felt pretty responsive e.g. to keystrokes, and it did indeed take c. 11.7s to run.
Summary: In this simple example, by chunking in this way I was able to keep mIRC responsive during a loop which would normally lock-up mIRC for over 10s with an overhead of <10% - a definite win.
Benchmark utility[edit]
The benchmark examples above are designed to work with the following script. Type "/benchmark" to get help.
alias benchmark { if ($show) { if ($0 == 0) { echo -cag action $str(-,80) echo 4 -ag /benchmark -aciN string1[;string2[;string3 …]] echo -ag $crlf echo -cag info The default number of iterations is 10000. echo -cag info -i switch defines a different number of iterations. echo -cag info -c tests as a command, not a string echo -cag info -a applies the same switches to all strings echo -ag $crlf echo -cag action Examples: echo -cag info /benchmark $ $+ replace(a,a,b) echo -cag info /benchmark -i5000 $ $+ replace(a,a,b) echo -cag info /benchmark -i5000 $ $+ replace(a,a,b);-i5000 $ $+ regsubex(a,a,b) echo -cag info /benchmark -ai5000 $ $+ replace(a,a,b);$ $+ regsubex(a,a,b) echo -ag $crlf echo -cag info /benchmark -c var % $+ a 1;-c set -l % $+ a 1 echo -cag info /benchmark -ci20000 var % $+ a 1;-ci20000 set -l % $+ a 1 echo -cag info /benchmark -aci20000 var % $+ a 1;set -l % $+ a 1 echo -cag action $str(-,80) return } echo -cag action Benchmark $1- if ($switch($1,a)) { var %o $iif($remove($1,a) != -,$v1) tokenize 32 $2- } tokenize 59 $1- var %a 1 while ($(,%o $ $+ %a) != %o) { scon 0 .benchmark $!v1 inc %a } return } var %benchmark $iif($switch($1,i) isnum,$v1,10000) var %b %benchmark,%s $1 if ($switch(%s)) tokenize 32 $2- tokenize 32 $iif(!$switch(%s,c),noop) $1- var %t $ticks while (%benchmark) { scon 0 $1- dec %benchmark } var %e $ticks - %t var %c = $switch(%s,c) echo 3 -ag %b iterations of the $iif(%c,command,string) $qt($iif(%c,$1-,$2-)) took %e $+ ms return :error echo -cag info $error echo 4 -ag Could not complete benchmark test for $qt($iif($1 != noop,$1) $2-) reseterror } alias -l switch { if (-?* !iswm $1) return $false if (!$regex($1,/([a-zA-Z](?:".+?"|[^a-zA-Z]+)?)/g)) return $false if ($2 isnum && $2 > $regml(0)) return $false if ($2 = $null) return $true if ($2 isnum) { var %m $regml($2),%s $left(%m,1),%p $noqt($mid(%m,2)) if ($2 = 0) return $regml(0) if (%m != $null) { if ($regex($prop,/^exists?$/i)) return $true if ($regex($prop,/^contents?$/i)) return $iif(%p,%p,$false) return %s } return $false } var %x 1 while ($regml(%x)) { var %m $v1,%s $left($v1,1),%p $noqt($mid($v1,2)) if ($2 === %s) { if ($prop != sname) return $iif(%p,%p,$true) elseif ($prop = sname) return %s } inc %x } return $false }