Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2003.06.26;
Скачать: [xml.tar.bz2];

Вниз

Глюки в Дэлфе   Найти похожие ветки 

 
Neft   (2003-06-03 17:12) [0]

Вот скажу честно, что видел как глючит делфя, это стопудов. Короче ставил там чтобы в переменную записывлось значение выражения, а она его как будто даже не видела, хотел протрасировать, так она его обозначет так, ну вы знаете как обозначются брейпоинты на эндах. короче пришлось это выражение запиывать в непрденазначенную для этого другую переменную. мне тут говорили что это может быть оптимизатор кода или чо то типа того гоняет, в общем интересно стало, что за фигня в датском государстве?


 
Mike B.   (2003-06-03 17:14) [1]

Действительно фигня стопудов.


 
Polevi   (2003-06-03 17:21) [2]

это хорошие глюки, добрые


 
Юрий Зотов   (2003-06-03 17:24) [3]

А у меня дома лампочка глючит. Пока выключателем не щелкнешь - ни фига гореть не хочет. Стопудово!

А раз щелкнул - все равно не горит. И холодильник не работает. Потом сосед пришел - говорит, и у него то же самое. Позвонили еще соседям - и у них то же. Короче, во всем доме такая фигня. А потом само собой у всех загорелось. И холодильники включились. Стопудовый глюк! Честно, сам видел. Че за фигня в датском государстве?


 
Digitman   (2003-06-03 17:34) [4]

или чо то типа того !


 
DrPass   (2003-06-03 17:57) [5]

Ну ты типакороче там как тибе говорят аптимизатор кода походу выключи да? Ну дык там глюки типа прападут


 
Neft   (2003-06-03 20:09) [6]


> Юрий Зотов

неудачное сравнение, не видел чтобы у всез одновременно глючила делфя. а тот "сосед" не такой тупой как ты его выставил, может и побольше тебя сечет в программинге, и вообще, если это оптимизатор, то как обходить такие глюки, не отрубая его


 
Юрий Зотов   (2003-06-03 20:25) [7]

> Neft © (03.06.03 20:09)

> не видел чтобы у всез одновременно глючила делфя

Уверяю Вас, такие "глюки", как Вы описали, есть именно у всех и именно одновременно.


> если это оптимизатор, то как обходить такие глюки, не отрубая
> его

Очень просто - надо писать ТАКОЙ код, чтобы НИ ОДНУ Вашу переменную оптимизатор не считал лишней. Тогда он ее и выбрасывать не будет.


 
Neft   (2003-06-03 20:56) [8]

Вот код.

For I:=0 to n-1 do
Begin
For J:=I+1 to n-1 do
Begin
fLambda[0]:=fLambda[0]+KF.c[I,J]*XTemp[I,1]*XTemp[J,1];
fLambda[1]:=fLambda[1]+KF.c[I,J]*XTemp[I,0]*XTemp[J,1]+KF.c[I,J]*XTemp[I,1]*XTemp[J,0];
End;
End;

If fLambda[0]<>0 Then fLambda[0]:=-fLambda[1]/2/fLambda[0] Else fLambda[0]:=0;
if fLambda[0]>1 Then fLambda[0]:=1;//здесь вместо fLambda[0]должна была быть другая переменная, чтобы использоваться в следующем цикле.

For I:=0 to n-1 do
PNew[I]:=P[I]+fLambda[0]*(Z[I]-P[I]);



Ну так вот. Вот тебе пример. если тут реально что то используется неоптимально, то прошу рассказать в чем фишка.
P.S. Я понимаю, что компилятор выдает сообщение о неиспользуемых переменных, но чтобы не выполнять команду... в чем моя ошибка???

> Юрий Зотов



 
Neft   (2003-06-03 21:18) [9]

Зотов ответь...


 
Ihor Osov'yak   (2003-06-03 21:35) [10]

> здесь вместо fLambda[0]должна была быть другая переменная, чтобы использоваться в следующем цикле.

> короче пришлось это выражение запиывать в непрденазначенную для этого другую переменную.

