Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.06.14;
Скачать: CL | DM;

Вниз

Pointer->Integer->Pointer   Найти похожие ветки 

 
alertus ©   (2005-05-27 17:36) [0]

Подскажите, пожалуйста, если я пишу так:

a:^real;
p:^real;
....
getmem(a,100*sizeof(real));
p:=pointer(integer(a)+0);
p^:=0.347;
p:=pointer(integer(a)+8);
p^:=0.764;
p:=pointer(integer(a)+16);
p^:=0.764;
....


Похоже ли это на работу динамического массива и какова будет разница в скорости??


 
Digitman ©   (2005-05-27 17:41) [1]


> Похоже ли это на работу динамического массива


нет, не похоже.


> какова будет разница в скорости


в связи с чем тебя озадачила "скорость" ?


 
-=XP=- ©   (2005-05-27 17:45) [2]

Похоже. Но и только.

Лучше использовать не +0, +8, а +SizeOf(pointer) * ItemIndex

А зачем так изголяться? Чем динамические массивы не подошли?
Или конструкция типа:

{$RANGECHECKS OFF}
type
 PRealArray = ^TRealArray;
 TRealArray = array[0..0] of real;
var
 A: PRealArray;
begin
 GetMem(A, 100 * SizeOf(real));
 A^[2] := 0.764;
 ...


 
alertus ©   (2005-05-27 17:47) [3]

Почему не похоже, ведь у меня получился массив из 100 элементов типа real, к каждому из которых я имею доступ.
И если я изначально не знаю размер массива, то я могу его задать в ходе выполнения программы (после мне его менятьт не потребуется).

Я столкнулся с программированием на Matlab, там перемножение двух матриц 300x700 элементов real занимает около 1/4 секунды, а у меня на Delphi с использованием динамических массивов - 4 сек.
Matlab написан на C++, там динамические массивы объявляются так:
float *a.

В общем я хочу ускорить операции с динамическими массивами, судя по Matlab"у это возможно.

Может быть кто-нибудь знает как это запрограммировано на Matlab"е??


 
Digitman ©   (2005-05-27 17:50) [4]


> Почему не похоже


по кочану.

любой дин.тип данных предусматривает упр.структуру ... она у тебя ГДЕ ?


 
alertus ©   (2005-05-27 17:50) [5]


> Лучше использовать не +0, +8, а +SizeOf(real) * ItemIndex


Да, я так и хотел сделать в дальнейшем.

А как насчет скорости или другой реализации??


 
alertus ©   (2005-05-27 17:52) [6]


> любой дин.тип данных предусматривает упр.структуру ... она
> у тебя ГДЕ ?

Меня беспокоит то, что любая управляющая структура уменьшает скорость, я специально делаю все на наиболее низком уровне, хочу попробовать на asm"е это сделать...


 
-=XP=- ©   (2005-05-27 17:54) [7]

Тут проблема не с динамическими массивами.
Скорее всего, у Вас алгоритм другой, нежели в MathLab.


 
Digitman ©   (2005-05-27 17:58) [8]


> Меня беспокоит то, что любая управляющая структура уменьшает
> скорость


при обращении по чтению - ровно на одну-две маш.инструкции.

мизер.


> специально делаю все на наиболее низком уровне


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


> хочу попробовать на asm"е это сделать


попробуй.
тем более что садомазо нынче в моде)


 
Lin7   (2005-05-27 18:11) [9]


> alertus ©   (27.05.05 17:47) [3]
> Я столкнулся с программированием на Matlab, там перемножение
> двух матриц 300x700 элементов real занимает около 1/4 секунды,
> а у меня на Delphi с использованием динамических массивов
> - 4 сек.


Может приведёшь свой код? Дело в том, что в MatLab"е интерпретатор и у меня Matlab работает медленнее, чем тот же код переписанный на Delphi, причём в разы. В новых версиях Mаtlab"а появился встроенный компилятор, но он позволяет (если не ошибаюсь) компилить matlab"овские функции в только dll.


 
alertus ©   (2005-05-27 18:12) [10]

