HLFX.Ru Forum
Показать все 43 сообщений этой темы на одной странице

HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- Технические вопросы (https://hlfx.ru/forum/forumdisplay.php?forumid=20)
-- Почему MSVC2010 генерирует такой дерьмовый код? (https://hlfx.ru/forum/showthread.php?threadid=4598)


Отправлено XaeroX 08-05-2015 в 01:21:

Почему MSVC2010 генерирует такой дерьмовый код?

Сегодня с удивлением заметил проблему: колоссальное падение производительности при попытке вычислить максимальное значение. Код вот такой:

C++ Source Code:
float f = somestuff;
f = max( f, 0 );

max стандартно определён как макрос в minmax.h:
C++ Source Code:
#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

Так вот этот код, будучи скомпилирован в MSVC2010, выполняется в три (!) раза медленнее, чем в MinGW!
Смотрим ассемблер:
code:
fld DWORD PTR _dp$68919[ebp] fcom ST(1) fnstsw ax test ah, 65 ; 00000041H jne SHORT $LN26@Mathlib_In@6 fstp DWORD PTR tv194[ebp] jmp SHORT $LN21@Mathlib_In@6 $LN26@Mathlib_In@6: fstp ST(0) fst DWORD PTR tv194[ebp] $LN21@Mathlib_In@6: fld DWORD PTR tv194[ebp]

Вот что это за дерьмецо, товарищи? Какого чёрта он лезет за нулём в стек? Проверяет контрольное слово, вынося его в ax? Джампы какие-то? И ладно бы, в дебаге. Это В РЕЛИЗЕ, при максимальной оптимизации!
А вот что генерит MinGW:
code:
fldz fxch %st(1) fcomi %st(1), %st fcmovb %st(1), %st fstp %st(1)

Красивый и быстрый код. Ноль берём из fldz, выбор значения делаем специально сделанной для этого кондишнл-инструкцией fcmovb.
Ну, собсно, два вопроса:
1) Почему компилятор MSVC2010 - говно наипреговённейшее, говнявое, преговённое, которое без предупреждения генерит в релизе такой жутко неоптимальный код?
2) Как обойти эту проблему? Я и темплейт-функциями пытался, и опции компилятора всякие крутил. Нифига. Вот стабильно такой код. Ощущение, будто в Microsoft не знают половину инструкций сопроцессора. Но это же бред, верно? Но зато теперь понятно, почему волатила так тормозит.

А может быть, это только мой компилятор взбесился? У других всё нормально?

__________________

xaerox on Vivino


Отправлено Skaarj 08-05-2015 в 05:55:

А если заменить ассемблерной вставкой и забыть про это?


Отправлено ComradeAndrew 08-05-2015 в 06:39:

Skaarj раз этот код такой, то и весь остальной такой же. Не на ассемблере же теперь писать. Проще компилировать через minGW, но это может не вариант.
Попробую посмотреть позже как на vs2013.
Можно полный пример кода? Чтобы функцию оптимизацией не откинуло.


Отправлено XaeroX 08-05-2015 в 08:36:

Цитата:
ComradeAndrew писал:
Можно полный пример кода? Чтобы функцию оптимизацией не откинуло.

C++ Source Code:
1
void Mathlib_Internal_TransformVectorsLV_SingleBone( int count, const float *inputVectors, const vmd_matrix_t *inputMatrices, const float *lightDir, float *outputVectors )
2
{
3
  for ( ; count > 0; --count, inputVectors += 4, outputVectors += 4 ) {
4
    outputVectors[0] = inputVectors[0] * inputMatrices[0][0][0] + inputVectors[1] * inputMatrices[0][0][1] + inputVectors[2] * inputMatrices[0][0][2] + inputMatrices[0][0][3];
5
    outputVectors[1] = inputVectors[0] * inputMatrices[0][1][0] + inputVectors[1] * inputMatrices[0][1][1] + inputVectors[2] * inputMatrices[0][1][2] + inputMatrices[0][1][3];
6
    outputVectors[2] = inputVectors[0] * inputMatrices[0][2][0] + inputVectors[1] * inputMatrices[0][2][1] + inputVectors[2] * inputMatrices[0][2][2] + inputMatrices[0][2][3];
7
    float dp = outputVectors[0] * lightDir[0] + outputVectors[1] * lightDir[1] + outputVectors[2] * lightDir[2] - 0.5f;
8
    outputVectors[3] = max( dp, 0.0f );
9
  }
10
}