Ты не вумничай, ты код приведи as is.. Тот, к которому у тебя якобы претензии..


 
Neft   (2003-06-03 21:38) [11]


> Ihor Osov"yak

а ты не груби

If fLambda[0]<>0 Then fLambda[0]:=-fLambda[1]/2/fLambda[0] Else fLambda[0]:=0;
if fLambda[0]>1 Then fLambda[0]:=1

а нужно было:

If Lambda[0]<>0 Then Lambda:=-fLambda[1]/2/fLambda[0] Else fLambda[0]:=0;
if Lambda>1 Then Lambda:=1


 
Ihor Osov'yak   (2003-06-03 21:47) [12]

> а ты не груби

Ну-ну.. Это еще вопрос, кто первым начинает (не по отношению ко мне, а вообще)

> if Lambda>1 Then Lambda:=1


Откуда это лямбда узялось, где инициализируется, где потом используется? Повторяю - не упрямся, код приведи достаточный для анализа. Вместе с декларациями переменных, кодом присваивания начальных значений и областью ухода из области видимости..



 
Юрий Зотов   (2003-06-03 21:49) [13]

> Neft

ОК, давайте разбираться. Раз уж речь идет об оптимизаторе, то надо иметь в виду, что с локальными и глобальными переменными он обращается по-разному. Значит, если Вы хотите услышать что-то конкретное, то Вы должны дать, как минимум, такую информацию по КАЖДОМУ используемому идентификатору:

1. Его реальное объявление;
2. Где это объявление стоит;

И, кроме того, где находится приведенный Вами код.

Жду.


 
Neft   (2003-06-03 21:53) [14]


> Юрий Зотов

приятно разговаривать с вежливым человеком ))
просто игнорировалась такая строчка
Lambda:=-fLambda[1]/2/fLambda[0];
без всяких ифов и с ними она просто не вычислялась, пришлось делать как сделал. я же говорю что это глюк. неужели никто ни разу не видел как,поднимаеш например одну строчку на позицию вверх и ошибка пропадает. я честно не вру.



 
Ihor Osov'yak   (2003-06-03 22:08) [15]

2 Neft © (03.06.03 21:53)

Если Lambda потом нигде не использовалась, то оптимизатор выбрасывал ненужный код.

Чтобы однозначно ответить на этот вопрос, нужно увидеть фрагмент кода полностью, включая декларации и место ухода из области видимости, если это локальная переменная. Если это глобальная, или поле класса - то код, необходимый для анализа, будет значительно больше.

Зы. Это не глюк. Это нормальная работа оптимизирующего компилятора.


 
Юрий Зотов   (2003-06-03 22:08) [16]

> Neft © (03.06.03 21:53)

> приятно разговаривать с вежливым человеком

Вы даже более чем правы. Мне тоже.


> я честно не вру.

Я знаю, что Вы не врете. Я даже это очень хорошо знаю. Речь идет не об этом, а о том, чтобы разобраться с причиной. Но я не могу ее показать, потому что не знаю, где и как у Вас объявлены все эти переменные - а именно от этого и зависит поведение оптимизатора.

Поэтому Вам нужно эти объявления привести, иначе я могу сказать только одно - никакой это не глюк, а совершенно нормальная (и очень полезная!) особенность оптимизируещего компилятора. Так что, Вы сами понимаете, что Ваш первый постинг, в котором прозвучала эдакая тинейджерская самоуверенность при полном незнании предмета, был просто смешным. Вот почему над ним и смеялись.

Давайте информацию о переменных - будем смотреть дальше.


 
Neft   (2003-06-03 22:14) [17]

Function Search;
Var Proisv : TSyst;
I,J : Integer;
Grad : TPoint;
Z : Array of Real;
Syst : TSyst;
PNew : TPoint;
Res : Byte;
XTemp : Array of Array[0..1] of Real;
fLambda : Array[0..1] of Real;


Lambda была типа Real.
насчет тинейджерской самоуверенности. просто я пишу так как я разговариваю, только маты убираю. вы же понимаете что я в 22 года разговариваю на соответсвующим сленге.


 
Ihor Osov'yak   (2003-06-03 22:20) [18]