А что насчет менеджера памяти, я так понимаю, есть такой тип TMemoryManeger, он создается вместе с приложением (TApplication).
Он управляет всей памятью, которая требуется приложению, в том числе и динамическими массивами, но он проверяет не превышены ли текущие границы динамического массива и т.п.
Это, как я понимаю и есть управляющая структура, а я хочу ее обойти или поменьше использовать.
Я совсем не уверен что это все правильно, но я ищу способы ускорить мою программу.

> Скорее всего, у Вас алгоритм другой, нежели в MathLab.

Я думаю, что для перемножения матриц другого алгоритма просто не существует в природе (возможно я не прав). Я использую такой:

var
 m1:array[1..2,1..3] of real;
 m2:array[1..3,1..2] of real;
 mr:array[1..2,1..2] of real;
 temp:real;
begin
 for i:=1 to 2 do begin
   for j:=1 to 2 do begin
     temp:=0;
     for k:=1 to 3 do begin
       temp:=temp+m1[i,k]*m2[k,j];
     end;
     mr[i,j]:=temp;
   end;
 end;
end;


Я реализовал свою небольшую прогараммку:

procedure TForm1.Button1Click(Sender: TObject);
var
 i,j,k:integer;
 temp:real;
 t1,t2:real;
 pt:^real;
 off:integer;
begin
 m1.w:=400;
 m1.h:=800;
 getmem(m1.pint,400*800*sizeof(real));
 m2.w:=800;
 m2.h:=400;
 getmem(m2.pint,800*400*sizeof(real));
 mr.w:=400;
 mr.h:=400;
 getmem(mr.pint,400*400*sizeof(real));
 for i:=1 to 400 do begin
   for j:=1 to 400 do begin
     temp:=0;
     for k:=1 to 800 do begin
       pt:=pointer(integer(m1.pint)+(i-1+(k-1)*m1.w));
       t1:=pt^;
       pt:=pointer(integer(m2.pint)+(k-1+(i-1)*m1.h));
       t2:=pt^;
       temp:=temp+t1*t2;
     end;
     pt:=pointer(integer(mr.pint)+(i-1+(j-1)*m1.h));
     pt^:=temp;
   end;
 end;
end;


Она длится 2.7 секунды, уже лучше чем 4, да и размер матриц больше, только это не объективно, потому что я на другом компьютере, хотя процессоры почти одинаковые.


 
MBo ©   (2005-05-27 18:16) [11]

Какой процессор?


 
alertus ©   (2005-05-27 18:17) [12]


> В новых версиях Mаtlab"а появился встроенный компилятор,
> но он позволяет (если не ошибаюсь) компилить matlab"овские
> функции в только dll.


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

Ну и, в конце концов, я хочу написать свою функцию на Dephi.


> Дело в том, что в MatLab"е интерпретатор и у меня Matlab
> работает медленнее, чем тот же код переписанный на Delphi,
> причём в разы.

Да, это абсолютно так для циклов и программ, но если взять отдельно функцию перемножения матриц, то ситуация резко меняется в противоположную сторону.


 
alertus ©   (2005-05-27 18:18) [13]


> Какой процессор?

Дома Athlon 1000, а на работе, где я сейчас - Athlon 800.


 
Digitman ©   (2005-05-27 18:23) [14]


> хочу написать свою функцию на Dephi


это похвально.
и перевешивает любые иные аргументы.

размер матрицы-результата тебе известен ?

думаю - да.

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


 
MBo ©   (2005-05-27 18:41) [15]

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


 
begin...end ©   (2005-05-27 18:50) [16]

> -=XP=- ©   (27.05.05 17:45) [2]

> Лучше использовать не +0, +8, а +SizeOf(pointer) * ItemIndex

А причём тут SizeOf(pointer)?


 
Verg ©   (2005-05-27 18:52) [17]