Цитата:
Skaarj писал:
А если заменить ассемблерной вставкой и забыть про это?

Не получится. Этот код полагается на содержимое стека сопроцессора. Если выносить в отдельную функцию - потребуются дополнительные fld/fsp, и теряются возможности по оптимизации типа instruction flow.
Это во-первых. А во-вторых, в 64-битном коде всё компилируется с использованием SSE-инструкций (неоптимально, конечно, но тем не менее), и FPU-код там будет не в тему.

__________________

xaerox on Vivino


Отправлено ComradeAndrew 08-05-2015 в 09:52:

XaeroX
Вроде получил код с простым примером.
Platform Toolset v120
оптимизация /O2

code:
012D101F movss xmm0,dword ptr [f] 012D1024 xorps xmm1,xmm1 012D1027 add esp,8 012D102A comiss xmm0,xmm1 012D102D ja main+37h (012D1037h) 012D102F movaps xmm0,xmm1 012D1032 movss dword ptr [f],xmm0


Отправлено XaeroX 08-05-2015 в 10:09:

Цитата:
ComradeAndrew писал:
Platform Toolset v120

Ставь тулсет v100. И отключи /arch:SSE, если стоит.

__________________

xaerox on Vivino


Отправлено ComradeAndrew 08-05-2015 в 10:59:

XaeroX
Достать у MS старые версии VS не так-то просто.

>>И отключи /arch:SSE
Где отключить?

Платформа x64?
Platform Toolset v100; /O2

code:
00007FF6830D1025 movss xmm0,dword ptr [f] 00007FF6830D102B xorps xmm1,xmm1 00007FF6830D102E comiss xmm0,xmm1 00007FF6830D1031 ja main+3Ch (07FF6830D103Ch) 00007FF6830D1033 movaps xmm0,xmm1 00007FF6830D1036 movss dword ptr [f],xmm0


Добавлено 08-05-2015 в 13:59:

А нет. То что сверху, это v120 на x64. Я не уследил, когда платформу выставил.
Для v100 x86 /O2
code:
00371013 fldz 00371015 fcom dword ptr [f] 00371018 add esp,8 0037101B fnstsw ax 0037101D test ah,5 00371020 jnp main+27h (0371027h) 00371022 fstp dword ptr [f] 00371025 jmp main+29h (0371029h) 00371027 fstp st(0)


Отправлено XaeroX 08-05-2015 в 18:06:

Ну получше, по крайней мере есть fldz.
Но всё равно не так красиво, как в MinGW/GCC.
Это ты 2013-й студией компилил с тулсетом v100?

__________________

xaerox on Vivino


Отправлено ComradeAndrew 08-05-2015 в 19:20:

XaeroX
Да. 2013 студия с тулсетом от visual studio 2010 express.
Как я понял с этой express нельзя на x64 компилировать. По крайней мере, мне написало, что тулсет не установлен.
Хорошо хоть community edition теперь сделали, где, вроде, ничего не урезано и можно vs assist поставить.


Отправлено XaeroX 08-05-2015 в 19:23:

Меня 2010 студия во всём устраивала. До вчерашнего дня, пока не обнаружился этот досадный баг. Который, по всей видимости, частично и в 2013 остался - раз код по-прежнему недостаточно оптимален.
Переходить на MinGW не вариант, там своих проблем хватает. В целом микрософтовский компилятор мне показался надёжнее. Но вот как это говно забороть-то?

__________________

xaerox on Vivino


Отправлено ComradeAndrew 08-05-2015 в 19:32:

XaeroX
Могу попробовать ещё на vs 2015. Для сравнения, так сказать. Исправились ли они или ошибки тащат в следующие релизы.
И в саппорт, наверное, им надо репортнуть. Может что скажут.


Отправлено XaeroX 08-05-2015 в 19:48:

Цитата:
ComradeAndrew писал:
Могу попробовать ещё на vs 2015.

Попробуй, пожалуйста.
Но, возможно, они уже забили на тулсет 100.
Цитата:
ComradeAndrew писал:
Может что скажут.