2 Юрий Зотов

Я бы хотел ошибится, но мне кажется, что разговор с Neft нужно прекращать.

Если человека, как минимум, дважды просят привести исходный код, а он этого по каким-то загадочным причинам этого сделать не может, то.. я не настолько терпелив..

Приношу еще раз извинения за резкий выпад.


 
Neft   (2003-06-03 22:21) [19]


> Юрий Зотов

кстати прогу когда седня посмотрел, вспомнил, что я там недоделал)), так бы вообще пропустил


 
Юрий Зотов   (2003-06-04 01:26) [20]

Что-то тут не срастается. Например, объявление
PNew : TPoint;
никак не вяжется с PNew[i] в коде. А объявления P вообще нет. И даже непонятно, все приведенные Вами объявления - локальные по отношению к коду, или внешние (например, локальные в охватывающей функции Search).

Так не годится, или давайте всерьез, или не будем тратить время.


 
k-man   (2003-06-04 08:32) [21]

Можно ли параллельно задать вопрос по данной теме Юрию Зотову?
Вопрос такой: Часто при отладке пытаюсь смотреть значения разных переменных из окна Watch. Но бывает натыкаюсь на такое препятствие: вместо значения переменной получаю строку типа "Значение переменной недоступно из-за оптимизации"
Вопрос: Если я отключу ее (оптимизацию) будут ли значения этих переменных видны всегда? И еще: Это происходит далеко не со всеми переменными, Почему? И что дает это не показывание значений в коде?


 
Юрий Зотов   (2003-06-04 11:01) [22]

IMHO, "значение переменной недоступно из-за оптимизации" означает, что транслятор построил код так, что в данном месте этой переменной в машинном коде просто не существует. То есть, не существует соответствующего ей адреса. Например, в цикле используется инвариантная переменная, поэтому код для ее вычисления транслятор разместил перед циклом, а уже готовый результат загнал в регистр, не размещая в памяти вообще никакой переменной. Или транслятор как-то еще изменил порядок операций и теперь он не совсем соответствует исходному коду. И т.д. - там целая наука на эту тему существует.

Естественно, так транслятор поступает далеко не всегда и далеко не со всеми переменными. Иначе это был бы уже не транслятор, а искусственный интеллект, да и железо этого не позволяет (скажем, количество регистров не бесконечно, чтобы хранить в них все подряд). Кстати, ведь и отладчик - это тоже не искуственный интеллект, а всего лишь программа, и она тоже не все умеет.

Если транслятор написан корректно (будем считать, что так оно и есть - а что нам остается?), то на работоспособности программы все это сказывается только в лучшую сторону. Но затрудняет отладку - поэтому и вводится опция отключения оптимизации (а иногда и опции типа "что оптимизировать - размер или скорость").

Если оптимизация отключена, то значения переменных будут видны в подавляющем большинстве случаев (почти всегда, особенно, если еще разрешить вызов функций для вычисления этих значений, есть и такая опция). Но могут быть какие-то особенности компилятора, которые в принципе не дают отладчику возможности вычислить эти значения при данном построении кода. Отсюда и идут чудеса типа "перенес на строку выше - стало показывать".

Все это не глюки, а нормальная работа компилятора. Нужно понять главное: компилятор - это переводчик с одного языка на другой, а даже в намного более богатых человеческих языках идеально точный перевод иногда бывает невозможен (да и не нужен, потому что, например, пословицу одного народа всегда можно заменить аналогичной пословицей другого народа). А уж задача художественного перевода (то есть, той же оптимизации) - это вообще перевод смысла, а не текста.


 
k-man   (2003-06-04 11:29) [23]

Из Вашего ответа я могу сделать вывод о том
1) При отладке приложения оптимизацию можно исключить и включить только при окончательной сборке.(Тут параллельно возникает вопрос, а насколько полезна оптимизация для современных компьютеров с большим кол-вом памяти и высокой часотой процессора).
2) При правильном построении кода не будет этих самых "глюков".
(Тут опять возникает вопрос о том, что есть правильное построение кода....)


 
Юрий Зотов   (2003-06-04 11:50) [24]