> Она длится 2.7 секунды, уже лучше чем 4,


Это логично. Она (вторая функция) - реализована неверно. Экономия времени достигнута отсутствием требуемого умножения смещения от указателя на sizeof(real). Реально вычисляются адреса ячеек, как будь-то они размером 1 (байт).
А вообще задумайся - обход столбца одной матрицы и строки другой делаются строго последовательно. Т.о. если уж так хочется оптимизировать, то надо от прямого индексирования, сопровождающегося "в лоб" вычислением адреса через i,j и умножением на sizeof(real) на каждом шаге, переходить на суммирование индексных смещений (наращивание на каждом шаге). Т.о. ты избавишься от операций умножения для вычислений физического адреса ячейки матрицы.


 
-=XP=- ©   (2005-05-27 18:55) [18]

А причём тут SizeOf(pointer)?

Типа опечатка. Там дальше написано: SizeOf(real)

:)


 
alertus ©   (2005-05-27 19:03) [19]


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

Размеры матриц изначально не известны на первой, ни второй, ни ризультата, но в ходе программы они не меняются.


> Она (вторая функция) - реализована неверно.

Да, я теперь заметил, торопился когда писал.


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

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

Всем огромное спасибо, попробую совместить все советы.


 
begin...end ©   (2005-05-27 19:06) [20]

> alertus ©   (27.05.05 19:03) [19]

> а что, если используются близкие адреса, то это будет быстрее??

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

var
 a: Pointer;
 p: ^Real;
begin
 GetMem(a, 100 * sizeof(Real));
 p := a;
 p^ := 0.347;
 Inc(p);
 p^ := 0.764;
 Inc(p);
 p^ := 0.764;
 ...
end.


 
Defunct ©   (2005-05-27 19:08) [21]

Verg ©   (27.05.05 18:52) [17]

Допускается же умножение индексного регистра при адресации массивов. пример:

 mov EAX, [EBX + ESI*8]


 
begin...end ©   (2005-05-27 19:10) [22]

> Defunct ©   (27.05.05 19:08) [21]

Ну и что? Речь-то не о возможности, а о скорости.


 
Anatoly Podgoretsky ©   (2005-05-27 19:12) [23]

p:=pointer(integer(a)+8);
Так не допустимо, размер Real не 8, а величина переменная, но даже если бы и постоянно, то это все равно выглядит некрасиво.


 
alertus ©   (2005-05-27 19:41) [24]

Да, я согласен, я думал, что sizeof(real) тормозит программу, но это как-бы константа, поэтому лучше писать
p:=pointer(integer(a)+sizeof(real));


> begin...end ©   (27.05.05 19:06) [20]

насчет наращивания адреса, похоже, очень дельный совет.

А еще такой вопрос:
если я по очереди обращаюсь к нескольким элементам памяти, зависит ли скорость от расстояния между этими физическими адресами??


 
Defunct ©   (2005-05-27 19:48) [25]

begin...end ©   (27.05.05 19:10) [22]

Ну и ничего,
прежде чем сразу спорить возьми и проверь по скорости

mov  eax, [ebx + ecx]
и
mov  eax, [ebx + ecx*8]

оно рульно будет (С) begin...end


 
begin...end ©   (2005-05-27 19:53) [26]

> Defunct ©   (27.05.05 19:48) [25]

А Вы, надо полагать, уже проверили?

type
 TElemType = Integer;
 PElemType = ^TElemType;
const
 ElemsCount = 10000000;
var
 a: Pointer;
 p: PElemType;
 T1, T2, I: Cardinal;
begin
 GetMem(a, ElemsCount * sizeof(TElemType));
 p := a;
 T1 := GetTickCount;
 for I := 0 to Pred(ElemsCount) do
   PElemType(Integer(p) + I * sizeof(TElemType))^ := 1;
 T1 := GetTickCount - T1;
 p := a;
 T2 := GetTickCount;
 for I := 0 to Pred(ElemsCount) do
 begin
   p^ := 1;
   Inc(p)
 end;
 T2 := GetTickCount - T2;
 Caption := Format("Умножение: %d; сложение: %d", [T1, T2]);
 FreeMem(a)