А то я не знаю, что они скажут. Скажут "купите виндовс 10 и вижуал студию 2015, а тулсет 100 - легаси и депрекейтед".
А то, что вы хотите поддерживать процессоры без SSE - это ваши глубоко личные проблемы.

__________________

xaerox on Vivino


Отправлено ComradeAndrew 08-05-2015 в 21:19:

XaeroX
v140 x86 /O2

code:
012810C3 movss xmm0,dword ptr [esp+40h] 012810C9 xorps xmm1,xmm1 012810CC add esp,8 012810CF comiss xmm0,xmm1 012810D2 ja main+3Dh (012810DDh) 012810D4 movaps xmm0,xmm1 012810D7 movss dword ptr [esp+38h],xmm0


Отправлено XaeroX 09-05-2015 в 03:04:

ComradeAndrew
Опять ты SSE-код показываешь. Да знаю я, знаю, что под SSE нормально всё. В 2010 тоже нормально под х64. Меня интересует исключительно FPU-код, совместимый.

__________________

xaerox on Vivino


Отправлено ComradeAndrew 09-05-2015 в 05:01:

XaeroX а где отключать-то?


Отправлено XaeroX 09-05-2015 в 05:43:

ComradeAndrew
Ну в 2013 ты же как-то отключил?
В 2010 в настройках можно выбрать тулсет. На v100 используется FPU, если явно не включить SSE настройкой /arch. То же самое поведение у GCC/MinGW.

__________________

xaerox on Vivino


Отправлено ComradeAndrew 09-05-2015 в 07:02:

XaeroX
Не. На тулсете v120, как и на v100 я ничего не менял, а по дефолту там настройка /arch (not set).
Нашел где эта настройка. Итак, давай теперь по порядку.
Везде оптимизация /O2:

Platform toolset v100
/arch (not set)

code:
008A1013 fldz 008A1015 fcom dword ptr [f] 008A1018 add esp,8 008A101B fnstsw ax 008A101D test ah,5 008A1020 jnp main+27h (08A1027h) 008A1022 fstp dword ptr [f] 008A1025 jmp main+29h (08A1029h) 008A1027 fstp st(0)


Platform toolset v120
No Enhanced Instructions (/arch:IA32)
code:
0002101F fldz 00021021 add esp,8 00021024 fld dword ptr [f] 00021027 fcom st(1) 00021029 fnstsw ax 0002102B test ah,41h 0002102E je main+3Ah (02103Ah) 00021030 fstp st(0) 00021032 fstp dword ptr [f] 00021035 fld dword ptr [f] 00021038 jmp main+3Ch (02103Ch) 0002103A fstp st(1)


Platform toolset v140
No Enhanced Instructions (/arch:IA32)
code:
008210C3 fldz 008210C5 add esp,8 008210C8 fld dword ptr [esp+38h] 008210CC fcom st(1) 008210CE fnstsw ax 008210D0 test ah,41h 008210D3 je main+41h (08210E1h) 008210D5 fstp st(0) 008210D7 fstp dword ptr [esp+38h] 008210DB fld dword ptr [esp+38h] 008210DF jmp main+43h (08210E3h) 008210E1 fstp st(1)


Настройки "Enable Enhanced Instructions":
На v100 3 настройки:


На v120 и v140 6 настроек:


Отправлено XaeroX 09-05-2015 в 08:33:

Ну странно, почему он не хочет использовать fcomi/fcmovb? Полагает, что распределяет нагрузку между целочисленным и floating-point модулями?
А какой вариант быстрее у тебя оказывается? Можешь по-быстрому спрофилировать, плз?

__________________

xaerox on Vivino


Отправлено ComradeAndrew 09-05-2015 в 09:13:

XaeroX
Ну, если я правильно понимаю как это делается, то смотрим.
Проверим на v140

C++ Source Code:
1
#include <stdio.h>
2
#include <stdlib.h>
3
 
4
#ifndef max
5
#define max(a,b)            (((a) > (b)) ? (a) : (b))
6
#endif
7
 
8
int main(int argc, char** argv)
9
{
10
  float f = 0;
11
  int i;
12
  int iterations = (int)atoi(argv[1]);
13
  for (i = 0; i < iterations; i++) {
14
    f += (i%2) ? i: -i;
15
    f = max(f, 0.0f);
16
  }
17
  printf ("%f\n", f);
18
 
19
  return 0;
20
}

