Перевод на русский язык: v0id.
Взято с: http://kreon.net.ru/index.php?name=Pages&op=page&pid=4
1. Быстрая замена элементов в списках.
proc K {x y} {set x}
set theList [lreplace [K $theList [set theList {}]] 7 42]
set theList [lreplace [K $theList [set theList {}]] 7 42]
Что здесь происходит? При использовании [lreplace] обычным способом, создаётся дубликат всего списка, производятся какие-либо манипуляции, затем дубликат копируется назад в переменную и, наконец, дубликат уничтожается.
Если вы используете вышеприведённый метод с так называемым К-комбинатором (важное понятие функционального программирования), то манипуляции со списком проводятся минуя создание промежуточного дубликата списка.
На моей машине такой способ быстрее почти в 50 раз! Естественно, при манипуляциях большими списками:
% proc K {a b} {set a}
% for {set i 0} {$i<100000} {incr i} {
lappend a $i; lappend b $i
}
% time {set a [lreplace $a 40 80]}
24879 microseconds per iteration
% time {set b [lreplace [K $b [set b {}]] 40 80]}
537 microseconds per iteration
% for {set i 0} {$i<100000} {incr i} {
lappend a $i; lappend b $i
}
% time {set a [lreplace $a 40 80]}
24879 microseconds per iteration
% time {set b [lreplace [K $b [set b {}]] 40 80]}
537 microseconds per iteration
V0id писал(а):На моей машине прирост производительности не превысил 2,5 раз при таком же размере списка. Конечно, не стоит бросаться переписывать ваши скрипты, т.к. для ощутимого прироста производительности они должны работать с очень большими списками. Но, с другой стороны, если ваш скрипт манипулирует списками в десятки тысяч элементов, то будет преступлением - пренебречь этим способом оптимизации!
2. Преобразования типов.
Типы данных в Tcl 8.0 и выше представляются внутренним объектом Tcl_Obj. По возможности, не изменяйте тип этого представления, например используйте:
но не:
(во втором выражении внутреннее представление аргумента-списка $alist меняется на строку, что ведёт к неоправданным затратам производительности)
Это правило не должно относиться к триггерам биндов дропа (которые обычно не являются узким местом производительности), но всё же:
быстрее и компактней, чем:
set foo [lindex [split $args] 0]
set bar [lindex [split $args] 1]
set moo [lindex [split $args] 2]
set bar [lindex [split $args] 1]
set moo [lindex [split $args] 2]
3. Фигурные скобки.
Все выражения в Tcl должны быть заключены в фигурные скобки. У меня была плохая привычка писать:
Не делайте этого, используйте:
V0id писал(а):В отличие от оптимизации замены в списках, этот вид оптимизации важен практически для всех, так как вычисления и условные операторы в циклах использует в своей практике любой скриптер. Возьмите себе за правило заключать все выражения в {}. Вот простой пример, демонстрирующий разницу производительности:
% proc a {n} {
for {set i 0} {$i < $n} {incr i} {
if [llength $i] { continue }
}
}
% proc b {n} {
for {set i 0} {$i < $n} {incr i} {
if {[llength $i]} { continue }
}
}
% time {a 10000}
95375 microseconds per iteration
% time {b 10000}
59353 microseconds per iteration
for {set i 0} {$i < $n} {incr i} {
if [llength $i] { continue }
}
}
% proc b {n} {
for {set i 0} {$i < $n} {incr i} {
if {[llength $i]} { continue }
}
}
% time {a 10000}
95375 microseconds per iteration
% time {b 10000}
59353 microseconds per iteration
Результаты говорят сами за себя...
4. Процедуры.
По возможности, оформляйте весь код в процедуры, т.к. инлайн Tcl код не так хорошо оптимизирован как скомпилированные в байт-код процедуры:
% time {for {set i 0} {$i<10000} {incr i} {lappend a $i}} 100
35300 microseconds per iteration
% proc init_me {} {
global b
for {set i 0} {$i<10000} {incr i} {lappend b $i}
}
% time {init_me} 100
21888 microseconds per iteration
35300 microseconds per iteration
% proc init_me {} {
global b
for {set i 0} {$i<10000} {incr i} {lappend b $i}
}
% time {init_me} 100
21888 microseconds per iteration
5. Инлайн-регэкспы.
Не злоупотребляйте инлайновыми регулярными выражениями:
Вместо этого сохраните выражение в переменной, а затем используйте её:
При такой записи в переменной $ws хранится скомпилированное выражение представленное объектом Tcl_Obj и просто выполняется по требованию, тогда как использование первой формы записи приведёт к компиляции выражения при каждом вызове (если кэш интерпретатора будет исчерпан)
6. Catch
Не используйте [catch] слишком часто, она медленна и должна использоваться только если вы уверены, что она будет вызываться достаточно редко. Например, эта "ленивая" строка кода в 10 раз медленнее, чем конструкция [info exists], которая выполняет ту же задачу:
7. Вызовы процедур.
Если вы используете результат процедуры/команды несколько раз, то лучше воспользоваться промежуточной переменной, а не выполнять команду каждый раз:
медленно:
proc te10 { } {
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
}
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
putlog "te10: [hand2nick $::owner]"
}
быстрее:
proc te10 { } {
set ownerhand [hand2nick $::owner]
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
}
set ownerhand [hand2nick $::owner]
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
putlog "te10: $ownerhand"
}
Во втором случае, на мой взгляд, скрипт выглядит проще и чище.
8. Регэкспы.
V0id писал(а):По себе знаю: после овладения синтаксисом регулярных выражений, скриптер зачастую начинает ими злоупотреблять. Этого нужно избегать. Для простых случаев, когда нужен не парсинг, а только небольшая синтаксическая проверка, следует пользоваться альтернативными и более быстрыми методами: [scan], [string is ...], [string match].