end.

"Чуешь Тайд?" (c) Defunct


 
Defunct ©   (2005-05-27 19:55) [27]

> begin...end
> сколько в облегчении вычисления индексов.

Вот например это вообще теряет смысл, если проц. и так умеет перемножать индексные регистры непосредственно при адресации элемента массива.

т.е. доступно даже такое

mov eax, [CONST + ebx + ecx*8]
и эта команда будет выполняться одинаково быстро с командой
mov eax, [ebx]


 
Defunct ©   (2005-05-27 19:57) [28]

begin...end ©   (27.05.05 19:53) [26]

Да я уже проверил, а тот код который вы приводите я не понимаю. Это какой-то набор значков. Не знаю что вы там в нем меряли.

function test:integer;
var
  a : array[0..100] of byte;
asm
 xor  ecx, ecx
 mov  eax, [ESP + ECX*8]
end;

function test2:integer;
var
  a : array[0..100] of byte;
asm
 xor  ecx, ecx
 mov  eax, [ESP + ECX]
end;

procedure TForm1.rClick(Sender: TObject);
var
 i : integer;
 E1, E2 : Integer;
begin

 T1 := Now;

 for i := 0 to 2000*1000*1000 do
   begin
      E1 := Test;
   end;
   E2 := E1;

 T3 := E2*T1;
 T2 := Now;

 ShowMessage("С умножением "+ FormatDateTime("nn:ss:zzz", T2 - T1));

 T1 := Now;

 for i := 0 to 2000*1000*1000 do
   begin
      E1 := Test2;
   end;

   E2 := E1;
 T2 := Now;

 ShowMessage( "Без умножения "+ FormatDateTime("nn:ss:zzz", T2 - T1));

 T2 := E2*T2;

end;


 
Anatoly Podgoretsky ©   (2005-05-27 20:12) [29]

Чего результаты то не привел, ну зачем же каждому компилировать. Кроме того тест желательно оптимизировать, что бы уменьши погрешность от цикла и команд вызова/возврата. это делается так

mov  eax, [ESP + ECX*8]
... повторения строки от 100 раз и более
mov  eax, [ESP + ECX*8]


 
Defunct ©   (2005-05-27 20:51) [30]

Anatoly Podgoretsky ©   (27.05.05 20:12) [29]

Спасибо за метод устранения ошибки.

В общем картина такая.
1. выключил оптимизацию.

2. функция test
приняла такой вид:

xor  ecx, ecx
mov  eax, [0 + ESP + ECX*8]
...
mov  eax, [99 + ESP + ECX*8]

3. Функция test2 такой:
xor  ecx, ecx
mov  eax, [ESP]
...
mov  eax, [ESP]

по сотне команд в обеих функциях. Циклы остались такими же - по 2млрд итераций.


Результаты выполнения теста на P4:

1-й запуск:
с умножением   без умножения
44.581с          44.565с

2-й запуск
с умножением   без умножения
44.518с          44.518с

3-й запуск
с умножением   без умножения
44.518с          44.518с


Выводы постоите сами ;>


 
begin...end ©   (2005-05-27 20:58) [31]

> Defunct ©   (27.05.05 19:57) [28]

> тот код который вы приводите я не понимаю

Сорри, но я ничего не могу с этим поделать.

> Это какой-то набор значков.

LOL. Любой листинг программного кода -- это набор значков.

> Не знаю что вы там в нем меряли.

Времена выполнения двух вариантов кода.

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

var
 i : integer;
 T1, T2, T3: Cardinal;
 E1, E2 : Integer;
begin

 T1 := GetTickCount;

 for i := 0 to 2000*1000*1000 do
 begin
   E1 := Test;
 end;
 E2 := E1;
 T3 := E2*T1;

 T2 := GetTickCount;

 ShowMessage("С умножением: "+ IntToStr(T2 - T1));

 T1 := GetTickCount;

 for i := 0 to 2000*1000*1000 do
 begin
   E1 := Test2;
 end;
 E2 := E1;

 T2 := GetTickCount;

 ShowMessage( "Без умножения: "+ IntToStr(T2 - T1));

 T2 := E2*T2;

end


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

Результаты проверки таковы (на Pentium 3):

С умножением  -- 30304
Без умножения -- 25947


Чуда не произошло, что мной и ожидалось.


 
begin...end ©   (2005-05-27 20:59) [32]


> времени выполнения кода функцией

времени выполнения кода функцией Now


 
alertus ©   (2005-05-27 21:02) [33]

Наваял такую программу:

function writems:string;
var
 t:TDateTime;
 h,m,s,ms:word;
begin
 t:=Time;
 DecodeTime(t, h, m, s, ms);
 result:=inttostr(s)+"."+inttostr(ms);
end;

procedure TForm1.Button5Click(Sender: TObject);
var
 a:^real;  //311x700
 b:^real;  //700x311
 c:^real;  //311x311
 i,j,k:integer;
 p:^real;
 pa,pb,pc:^real;
 temp:real;
begin
 getmem(a,311*700*sizeof(real));
 getmem(b,311*700*sizeof(real));
 getmem(c,311*311*sizeof(real));

 memo1.lines.add(writems);
 for i:=1 to 311 do begin
   for j:=1 to 311 do begin
     temp:=0;
     p:=integer(0);
     for k:=1 to 700 do begin
       pa:=pointer(integer(a)+(i-1)*311+integer(p));
       pb:=pointer(integer(b)+(j-1)*311+integer(p));
       temp:=temp+pa^*pb^;
       inc(p);
     end;
     pc:=pointer(integer(c)+(i-1+(j-1)*311)*sizeof(real));
     pc^:=temp;
   end;
 end;
 memo1.lines.add(writems);
end;


Время умножения - 1.2 с, прежний метод 3.2 с, метод с использованием a:array[0..0] - 3.0 с (до этого про 4.0 было неверно, я брал другой размер матрицы и перепутал).

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

Очень помогли советы
MBo ©   (27.05.05 18:41) [15] и
begin...end ©   (27.05.05 19:06) [20], всем огромное спасибо.


 
Defunct ©   (2005-05-27 21:05) [34]

begin...end ©   (27.05.05 20:58) [31]

Я прекрасно знаю насколько объективно, именно Вы относитесь к моим постам, но в этот раз ничего у Вас не выйдет. LOL оставьте себе, раз забыли выключить оптимизацию.

> Кстати, обратите внимание на количество хинтов компилятора
Обратил - ни одного hint"a либо warning"a.


 
Defunct ©   (2005-05-27 21:10) [35]

begin...end ©   (27.05.05 20:58) [31]

> Ваш код я видоизменил: вместо ламерского способа измерения времени выполнения кода функцией использовал GetTickCount,

Вам не стыдно такое писать?


 
begin...end ©   (2005-05-27 21:37) [36]

> Defunct ©   (27.05.05 21:05) [34]

> Я прекрасно знаю насколько объективно, именно Вы относитесь
> к моим постам

Вы своими постами заслужили такое отношение. Поэтому смело можете сказать спасибо зеркалу.

> забыли выключить оптимизацию

Не совсем понятно, каким образом опция оптимизации может повлиять на код самих asm-функций, но, раз уж Вы об этом упомянули, я повторил тест с отключённой оптимизацией.

Результаты таковы:

С умножением  -- 37974
Без умножения -- 34750


Уж и не знаю, что нужно сделать, чтобы вариант с умножением выиграл. Может, с бубном возле компьютера поплясать?