На вход даем 1000000000 (10^9)

Среднее время 5 таких прогонов:


Отправлено XaeroX 09-05-2015 в 12:47:

Эм... Спасибо, конечно, но я имел в виду - спрофилировать FPU-код из разных тулсетов.
А что, у тебя процессор держит AVX и даже AVX2? Какой у тебя?

__________________

xaerox on Vivino


Отправлено ComradeAndrew 09-05-2015 в 13:47:

У меня Intel Core i5-4210U( 64-bit, SSE4.1/4.2, AVX 2.0 ).

Ну, FPU на всех трех почти одинаково.
Тот же тест на них:
v140 : 3355ms
v120 : 3530ms
v100 : 3267ms

Учитывая прошлый результат v140 - разницы нет.

Или это не так делается?


Отправлено XaeroX 09-05-2015 в 14:50:

У меня тоже Core-i5, но AVX нет.
Может, потому, что он появился в 2011 году, а процессор я купил в 2009? Кто его знает...

Цитата:
ComradeAndrew писал:
Ну, FPU на всех трех почти одинаково.

Понятно, спасибо. Похоже, в 2010 всё-таки баг.

__________________

xaerox on Vivino


Отправлено ~ X ~ 16-05-2015 в 15:06:

Цитата:
XaeroX писал:
А то, что вы хотите поддерживать процессоры без SSE - это ваши глубоко личные проблемы.

Я, конечно, не стану Дядей Мишей и на ВС6 не пересяду после такого. Но есл получится мод компилить мингвом, то так тому и быть.

Кстати, ЕМНИП, кто-то (оп) очень любил десяточку и мне её жаоко рекомендовал ))))))))))

__________________
Минутка полезного:
Бесплатный UT-подобный Half-Life mod.
Бесплатный редактор для 32-битных текстур. Без дотнета.
Бесплатный IDE для любых компиляторов и ЯП.
Бесплатная Windows-подобная ОС.
Проверка грамматики русского языка.
Чат по hl[fx]: half-life@conference.jabber.ru


Отправлено XaeroX 16-05-2015 в 15:58:

Цитата:
~ X ~ писал:
Кстати, ЕМНИП, кто-то (оп) очень любил десяточку и мне её жаоко рекомендовал

Я рекомендовал микрософтовский компилятор как таковой. В целом-то он более быстрый код генерит. Я собирал старую волатилу мингвом и десяткой, в десятке, по ощущениям, было немного быстрее. То есть там, вероятно, есть другие моменты, которые реализованы лучше, чем в мингве.

__________________

xaerox on Vivino


Отправлено ~ X ~ 16-05-2015 в 16:34:

В общем, "ужасы нашего городка". Интел юзать нельзя, опен64 только под 64...

__________________
Минутка полезного:
Бесплатный UT-подобный Half-Life mod.
Бесплатный редактор для 32-битных текстур. Без дотнета.
Бесплатный IDE для любых компиляторов и ЯП.
Бесплатная Windows-подобная ОС.
Проверка грамматики русского языка.
Чат по hl[fx]: half-life@conference.jabber.ru


Отправлено MegaGovnoCoder 14-09-2015 в 15:55:

Какие процессоры не поддерживают SSE ?


Отправлено XaeroX 14-09-2015 в 16:08:

MegaGovnoCoder
Все до Pentium 3.
Вот, например, сейчас у меня тут комп с Celeron 466, SSE не поддерживает.


Отправлено MegaGovnoCoder 14-09-2015 в 16:32:

На помойку пора таким процессорам зачем из-за них отключать SSE


Отправлено XaeroX 14-09-2015 в 16:59:

MegaGovnoCoder
Можно, я сам решу, какие процессоры мне отправлять на помойку, а какие нет?
Можно? Спасибо.


Отправлено MegaGovnoCoder 14-09-2015 в 17:31:

Ладно, удачи тогда в оптимизации.

Конечно компилятор не рассчитан на оптимизацию кода для такого мусора)))

Твои же слова, что сейчас 2015 год, а компилятор не умеет оптимизировать код для процессоров ниже Pentium 3. А смысл в оптимизации для этих "отходов"?


Отправлено XaeroX 14-09-2015 в 18:09:

MegaGovnoCoder
То есть тебя ни разу не смущает тот факт, что в 1998 году компилятор умел оптимизировать под "отходы" (а на тот момент - широко распространённые процессоры), но в 2015 году внезапно "позабыл", как это делать?

Конкретно я предпочитаю FPU по следующим причинам:
1) Использование SSE подразумевает векторизацию, которую компилятор иногда делает неправильно. Я сталкивался с функциями, которые неправильно оптимизируются с включённым SSE - вплоть до того, что функции начинают работать принципиально неверно. Нет никакого желания жонглировать с версиями компиляторов и тулсетов в поисках таких трудноидентифицируемых багов.
2) Регистры FPU 80-битные, по дефолту расчёты идут двойной точности (53 бита) для 32-битных floatов, что минимизирует ошибки округления. В SSE числа с плавающей точкой занимают 32 бита даже во внутреннем представлении, что ведёт к серьёзным ошибкам. В частности, почти неизбежно приходится переписывать алгоритм на использование эпсилонов, в то время как на FPU довольно безопасно сравнивать числа напрямую (за исключением режима одинарной точности, но его нужно выставлять вручную, по дефолту он не включен).
3) Сопроцессор имеет большое число математических команд - например, вычисление тригонометрии - чего нет в наборах инструкций SSE. В последнем случае необходимые подпрограммы пишутся специально, и как правило, имеют невысокую точность. Эти подпрограммы, как и принято в SSE, заточены под векторизацию, т.е. вычисление четырёх синусов/косинусов за раз, что в условиях убогой векторизации в компиляторах совершенно бесполезно.
4) Выигрыш от использования скалярных операций SSE почти нулевой, если FPU код написан также грамотно - зато точность расчётов ниже. Кривой FPU-код, описанный в первом посте темы - либо баг компилятора, либо сознательный маркетинговый ход с целью популяризовать новые тулсеты. Как я писал выше, у GCC таких проблем нет.
5) SSE нет на мобильных процессорах, а FPU есть, поэтому для получения сравнимых и воспроизводимых результатов на всех платформах последний предпочтителен.

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

Ну и, конечно же, вопрос самоконтроля: если движок выдаёт картинку уровня, скажем, дум3, а дум3 запускается на celeron 466, то по какому праву твой движок не будет на нём запускаться?


Отправлено ~ X ~ 14-09-2015 в 18:45:

MegaGovnoCoder >>Проживает: Деревня
Богатая, видать, деревня, что процы на помойку выкидывают

Кстати, SSE не поддерживали и многие Атлоны, у которых зато был 3DNOW!.

XaeroX полностью согласен, что векторизованный расширения типа sse надо использовать вручную.

__________________
Минутка полезного:
Бесплатный UT-подобный Half-Life mod.
Бесплатный редактор для 32-битных текстур. Без дотнета.
Бесплатный IDE для любых компиляторов и ЯП.
Бесплатная Windows-подобная ОС.
Проверка грамматики русского языка.
Чат по hl[fx]: half-life@conference.jabber.ru


Отправлено Дядя Миша 15-06-2016 в 17:33:

Вот вам еще в копилку новостей: http://www.yaplakal.com/forum1/topic1396002.html

Для тех кто удивляется, почему я не спешу уходить с шестой студии и WinXP.

Добавлено 15-06-2016 в 20:30:

Цитата:
~ X ~ писал:
Я, конечно, не стану Дядей Мишей и на ВС6 не пересяду после такого

А после вышеприведённого?

Добавлено 15-06-2016 в 20:33:

ЗЫ. SSE для игровых движков практически бесполезен. Куда лучше перенести расчёты на GPU. Ну может быть в физике есть определенный прирост, если модель симуляции достаточно сложная. Или скажем в кодировании DXT - тут да, спорить трудно.

__________________
My Projects: download page

F.A.Q по XashNT
Блог разработчика в телеграме

Цитата:

C:\DOCUME~1\C4C5~1\LOCALS~1\Temp\a33328if(72) : see declaration of 'size_t'


Отправлено nemyax 15-06-2016 в 18:10:

На MinGW надо валить.


Отправлено thambs 15-06-2016 в 20:48:

>не спешу уходить с шестой студии и WinXP.
как таймбомба в железках сработает, так и поспешишь.

__________________
http://www.moddb.com/mods/monorail-quest


Отправлено Crystallize 16-06-2016 в 02:59:

Цитата:
thambs писал:
как таймбомба в железках сработает, так и поспешишь.


Ух ты, а как это? Win.CIH с периодом вызревания в 10 лет?


Отправлено XaeroX 16-06-2016 в 05:17:

Цитата:
Дядя Миша писал:
Вот вам еще в копилку новостей: http://www.yaplakal.com/forum1/topic1396002.html

А не враньё ли? Ни ссылок авторитетных, ни даже версии Студии не указано.
Цитата:
Дядя Миша писал:
А после вышеприведённого?

Никогда. Да я на своей десятке уже выгляжу как любитель мамонтов. Не думаю, что кто-то сидит на такой древней студии. Но пока с обновлением не спешу.
Цитата:
Дядя Миша писал:
Куда лучше перенести расчёты на GPU.

Сильно зависит от расчётов. Простой пример: у SSE есть прямой доступ к памяти, у GPU - нет, и надо копировать данные туда/сюда.
Цитата:
nemyax писал:
На MinGW надо валить.

MinGW надо валить.


Отправлено nemyax 16-06-2016 в 07:17:

Цитата:
XaeroX писал:
А не враньё ли? Ни ссылок авторитетных, ни даже версии Студии не указано.

Студия 2015. https://www.infoq.com/news/2016/06/visual-cpp-telemetry
Слово "вредоносный" в заголовке — обычный кликбейт.
Вот интересно, можно ли описанным способом отключить это говно в community edition.


Отправлено XaeroX 16-06-2016 в 07:21:

nemyax

Цитата:
VS2012 and VS2013 do not include this telemetry behavior.

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


Отправлено FreeSlave 16-06-2016 в 08:23:

Про телеметрию ещё в апреле писали на хабре.
Непонятно, почему об этом снова вспомнили.


Отправлено Дядя Миша 16-06-2016 в 14:06:

Цитата:
thambs писал:
как таймбомба в железках сработает, так и поспешишь.

Железка сдохнет раньше, чем в ей таймбомба сработает.
Цитата:
XaeroX писал:
Да я на своей десятке уже выгляжу как любитель мамонтов

А я на своей шестерке как тогда выгляжу? Как любитель папонтов?
Цитата:
XaeroX писал:
Сильно зависит от расчётов.

Ну я же говорю, единственный пример, что мне приходит на ум - это физика. И то хрен его знает, насколько это будет стабильно с SSE-шной точностью.
Цитата:
XaeroX писал:
словно всем и вправду пора откатываться на шестую студию

Истинно говорю вам: так и есть

__________________
My Projects: download page

F.A.Q по XashNT
Блог разработчика в телеграме

Цитата:

C:\DOCUME~1\C4C5~1\LOCALS~1\Temp\a33328if(72) : see declaration of 'size_t'


Отправлено XaeroX 16-06-2016 в 14:45:

Цитата:
Дядя Миша писал:
А я на своей шестерке как тогда выгляжу? Как любитель папонтов?

Я, пожалуй, промолчу.
Цитата:
Дядя Миша писал:
И то хрен его знает, насколько это будет стабильно с SSE-шной точностью.

Судя по Newton - вполне стабильно.
А вот CUDA-версию тамошнего солвера я так и не заценил. Не скомпилился он у меня, не помню уже почему.


Отправлено Дядя Миша 16-06-2016 в 17:57:

Цитата:
XaeroX писал:
Судя по Newton - вполне стабильно.

Это да, но я не углублялся, что именно там перевели на SSE.

__________________
My Projects: download page

F.A.Q по XashNT
Блог разработчика в телеграме

Цитата:

C:\DOCUME~1\C4C5~1\LOCALS~1\Temp\a33328if(72) : see declaration of 'size_t'


Временная зона GMT. Текущее время 12:31.
Показать все 43 сообщений этой темы на одной странице

На основе vBulletin версии 2.3.0
Авторское право © Jelsoft Enterprises Limited 2000 - 2002.
Дизайн и программирование: Crystice Softworks © 2005 - 2024