1. Я всегда так и делаю.

2. Если уж мы пишем не на ассемблере, да еще используем RAD, то говорить об особой уж там оптимизации как-то все же немного странно. Поэтому я стараюсь писать код, руководствуясь, прежде всего, критериями надежности, читабельности, простоты, легкости его будущей модификации, универсальности, повторной используемости и пр. Но и в то же время буквально в каждом слове и в каждой строке думаю, а как это воспримет компилятор и во что это в итоге превратится - то есть, оптимизирую код САМ (кстати, это и есть лучший способ оптимизации, потому что транслятор не гений и много он там наоптимизировать все равно не сможет, да и оптимизировать уже оптимизированный программистом код ему проще, и вероятность внесения им глюков при этом тоже меньше).

Иногда, конечно, бывают какие-то куски кода, где, например, скорость или память выходят на первый план - ну так к этим кускам и подход другой.


 
NailMan   (2003-06-04 13:35) [25]

Ну тогда я тоже задам такой вопрос касающийся глюков Дельфинариума:

В функции есть цикл, в котором создается объект.
Все бы хорошо, но счетчик цикла почему то не инициализируется с нуля хотя указано прямо:
for i:=0 to ActorCount-1 do ...

var i:integer;

переменная i локальная, в теле for она не изменяется и работает только как счетчик, тоесть не используется для индексации.

Ловлю значение в i при пошаговом выполнении программы и наблюдаю что при первом входе в цикл переменная i либо заблокирована(при наведении мыша значение не показывается) либо в ней значение ActorCount и дальше увеличивается.

Сделал для счетчика переменную NN:integer - все нормально.
Цикл выполняется как положено, с нуля.

Ну и что это за баг такой? Кто мне объяснит?


 
k-man   (2003-06-04 13:40) [26]

Опять таки я думаю тебе надо привести полный код функции
со всеми объявлениями.
Говоришь, что если другой идентификатор то все нормально?
Посмотри где у тебя еще может быть описана i.


 
Юрий Зотов   (2003-06-04 13:47) [27]

В первом случае компилятор строит код с декрементированием регистра ECX (или EDX) и с переходом JZ (или JNZ) - то есть, как нисходящий. Это хорошо видно в окне CPU и это действительно оптимально. Как он строит его во втором случае - зависит от того, где объявлена переменная NN и как она используется, помимо счетчика цикла.


 
Ihor Osov'yak   (2003-06-04 13:57) [28]

2 NailMan © (04.06.03 13:35)

Поставьте точку останова в начале цыкла и после ее срабатывания сделайте CTRL_ALT_C. Если в ассемблере немного разбираетесь, сразу все станет ясно.. Если нет - то в общих чертах дело обстоит так:
для цыклов for если переменная цыкла не используется в цыкле, то в регистр ecx заносится количество выполнений цыкла, а потом при каждом прохождении цыкла делается декремент ecx. Условие окончания цыкла - равенство ecx нулю. Почему компилятор для этого случая делает так? наверное потому, что сравнение с нулем происходит быстрее. Вернее, даже сравнения не нужно - просто анализируется соотв. флаг из регистра флагав после декремента.. И анализ идет не отдельной командой, а командой условного перехода. То есть код получается достаточно компактный и эффективный..

Зы - см. ЮЗ, там где он говорил о переводе смысла, а не о дословном переводе..



 
NailMan   (2003-06-04 14:06) [29]

To -> k-man ©
полный код привести не могу, так как функция габаритная, да и глюкает почти в самом начале, привожу краткий кусок:

Function TScene.Load(name:string):boolean;
var i:integer;
Z:integer;
st:string;
begin
... открываю файл сцены и считываю переменные ActorCount, LightCount

//ActorCount считывается со значением 2

for i:=0 to ActorCount-1 do
begin
z:=GetFreeIndex(FREE_ACTOR);
Actor[Z]:=TActor.Create;
...считывание строки с именем конфига в st и параметры расположения в мире
Actor[Z].LoadINI(st);
Actor[Z].MoveToS(0,0,0);
end;