> > Кстати, обратите внимание на количество хинтов компилятора
> Обратил - ни одного hint"a либо warning"a.

Это у Вас что-то с компилятором. А может быть, Вы просто отключили показ хинтов? Приведу список хинтов, возникших на Delphi 7 (конечно, с первого раза код [28] откомпилировать не удалось -- пришлось добавить объявления перменных T1, T2 и T3):

[Hint] Unit1.pas(75): Value assigned to "T2" never used
[Hint] Unit1.pas(70): Value assigned to "E2" never used
[Hint] Unit1.pas(67): Value assigned to "E1" never used
[Hint] Unit1.pas(58): Value assigned to "T3" never used
[Hint] Unit1.pas(56): Value assigned to "E2" never used
[Hint] Unit1.pas(54): Value assigned to "E1" never used


Если выключить оптимизацию, то количество хинтов уменьшается до двух, но они есть.

> Defunct ©   (27.05.05 21:10) [35]

Не стыдно.


 
begin...end ©   (2005-05-27 22:12) [37]

Воспользовался "методом устранения ошибки" из [30]. Теперь функции содержат по 100 команд MOV: в Test -- команды вида mov eax, [I + ESP + ECX*8], где I -- целочисленные константы от 0 до 99, а в Test2 -- mov eax, [ESP + ECX].

В связи с малой производительностью процессора пришлось уменьшить число итераций до 200 миллионов, иначе я бы заснул в ожидании результатов. А результаты впечатляют:

С умножением  -- 84520
Без умножения -- 46480


"Выводы построите сами" (c) Defunct


 
Anatoly Podgoretsky ©   (2005-05-27 22:27) [38]

Вот оно влияние цикла, вызова и возврата из подпрограммы. А казалось бы безобидный с виду тест.


 
Defunct ©   (2005-05-27 23:45) [39]

Anatoly Podgoretsky ©   (27.05.05 22:27) [38]

Нет скорее это не влияние цикла вызова и возврата а особенность работы P3, для которого более сложная иструкция (читать более большая), тратит больше времени на погдотовку, потому что за результат [30] я ручаюсь со всей ответственностью.


begin...end ©   (27.05.05 21:37) [36]
Промолчу, если вы заметили, я уже стараюсь с вами не спорить.


begin...end ©   (27.05.05 22:12) [37]

Предполагаю, что на P3 тормозит не умножение индекса, а наличие постоянной константы, которое значительно раздувает размер команды. Для получение объективного результата проверьте в равных условиях команды

mov eax, [I + ESP + ECX*8]
и
mov eax, [I + ESP + ECX]

т.е. сугубо наличие умножения индекса.


 
Marser ©   (2005-05-27 23:50) [40]


> Defunct ©   (27.05.05 21:10) [35] [Новое
>сообщение][Ответить]
>begin...end ©   (27.05.05 20:58) [31]
>
> > Ваш код я видоизменил: вместо ламерского способа
> измерения времени выполнения кода функцией использовал
>GetTickCount,
>
> Вам не стыдно такое писать?

Действительно ламерство. Если б ты предложил QueryPerformanceTimer, то это дело. Но TDateTime с сопутствующими - истинная черепаха.
Твоя проблема, ИМХО, в том, что глубочайшие знания у тебя соседствуют с пробелами чуть ли не в табличке умножения.



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

Текущий архив: 2005.06.14;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.028 c
3-1115457174
Geka
2005-05-07 13:12
2005.06.14
Удаление одинаковых позиций SQL


14-1117026867
Korvet
2005-05-25 17:14
2005.06.14
Открытие окон в инете.


4-1114077323
ANB
2005-04-21 13:55
2005.06.14
Номер EM_GETSELTEXT


3-1115358479
Patrick
2005-05-06 09:47
2005.06.14
Delphi 7 и Oracle 8i


6-1111439002
Zion
2005-03-22 00:03
2005.06.14
Как узнать IP-адрес компа в локалке по его сетевому имени?