При первой встрече в тексте программы
При компиляции сначала происходит обработка литералов
x учитывается при обработке литерала $|, $), … не являются переменными и не интерполируются./U, /l, … а также конструкции /Q…/E, но в тех частях Потом, если не было замечено ошибок на шагах 1-3,
Но o. Переменные, содержащиеся во встроенном коде, и динамические
Если
Отказ от перекомпиляции qr/…/. Они содержат уже откомпилированное и готовое к использованию
Если после интерполяции переменных
my $a='abc'; my $b='cde'; my $re='(\w)'; $a =~ /$re/; print "$1\n"; $b =~ //; print $1;
На печать выводится
a c
Пояснение: в операторе $a =~ /$re/; мы использовали a. Далее мы можем множество раз задавать пустое //. Вместо него будет использоваться это последнее совпавшее кэшированное c. Если теперь изменить содержимое переменной $re, то это не повлияет на результат применения пустого $re (т.е. внутреннее представление литерала (\w) ) уже находится в кэшированном
Эта оптимизация не является естественной и с приходом объектов
Если оператор поиска или замены не содержит ничего кроме объекта
my $re=/…/; … if ($_ =~ $re) … или if (m/$re/) …
то в этом операторе напрямую применяется откомпилированный объект
Здесь уместно вспомнить об опасности применения o с объектами
my $re=/…/o;
который дает неожиданный и неприятный эффект, описанный ранее.
Если применить директиву
use re qw(debug);
то
use re qw(debug); 'abcd' =~ /abc(d)/;
На печать будет выдано:
Compiling REx `abc(d)'
size 9 Got 76 bytes for offset annotations.
first at 1
1: EXACT <abc>(3)
3: OPEN1(5)
5: EXACT <d>(7)
7: CLOSE1(9)
9: END(0)
anchored `abcd' at 0 (checking anchored) minlen 4
Offsets: [9]
1[3] 0[0] 4[1] 0[0] 5[1] 0[0] 6[1] 0[0] 7[0]
Guessing start of match, REx `abc(d)' against `abcd'...
Found anchored substr `abcd' at offset 0...
Guessed: match at offset 0
Matching REx `abc(d)' against `abcd'
Setting an EVAL scope, savestack=3
0 <> <abcd> | 1: EXACT <abc>
3 <abc> <d> | 3: OPEN1
3 <abc> <d> | 5: EXACT <d>
4 <abcd> <v | 7: CLOSE1
4 <abcd> <> | 9: END
Match successful!
Freeing REx: `"abc(d)"'
Текст
1: EXACT <abc>(3) 3: OPEN1(5) 5: EXACT <d>(7) 7: CLOSE1(9) 9: END(0)
является расшифровкой внутреннего представления
1: EXACT <abc>(3)
означает, что надо искать строковый литерал abc длиной 3 символа. Строка
anchored `abcd' at 0 (checking anchored) minlen 4
означает, что текст abcd длины 4 привязан к началу
3: OPEN1(5)
означает, что открылась первая
Функция
study( переменная с целевым текстом )
является еще одной возможностью оптимизации использования
Эта функция создает в переменной с целевым текстом скрытую информацию о тексте, который содержит эта переменная. Это список позиций, в которых каждый символ встречается в данном тексте. В результате на построение этого списка позиций расходуется время и память, которая обычно превосходит в четыре раза объем памяти для текста этой переменной. Поэтому выигрыша во времени можно не получить вовсе, если применять функцию не там, где нужно. Функция может существенно ускорить поиск, если в i сводит на нет усилия функции . Также функция может оказаться полезной, если к одной и той же переменной применяется много операторов с
Для применения оптимизаций, которые возможны с функцией , надо стараться выделять литеральный текст. Например, вместо
a+
надо написать эквивалентный
aa*
а вместо
a{3,6}
нужно записать
aaa{0,3}
Если конструкция выбора (
that|this
записать
th(?:at|is)
В поставке . Он позволяет измерять время выполнения участков кода. При этом можно учитывать только время, которое потрачено процессором на выполнение кода вашей программы, а не на всю систему. Механизм применения этого модуля таков:
use Benchmark; … my $t1=new Benchmark; # Здесь находится участок кода, время работы которого измеряется … my $t2=new Benchmark; print timestr(timediff $t2,$t1);
В переменной $t1 запоминается время начала исполнения участка кода, в переменной $t2 запоминается время окончания выполнения этого участка кода. Затем с помощью функций timediff и timestr выводится разница между временем окончания и временем начала работы участка кода, который тестируется. Но не забывайте, что при первом обращении к
В качестве примера применения хронометража времени поставим задачу скорейшего определения факта наличия между тегами непробельного символа. При этом предполагается, что все теги закрыты и нет вложенных тегов. Для этого разработаем такой алгоритм
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/\A # начало текста
(?> # атомарная группировка
(?: # цикл пропуска пробелов и тегов
(?>\s*) # пропускаем пробельные символы
<(?>[^>]*)> # пропускаем тег с его содержимым
)* # повтор любое число раз
)
\S # и вот он наконец - непробельный символ
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
В этом примере избран такой подход: в цикле (?: … )* пропускаются пробельные символы и теги, а после окончания цикла таких пропусков должен встретиться непробельный символ \S. Если он встретится, то поиск завершится удачей. В переменной $_ создается длинная строка с тегами и пробелами, которая завершается непробельным символом a вне тегов. Чтобы время компиляции $re. Поскольку
При прогоне этой программы на моем компьютере выводится следующее значение времени выполнения заданного участка кода:
1 wallclock secs ( 1.11 usr + 0.00 sys = 1.11 CPU)
Итак, это
Теперь давайте уберем
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/\A # начало текста
(?: # цикл пропуска пробелов и тегов
(?>\s*) # пропускаем пробельные символы
<(?>[^>]*)> # пропускаем тег с его содержимым
)* # повтор любое число раз
\S # и вот он наконец - непробельный символ
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
Распечатка времени показывает, что программа стала работать быстрее и теперь тратит всего 1.08 секунды. Это можно понять так: зачем трудиться по уничтожению сохраненных состояний, когда \s*, то \s* может пригодиться. Кроме того, у нас нет возвратов при переборе. Если убрать последнюю
/\A # начало текста
(?: # цикл пропуска пробелов и тегов
\s* # пропускаем пробельные символы
<[^>]*> # пропускаем тег с его содержимым
)* # повтор любое число раз
\S # и вот он наконец - непробельный символ
/x;
то время выполнения участка кода практически не изменится и станет равным 1.03 секунды.
Предыдущее
(?=[^<>]*>)
А если этот символ стоит вне тегов, то такого текста найдено не будет, поэтому будет истинен
(?![^<>]*>)
Вот вся эта программа:
use Benchmark;
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/[^\s<>]
(?![^<>]*>)
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
На печати получаем:
1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU)
Мы видим, что второй вариант работает немного медленнее первого. Очевидно, это связано с тем, что ему приходится проверять каждый символ внутри тегов с помощью негативной опережающей проверки. Отсюда мы видим, что для наших данных (много тегов и мало пробелов) лучше использовать первый вариант
Теперь узнаем, в какую цену обходится встроенный код
use Benchmark;
$_=' <pppp>' x 13000;
$_.='<table>a';
my $count=0;
my $re=qr
/[^\s<>]
(?![^<>]*>)
(?{ ++$count })
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";
На печати появится:
3 wallclock secs ( 3.44 usr + 0.00 sys = 3.44 CPU) 1000000
Время выполнения увеличилось примерно в 3 раза. Сравним это со временем выполнения самого кода автоприращения:
use Benchmark;
my $count=0;
my $t1=new Benchmark;
for (1..1000000) { ++$count }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";
Напечатается
0 wallclock secs ( 0.13 usr + 0.00 sys = 0.16 CPU) 1000000
Мы видим, что сам по себе этот код берет времени намного меньше, чем когда он выполняется внутри
Далее рассмотрим ресурсоемкость динамического
use Benchmark;
$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr/(\d+)\D+(??{"0{$1}"})/;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
Напечатается
4 wallclock secs ( 4.74 usr + 0.00 sys = 4.74 CPU)
А сейчас заменим динамическое \d+:
use Benchmark;
$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr
/(\d+)\D+\d+/;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
В этот раз время уменьшится:
1 wallclock secs ( 1.38 usr + 0.00 sys = 1.38 CPU)
Замечаем, что встроенный код и динамические
Аналогично заметно возрастает время выполнения оператора подстановки s/// с e. Были практические случаи, когда оператор подстановки с e замедлял работу программы в десятки раз.
При первой встрече в тексте программы
При компиляции сначала происходит обработка литералов
x учитывается при обработке литерала $|, $), … не являются переменными и не интерполируются./U, /l, … а также конструкции /Q…/E, но в тех частях Потом, если не было замечено ошибок на шагах 1-3,
Но o. Переменные, содержащиеся во встроенном коде, и динамические
Если
Отказ от перекомпиляции qr/…/. Они содержат уже откомпилированное и готовое к использованию
Если после интерполяции переменных
my $a='abc'; my $b='cde'; my $re='(\w)'; $a =~ /$re/; print "$1\n"; $b =~ //; print $1;
На печать выводится
a c
Пояснение: в операторе $a =~ /$re/; мы использовали a. Далее мы можем множество раз задавать пустое //. Вместо него будет использоваться это последнее совпавшее кэшированное c. Если теперь изменить содержимое переменной $re, то это не повлияет на результат применения пустого $re (т.е. внутреннее представление литерала (\w) ) уже находится в кэшированном
Эта оптимизация не является естественной и с приходом объектов
Если оператор поиска или замены не содержит ничего кроме объекта
my $re=/…/; … if ($_ =~ $re) … или if (m/$re/) …
то в этом операторе напрямую применяется откомпилированный объект
Здесь уместно вспомнить об опасности применения o с объектами
my $re=/…/o;
который дает неожиданный и неприятный эффект, описанный ранее.
Если применить директиву
use re qw(debug);
то
use re qw(debug); 'abcd' =~ /abc(d)/;
На печать будет выдано:
Compiling REx `abc(d)'
size 9 Got 76 bytes for offset annotations.
first at 1
1: EXACT <abc>(3)
3: OPEN1(5)
5: EXACT <d>(7)
7: CLOSE1(9)
9: END(0)
anchored `abcd' at 0 (checking anchored) minlen 4
Offsets: [9]
1[3] 0[0] 4[1] 0[0] 5[1] 0[0] 6[1] 0[0] 7[0]
Guessing start of match, REx `abc(d)' against `abcd'...
Found anchored substr `abcd' at offset 0...
Guessed: match at offset 0
Matching REx `abc(d)' against `abcd'
Setting an EVAL scope, savestack=3
0 <> <abcd> | 1: EXACT <abc>
3 <abc> <d> | 3: OPEN1
3 <abc> <d> | 5: EXACT <d>
4 <abcd> <v | 7: CLOSE1
4 <abcd> <> | 9: END
Match successful!
Freeing REx: `"abc(d)"'
Текст
1: EXACT <abc>(3) 3: OPEN1(5) 5: EXACT <d>(7) 7: CLOSE1(9) 9: END(0)
является расшифровкой внутреннего представления
1: EXACT <abc>(3)
означает, что надо искать строковый литерал abc длиной 3 символа. Строка
anchored `abcd' at 0 (checking anchored) minlen 4
означает, что текст abcd длины 4 привязан к началу
3: OPEN1(5)
означает, что открылась первая
Функция
study( переменная с целевым текстом )
является еще одной возможностью оптимизации использования
Эта функция создает в переменной с целевым текстом скрытую информацию о тексте, который содержит эта переменная. Это список позиций, в которых каждый символ встречается в данном тексте. В результате на построение этого списка позиций расходуется время и память, которая обычно превосходит в четыре раза объем памяти для текста этой переменной. Поэтому выигрыша во времени можно не получить вовсе, если применять функцию не там, где нужно. Функция может существенно ускорить поиск, если в i сводит на нет усилия функции . Также функция может оказаться полезной, если к одной и той же переменной применяется много операторов с
Для применения оптимизаций, которые возможны с функцией , надо стараться выделять литеральный текст. Например, вместо
a+
надо написать эквивалентный
aa*
а вместо
a{3,6}
нужно записать
aaa{0,3}
Если конструкция выбора (
that|this
записать
th(?:at|is)
В поставке . Он позволяет измерять время выполнения участков кода. При этом можно учитывать только время, которое потрачено процессором на выполнение кода вашей программы, а не на всю систему. Механизм применения этого модуля таков:
use Benchmark; … my $t1=new Benchmark; # Здесь находится участок кода, время работы которого измеряется … my $t2=new Benchmark; print timestr(timediff $t2,$t1);
В переменной $t1 запоминается время начала исполнения участка кода, в переменной $t2 запоминается время окончания выполнения этого участка кода. Затем с помощью функций timediff и timestr выводится разница между временем окончания и временем начала работы участка кода, который тестируется. Но не забывайте, что при первом обращении к
В качестве примера применения хронометража времени поставим задачу скорейшего определения факта наличия между тегами непробельного символа. При этом предполагается, что все теги закрыты и нет вложенных тегов. Для этого разработаем такой алгоритм
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/\A # начало текста
(?> # атомарная группировка
(?: # цикл пропуска пробелов и тегов
(?>\s*) # пропускаем пробельные символы
<(?>[^>]*)> # пропускаем тег с его содержимым
)* # повтор любое число раз
)
\S # и вот он наконец - непробельный символ
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
В этом примере избран такой подход: в цикле (?: … )* пропускаются пробельные символы и теги, а после окончания цикла таких пропусков должен встретиться непробельный символ \S. Если он встретится, то поиск завершится удачей. В переменной $_ создается длинная строка с тегами и пробелами, которая завершается непробельным символом a вне тегов. Чтобы время компиляции $re. Поскольку
При прогоне этой программы на моем компьютере выводится следующее значение времени выполнения заданного участка кода:
1 wallclock secs ( 1.11 usr + 0.00 sys = 1.11 CPU)
Итак, это
Теперь давайте уберем
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/\A # начало текста
(?: # цикл пропуска пробелов и тегов
(?>\s*) # пропускаем пробельные символы
<(?>[^>]*)> # пропускаем тег с его содержимым
)* # повтор любое число раз
\S # и вот он наконец - непробельный символ
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
Распечатка времени показывает, что программа стала работать быстрее и теперь тратит всего 1.08 секунды. Это можно понять так: зачем трудиться по уничтожению сохраненных состояний, когда \s*, то \s* может пригодиться. Кроме того, у нас нет возвратов при переборе. Если убрать последнюю
/\A # начало текста
(?: # цикл пропуска пробелов и тегов
\s* # пропускаем пробельные символы
<[^>]*> # пропускаем тег с его содержимым
)* # повтор любое число раз
\S # и вот он наконец - непробельный символ
/x;
то время выполнения участка кода практически не изменится и станет равным 1.03 секунды.
Предыдущее
(?=[^<>]*>)
А если этот символ стоит вне тегов, то такого текста найдено не будет, поэтому будет истинен
(?![^<>]*>)
Вот вся эта программа:
use Benchmark;
$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/[^\s<>]
(?![^<>]*>)
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
На печати получаем:
1 wallclock secs ( 1.09 usr + 0.00 sys = 1.09 CPU)
Мы видим, что второй вариант работает немного медленнее первого. Очевидно, это связано с тем, что ему приходится проверять каждый символ внутри тегов с помощью негативной опережающей проверки. Отсюда мы видим, что для наших данных (много тегов и мало пробелов) лучше использовать первый вариант
Теперь узнаем, в какую цену обходится встроенный код
use Benchmark;
$_=' <pppp>' x 13000;
$_.='<table>a';
my $count=0;
my $re=qr
/[^\s<>]
(?![^<>]*>)
(?{ ++$count })
/x;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";
На печати появится:
3 wallclock secs ( 3.44 usr + 0.00 sys = 3.44 CPU) 1000000
Время выполнения увеличилось примерно в 3 раза. Сравним это со временем выполнения самого кода автоприращения:
use Benchmark;
my $count=0;
my $t1=new Benchmark;
for (1..1000000) { ++$count }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";
Напечатается
0 wallclock secs ( 0.13 usr + 0.00 sys = 0.16 CPU) 1000000
Мы видим, что сам по себе этот код берет времени намного меньше, чем когда он выполняется внутри
Далее рассмотрим ресурсоемкость динамического
use Benchmark;
$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr/(\d+)\D+(??{"0{$1}"})/;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
Напечатается
4 wallclock secs ( 4.74 usr + 0.00 sys = 4.74 CPU)
А сейчас заменим динамическое \d+:
use Benchmark;
$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr
/(\d+)\D+\d+/;
my $t1=new Benchmark;
for (1..1000000)
{ /$re/;
}
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
В этот раз время уменьшится:
1 wallclock secs ( 1.38 usr + 0.00 sys = 1.38 CPU)
Замечаем, что встроенный код и динамические
Аналогично заметно возрастает время выполнения оператора подстановки s/// с e. Были практические случаи, когда оператор подстановки с e замедлял работу программы в десятки раз.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.