For i:=0 to LightCount-1 do
begin
... считывание данных о источнике света и установка их в i-й источник Light[i]
end;

end;


Собсно глюкает именно в первом цикле c Actor. Второй цикл проходит нормально. Добавляю в var nn:integer; и юзаю его в первом цикле - все нормально.

Посмотри где у тебя еще может быть описана i.
В глобальных переменных никаких счетчиков не описано. Юзается именно локальная.


To -> Юрий Зотов ©
Как он строит его во втором случае - зависит от того, где объявлена переменная NN и как она используется, помимо счетчика цикла.
как я уже говорил NN есть только в локальных переменных данной функции. Нигде больше я не создавал переменных с подобным именем.


Ну что скажете, мастера?


 
evvcom   (2003-06-04 14:34) [30]

Тебе она нужна эта i или NN? Ну и чего ты голову всякими пустяками забиваешь? Ну наоптимизировал там чего-то компилятор, что прога не работает разве? Разберись лучше с ассемблером, и тогда на подобные вопросы сможешь сам отвечать без труда.


 
NailMan   (2003-06-04 14:40) [31]

To -> evvcom ©
дык работать-то работает, важен сам факт. Я вообще пробовал через repeat until и все прекрасно работает с одной i.


 
Danilka   (2003-06-04 14:44) [32]

NailMan © (04.06.03 14:06)
а в каком месте в этом коде точка останова обозначается как "ну вы знаете как обозначются брейпоинты на эндах"?
:))
что-то я не вижу таких мест в этом коде.


 
evvcom   (2003-06-04 14:45) [33]

Ну и хорошо, что работает. Компилятор видит, что тебе в этом коде эта i нужна только как счетчик безо всякой индексации и др. расчетов. Вот он и оптимизирует насколько может. Возможно, что в первом случае у него хватило регистров для хранения счетчика, а во втором уже не хватило, и он отвел под эту переменную место в памяти. Соответственно и код уже другой получился.


 
McSimm   (2003-06-04 15:04) [34]

Господа, темы с радостными известиями о том что наконец-то найдены глюки в компиляторе поднимались уже очень много раз.
(Причем, похоже, первое место по частоте здесь занимает обратный отсчет в цикле :)
И всегда выяснялось, что никаких глюков на самом деле нет, а имеет место быть нормальная работа оптимизатора кода.


 
McSimm   (2003-06-04 15:16) [35]

Вот, думаю в тему будет.
Не далее как вчера некоторое время с удивлением рассматривал фрагмент кода (причем довольно типичный):

K := Arr^[J]; //1
Arr^[J] := Arr^[I]; //2
Arr^[I] := K; //3


Удивляло в этом фрагменте то, что строка //1 не имела исполняемого кода, отладчик проходит только по //2 и по //3.
При этом значение переменной K можно было посмотреть только в строке //3 и оно было верным!
Не правда ли, на первый взгляд кажется загадочным :)

Оказалось все очень просто:

if Arr^[J] < Arr^[I] then // <-здесь отгадка :)
begin
K := Arr^[J];
Arr^[J] := Arr^[I];
Arr^[I] := K;
end;


Чтобы выполнить сравнение компилятор взял значение Arr^[J] в регистр, поскольку выполнить сравнение с двумя индексируемыми ячейками памяти непосредственно он не имеет возможности.
Оптимизатор справедливо рассудил, что значение в регистре вполне заменит использование переменной K и удалил саму переменную и код. А умный отладчик сумел в строке //3 распознать значение, используемое вместо переменной K.


 
Юрий Зотов   (2003-06-04 15:38) [36]

> NailMan © (04.06.03 14:06)

> как я уже говорил NN есть только в локальных переменных данной
> функции

Вот этого Вы как раз и не говорили, а говорили вот что:
"Сделал для счетчика переменную NN:integer"
Заметьте - здесь ничего не сказано о том, локальная она, или глобальная, используется ли где-то еще, или нет. А как раз от этого многое и зависит.

Ну, ладно, сказали - и хорошо. Я воспроизвел Вашу ситуацию в D5 (с включенной оптимизацией) на таком макете:

procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
for i := 0 to 5 do
MessageBeep(0); // Здесь BreakPoint
end;

И затем переменная i заменялась на переменную NN. Так вот, в обоих случаях я прекрасно вижу в отладчике значение счетчика цикла и оно меняется от 6 до 1. А в окне CPU вижу, что в обоих случаях компилятор строит АБСОЛЮТНО одинаковый код:

mov ebx, $00000006 ; начальное значение счетчика цикла в ebx
push $00 ; параметр MessageBeep заталкивается в стек
call -$00000799 ; вызов MessageBeep
dec ebx ; декремент счетчика
jnz TForm1.Button1Click + $6 ; переход на начало по флагу нуля

Что, собственно, и следовало ожидать.

Хорошо, теперь делаем переменную NN глобальной и запускаем тот же самый код. Во-первых, сразу же получаем предупреждение компилятора о том, что этот код он не может оптимизировать. Во-вторых, теперь отладчик показывает, что NN меняется от 0 до 5 - то есть компилятор построил код "в лоб". В-третьих, именно такой "дословный перевод" мы и видим в окне CPU (код не привожу, лень переписывать, хотите - проверьте сами).

Что, собственно, тоже и следовало ожидать.

Делаем выводы:

1. Воспроизвести Вашу ситуацию не удалось. Думаю, потому, что Вы привели неточную или неполную исходную инфоромацию.
2. Никаких глюков не обнаружено.

P.S.
Циклы while и repeat - это другая статья, там нет никаких счетчиков и поэтому к ним такая оптимизация не относится.


 
Neft   (2003-06-04 19:49) [37]

короче я уже заколебался втавляьб модуль, не влазит даже пятая часть


 
Neft   (2003-06-04 19:51) [38]

unit WolfUnit;

interface
Uses GoldSech;
Type
{


 
Neft   (2003-06-04 19:54) [39]

Function Search;
Var Proisv : TSyst;
I,J : Integer;
Grad : TPoint;
Z : Array of Real;
Syst : TSyst;
PNew : TPoint;
Res : Byte;
XTemp : Array of Array[0..1] of Real;
fLambda : Array[0..1] of Real;
Begin
SetLength(Proisv,n,n+1);
SetLength(Grad,n);
SetLength(PNew,n);
SetLength(Z,2);

For I:=0 to n-1 do
Begin
For J:=0 to I do
Begin
If I=J Then
Proisv[I,J]:=KF.C[J,I]*2
Else
Proisv[I,J]:=KF.C[J,I];
End;
For J:=I+1 to n-1 do
Begin
Proisv[I,J]:=KF.C[J,I];
End;
Proisv[I,n]:=KF.D[I]
End;


SetLength(Syst,m,n+1);
For I:=0 to m-1 do
For J:=0 to n-1 do
Syst[I,J]:=Ogr.A[I,J];
For I:=0 to m-1 do Syst[I,n]:=Ogr.B[I];


GradCalc(P,Grad,Proisv);
SM(Syst,m,n,Z,Res,Grad);

SetLength(XTemp,n);

For I:=0 to n-1 do
Begin
XTemp[I][0]:=P[I];
XTemp[I][1]:=Z[I]-P[I];
End;

For I:=0 to n-1 do
Begin
XTemp[I][1]:=Z[I]-P[I];
End;

//fLambda[0] -


 
k-man   (2003-06-04 20:04) [40]

2Neft
в таких случаях модуль делят.



Страницы: 1 2 вся ветка

Форум: "Потрепаться";
Текущий архив: 2003.06.26;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.58 MB
Время: 0.032 c
14-84630
Liso
2003-06-10 00:22
2003.06.26
zdrastwuj od padruga z polszy


1-84307
Мыш
2003-06-15 17:55
2003.06.26
встраиваемый плагин


1-84097
Chlavik
2003-06-10 15:01
2003.06.26
ПРодолжение...


1-84229
Ghost
2003-06-13 08:48
2003.06.26
Функция для удаления из Listbox2 строк которые есть в Listbox1


1-84115
mao
2003-06-10 15:43
2003.06.26
Некрасивое мерцание формы :(





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский