Форум: "Начинающим";
Текущий архив: 2012.02.12;
Скачать: [xml.tar.bz2];
ВнизСамый быстрый способ считать маленький файл в строку Найти похожие ветки
← →
Nucer (2011-10-13 15:59) [0]Подскажите самый быстрый способ считать файл в RawByteString. Необходимо обрабатывать около 50 тысяч файлов размеров в 100-200 килобайт.
При таком варианте только на чтение без обработки уходит минут 5:var
S: RawByteString;
F: File;
Size: Int64;
begin
AssignFile(F, Filename);
Reset(F, 1);
Size := FileSize(F);
Reset(F, Size);
SetLength(S, Size);
BlockRead(F, S[1], 1);
CloseFile(F);
end;
← →
Плохиш © (2011-10-13 16:02) [1]TFileStream
← →
Омлет © (2011-10-13 16:07) [2]> Плохиш © (13.10.11 16:02) [1]
> TFileStream
Ага, как будто он вызывает какую-то другую функцию для чтения файла (ReadFile).
← →
Омлет © (2011-10-13 16:11) [3]> Nucer (13.10.11 15:59)
Ты выделяешь память под каждый файл, а потом освобождаешь. А можно выделить один раз большой буфер и с ним работать. И не завязывайся на строки (что такое RawByteString?), работай напрямую с памятью через указатели.
← →
Омлет © (2011-10-13 16:15) [4]И не надо вызывать FileSize, почитай справку по BlockRead.
← →
Nucer (2011-10-13 16:19) [5]Выделение памяти на фоне чтения файла выполняется мгновенно. Если убрать строку и читать из файла только один байт, то времени на чтение всех файлов уходит примерно столько же (по байту с каждого).
С TFileStream быстрее не стало. На один файл уходит около 30 ms. 50 тысяч файлов - около 25 минут. Ужасно долго. Программно никак не ускорить процесс?
← →
stas © (2011-10-13 16:25) [6]Nucer (13.10.11 16:19) [5]
А если отключить антивирус?
← →
Ega23 © (2011-10-13 16:27) [7]
> 50 тысяч файлов - около 25 минут.
А щито вы хотели? Дисковая операция.
> Программно никак не ускорить процесс?
Ну если более одного ядра, то можно попробовать распараллелить потоки.
← →
Nucer (2011-10-13 16:28) [8]> А если отключить антивирус?
Отключил первым делом :)
← →
Омлет © (2011-10-13 16:30) [9]Какая файловая система? Какой носитель?
← →
Ega23 © (2011-10-13 16:30) [10]Потом что-то ещё такое было про количество файлов в директории. Мол, после какого-то количества (1000, например) сканирование начинает ощутимо подтормаживать.
Впрочем, с этим к Розычу.
← →
Nucer (2011-10-13 16:32) [11]> Какая файловая система? Какой носитель?
NTFS, HDD (SATA 5400)
> Ega23 © (13.10.11 16:30) [10]
Попробую раскидать по папкам, вдруг поможет.
← →
Омлет © (2011-10-13 16:33) [12]Сильно ли разбросаны файлы по диску?
← →
Ega23 © (2011-10-13 16:35) [13]Ещё один вариант: не делать 50.000 маленьких файлов, а сделать один большой из 50.000 частей.
← →
sniknik © (2011-10-13 16:37) [14]> около 50 тысяч файлов размеров в 100-200 килобайт.
а сколько времени занимает например копирование их системой? запись конечно дольше но порядок цифр будет показателен...
один большой файл копируется быстрее чем много много мелких. даже при том что размер большого в много раз больше суммы мелких.
← →
Anatoly Podgoretsky © (2011-10-13 16:39) [15]> Nucer (13.10.2011 16:19:05) [5]
30 миллисекунд это скорость равна 30-60 мб/сек
Вроде бы это нормально для диска.
← →
Dennis I. Komarov © (2011-10-13 17:51) [16]50000 файлов вдруг оказываются? Может поменять логику и обрабатывать их по мере появления (ReadDirectoryChangesW), или вообще исключить файловый обмен в пользу TCP?
← →
RWolf © (2011-10-13 17:53) [17]вариант: не подгружать файл в буфер вообще.
вместо этого получать отображение файла на память (CreateFileMapping и т. д.).
← →
Inovet © (2011-10-13 18:02) [18]> [10] Ega23 © (13.10.11 16:30)
> Мол, после какого-то количества (1000, например) сканирование
> начинает ощутимо подтормаживать.
Это на ФАТ, там поиск не индексированный.
← →
Inovet © (2011-10-13 18:03) [19]> [5] Nucer (13.10.11 16:19)
> На один файл уходит около 30 ms. 50 тысяч файлов - около 25 минут. Ужасно долго.
Зажрались однако.
← →
Игорь Шевченко © (2011-10-13 20:11) [20]Смотря зачем читать
← →
Slym © (2011-10-14 07:24) [21]4,3 секунды 50тыс открытие и "чтение" 1 файла в 356мб
procedure TForm1.Button2Click(Sender: TObject);
const FileName="D:\_films\Bones.s03e01.HDTVRip.NovaFilm.TV.avi";//356мб
var
t:dword;
i:integer;
hFile,hMap:THandle;
hMem:pointer;
begin
t:=GetTickCount;
for i:=0 to 50000 do
begin
hFile:=CreateFile(PChar(FileName),GENERIC_READ,FILE_SHARE_READ,nil, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0);
hMap:=CreateFileMapping(hFile,nil,PAGE_READONLY,0,0,nil);
hMem:=MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);
UnmapViewOfFile(hMem);
CloseHandle(hMap);
CloseHandle(hFile);
end;
t:=GetTickCount-t;
Caption:=InttoStr(t);
end;
← →
MBo © (2011-10-14 08:53) [22]Просто вход в папку с 20тыс мелких файлов FAR или проводником (в режиме без иконок) в первый раз занимает около минуты, во второй раз несколько секунд, но тоже не мгновенно.
← →
han_malign (2011-10-14 09:06) [23]
> открытие и "чтение" 1 файла в 356мб
- не правильный тест:
1. чтения, собственно, не происходит... - надо обратиться к странице памяти, чтобы она подгрузилась(Page Fault)...
2. файл в кэше(даже после закрытия(как минимум метаданные))...
- но направление мысли правильное...
1. сбоев страниц памяти не меньше чем в случае если каждый раз буфер создавать(зависит от менеджера памяти), зато нет дополнительного заполнения нулями(см. VirtualAlloc)...
2. (IMHO) поток не переходит в альтернативное состояние(на ReadFile) и соответственно есть шанс не потерять лишний квант(из тех 2-х, которые 30мс)...
З.Ы. еще можно попробовать FILE_FLAG_SEQUENTIAL_SCAN(без File Mapping), но по этому поводу у меня некоторые сомнения(поможет ли чем-то удвоенный буфер предвыборки если читаешь все?)...
← →
han_malign (2011-10-14 09:15) [24]
> Просто вход в папку с 20тыс мелких файлов FAR или проводником (в режиме без иконок) в первый раз занимает около минуты
- добавление 20тыс элементов в TListView(не виртуальный) занимает минут пять(без побочной работы)...
Это надо "чистый" тест делать, и отдельный поток на заполнение списка тоже никто не запрещал(если порядок обработки не имеет значения).
← →
MBo © (2011-10-14 09:26) [25]>обавление 20тыс элементов в TListView
В Far примерно то же самое.
И я сталкивался с тем, что FindFirst/Next безо всяких гуёв по подобным папкам выполняется в первый раз ощутимое время
← →
han_malign (2011-10-14 10:05) [26]
> И я сталкивался с тем, что FindFirst/Next безо всяких гуёв по подобным папкам выполняется в первый раз ощутимое время
- проверка прав - скорее всего...
Можно попробовать поиграться с владельцем, наследованием и т.д., но правильнее всего разбивать такую лапшу на дерево подпапок(при грамотном именовании можно делать(загрубленно) и фильтрацию, и сортировку)...
З.Ы. Сам не проверял, но некоторые клиенты утверждают, что после 64К наступает полная опа...
← →
QAZ (2011-10-14 10:47) [27]
> Nucer (13.10.11 16:19) [5]
ктож блин, читает файлы по байту?
минимальная единица чтения должна быть равна размеру кластера
а при твоих размерах нужно читать весь файл зараз
← →
han_malign (2011-10-14 11:03) [28]
> ктож блин, читает файлы по байту?
- кури мануал...
procedure Reset(var F: file; RecSize: Integer);
← →
QAZ (2011-10-14 11:27) [29]
> han_malign (14.10.11 11:03) [28]
данунафиг я такими файлофункциями в жизни не пользовался и не собираюсь
← →
Slym © (2011-10-14 11:28) [30]первый запуск 29700:10215 т.е. 30 сек по всему диску d:\ по маске pas+dcu найдено 10215 файлов
второй запуск 9900:10215
чтение побайтовое :) REPNE SCASBfunction LCharPos(Str:PChar;char:char;Size:integer): Integer;
asm {Str - EAX, char - DL (EDX), Size - ECX}
PUSH EDI
MOV EDI, EAX { Point EDI to str}
MOV AL,DL { AL = Search char}
MOV EDX,ECX {remember Length}
REPNE SCASB
JNE @@fail
MOV EAX,EDX { Calc offset }
SUB EAX,ECX
JMP @@fin
@@fail:
XOR EAX,EAX
@@fin:
POP EDI
end;
procedure FastOpenFile(const FileName:string);
var
i:integer;
hFile,hMap:THandle;
hMem:pointer;
begin
try
hFile:=CreateFile(PChar(FileName),GENERIC_READ,FILE_SHARE_READ,nil, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0);
if hFile=INVALID_HANDLE_VALUE then RaiseLastOSError;
hMap:=CreateFileMapping(hFile,nil,PAGE_READONLY,0,0,nil);
if hMap=0 then RaiseLastOSError;
hMem:=MapViewOfFile(hMap,FILE_MAP_READ,0,0,0);
if hMem=nil then RaiseLastOSError;
LCharPos(hMem,#1,GetFileSize(hFile,nil));
UnmapViewOfFile(hMem);
CloseHandle(hMap);
CloseHandle(hFile);
except
end;
end;
procedure TForm1.EnumFolder(dir:string);
var
f:WIN32_FIND_DATA;
hf: HFile;
flag: boolean;
begin
dir:=IncludeTrailingPathDelimiter(dir);
hf:=FindFirstFile(PChar(dir+"*.*"),f);
while FindNextFile(hf,f) do
begin
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)>0 then
begin
if (StrComp(f.cFileName,".")<>0) and (StrComp(f.cFileName,"..")<>0) then
EnumFolder(dir+f.cFileName);
end else
begin
if (pos(".pas",f.cFileName)>0) or (pos(".dcu",f.cFileName)>0) then
begin
inc(fc);
FastOpenFile(dir+f.cFileName);
end;
end;
end;
windows.FindClose(hf);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
t:dword;
begin
fc:=0;
t:=GetTickCount;
EnumFolder("d:\");
t:=GetTickCount-t;
Caption:=InttoStr(t)+":"+InttoStr(fc);
end;
← →
Inovet © (2011-10-14 12:01) [31]> [22] MBo © (14.10.11 08:53)
> Просто вход в папку с 20тыс мелких файлов FAR или проводником
> (в режиме без иконок) в первый раз занимает около минуты,
> во второй раз несколько секунд, но тоже не мгновенно.
Так он их имена и пр. грузит в свои структуры, а для открытия файла достаточно найти один в каталоге. На NTFS это быстро - каталог двоичное дерево, на FAT долго - каталог линейный.
← →
Игорь Шевченко © (2011-10-14 20:11) [32]цель чтения файлов не озвучена.
← →
Rouse_ © (2011-10-14 20:28) [33]
> REPNE SCASB
ужс, кто "такому ассемблеру" учит? Это из старых дельфийских библиотек или самописка?
← →
Игорь Шевченко © (2011-10-14 20:41) [34]Rouse_ © (14.10.11 20:28) [33]
Нормальный ассемблер
← →
Rouse_ © (2011-10-14 20:50) [35]
> Rouse_ © (14.10.11 20:28) [33]
> Нормальный ассемблер
Интеловские мануалы в плане профилирования кода с тобой не согласны, особливо в части рекомендаций по использованию исключающих префиксов опкодов первой группы (из четырех):pfxRepn = $F2;
pfxRep = $F3;
куда идут инструкции типа REPNE/REPNZ и т.д. тратящие пять тиков минимум на итерации, так и инструкции LOOP например...
Впрочем ты и сам это должен помнить...
← →
Игорь Шевченко © (2011-10-14 20:56) [36]Во-первых, на цитату из мануала нефигово приводить ссылку.
Во-вторых, открываем внутренности VCL и видим примеры нормального ассемблера.
В-третьих, помним первый принцип оптимизации - по возможности ее избегать.
← →
Rouse_ © (2011-10-14 20:57) [37]ЗЫ: кусок из реализации дизасма, где описаны все 4 группы префиксов (если я не прав - поправь, скажу спасибо):
const
// Lock and repeat prefixes:
pfxLock = $F0;
// The LOCK prefix can be prepended only to the following instructions and only to those forms
// of the instructions where the destination operand is a memory operand: ADD, ADC, AND,
// BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR,
// XADD, and XCHG
pfxRepn = $F2;
pfxRep = $F3;
// Segment override prefixes:
pfxCSSegmentOverride = $2E;
pfxSSSegmentOverride = $36;
pfxDSSegmentOverride = $3E;
pfxESSegmentOverride = $26;
pfxFSSegmentOverride = $64;
pfxGSSegmentOverride = $65;
// Operand-size override prefix
// (when used with the escape opcode 0FH, this
// is treated as a mandatory prefix for some SIMD instructions)
pfxOperandSizeOverride = $66;
pfxEscapeOpcode = $0F;
// Address-size override prefix
pfxAddressSizeOverride = $67;
← →
Anatoly Podgoretsky © (2011-10-14 21:06) [38]
> ужс, кто "такому ассемблеру" учит?
Это пример не профессионализма. Тупо сделаный код. Не читая ни умных книг, не руководство по оптимизации от Интела, не вглядываясь в генофонд.
← →
Rouse_ © (2011-10-14 21:09) [39]
> Игорь Шевченко © (14.10.11 20:56) [36]
> Во-первых, на цитату из мануала нефигово приводить ссылку.
Ну блин: http://developer.intel.com/design/pentiumii/manuals/245127.htm
Ну и до кучи: http://agner.org/optimize/
← →
Rouse_ © (2011-10-14 21:11) [40]
> Anatoly Podgoretsky © (14.10.11 21:06) [38]
> Это пример не профессионализма. Тупо сделаный код.
Собственно именно эту мысль я и попытался озвучить в первом посте :)
← →
Rouse_ © (2011-10-14 21:19) [41]
> Игорь Шевченко © (14.10.11 20:56) [36]
> Во-вторых, открываем внутренности VCL и видим примеры нормального
> ассемблера.
Начиная с 2007-ой дельфи асм реализаця части функций очень сильно похорошела за счет использования наработок сторонних разрабочиков, часть пошла от Fast Code часть от Пиета, поэтому я и спросил по поводу "старых дельфийских библиотек"
← →
Игорь Шевченко © (2011-10-14 21:20) [42]Rouse_ © (14.10.11 20:57) [37]
1. наборы SSE и VMX тебя приятно поразят разнообразием применения префиксов группы REP.
2. В Intel сидят далеко не дураки, и в новых архитектурах они оптимизируют выполнение старого набора инструкций.
"Intel microarchitecture (Nehalem) improves the performance of REP strings significantly
over previous microarchitectures in several ways:
• Startup overhead have been reduced in most cases relative to previous microarchitecture,
• Data transfer throughput are improved over previous generation
• In order for REP string to operate in “fast string“ mode, previous microarchitectures
requires address alignment. In Intel microarchitecture (Nehalem), REP
string can operate in “fast string” mode even if address is not aligned to 16 bytes"
(Intel® 64 and IA-32
Architectures
Optimization Reference Manual
Order Number: 248966-020
November 2009)
http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html
там оно правда про MOVSx
← →
Rouse_ © (2011-10-14 21:24) [43]
> Игорь Шевченко © (14.10.11 21:20) [42]
Согласен, CLMUL инструкции плана PCLMULLQLQDQ/PCLMULQDQ туда-же, но я не об этом, а о том что код не оптимизирован :)
← →
Игорь Шевченко © (2011-10-14 21:29) [44]Rouse_ © (14.10.11 21:19) [41]
> Начиная с 2007-ой дельфи асм реализаця части функций очень
> сильно похорошела
в RTL от XE2 все те же REPNE SCASB
← →
Игорь Шевченко © (2011-10-14 21:30) [45]Rouse_ © (14.10.11 21:24) [43]
Там вообще черт голову сломит с этими префиксами, учитывая что префикс REX может быть вставлен в середину команды.
← →
Rouse_ © (2011-10-14 21:38) [46]
> Игорь Шевченко © (14.10.11 21:30) [45]
> Там вообще черт голову сломит с этими префиксами, учитывая
> что префикс REX может быть вставлен в середину команды.
Хм, однако(думал ты забросил это дело отслеживать)...
Тогда, думаю будет интересно: http://www.beaengine.org/
← →
Anatoly Podgoretsky © (2011-10-14 21:44) [47]> Rouse_ (14.10.2011 21:11:40) [40]
Они думаю, что если применют аггрегатную функцию, inc edi, inc esi, dec
ecx - то мол будет быстрееЮ будет но на 486 процессоре, а не на пентиум и
наследниках. Там лучше вместо одной функции сделать несколько. Так и
компилятор построен, вместо loop - пара dec ecx, jnz label. а со scasb и еще
быстрее.
В свое время когдя я еще этого не знал, то так и поступал, а потом
попробовал заменить одну команду несколькими и был удивлен, и только потом
посмотрел документацию Интела и другие умные книги, часть статей по
оптимизации ассемблера есть на моем сайте.
← →
Anatoly Podgoretsky © (2011-10-14 21:49) [48]> Игорь Шевченко (14.10.2011 21:20:42) [42]
Наверно оптимизируют, но не только их, а и обычные тоже.
В Пентиум IV они достигли выполнения обычных команд до 5 за один так. В
следующем поколение они потеряли это.
Недостаток аггрегатных комманд в том, что они сбивают работу конвейера,
особенно REP
Сейчас я уже не читаю документацию по новым ассемблерам, за не надобностью.
← →
Rouse_ © (2011-10-14 21:57) [49]
> В свое время когдя я еще этого не знал, то так и поступал,
> а потом попробовал заменить одну команду несколькими и был удивлен,
> и только потом посмотрел документацию Интела и другие умные книги, часть статей по оптимизации ассемблера есть на моем сайте.
Есть у меня старая задачка, в свое время взламывал ей мозг Зотычу :)
Задача на знание оптимизации компилятора.
Необходимо написать функцию, которая принимает на вход параметр и выдает результат модификации оного, при этом занимает всего один байт, если посчитать размер асм инструкций, из которых она состоит.
← →
Игорь Шевченко © (2011-10-14 22:05) [50]Anatoly Podgoretsky © (14.10.11 21:49) [48]
> Наверно оптимизируют, но не только их, а и обычные тоже.
Это безусловно. С каждой новой архитектурой вносятся изменения, им же надо поддерживать всю массу унаследованного кода, они вокруг своего RISC-ядра оптимизируют микропрограмму.
> Сейчас я уже не читаю документацию по новым ассемблерам,
> за не надобностью.
Ассемблер-то старый, а вот аппаратная платформа она интересная.
> В Пентиум IV они достигли выполнения обычных команд до 5
> за один так. В
> следующем поколение они потеряли это
Сейчас я не пойму, как померить относительно Пентиума.
"The front end can decode up to 4 instructions in one cycle and
supports two hardware threads by decoding the instruction streams between two
logical processors in alternate cycles. The front end includes enhancement in branch
handling, loop detection, MSROM throughput, etc. These are discussed in subsequent
sections.
The scheduler (or reservation station) can dispatch up to six micro-ops in one cycle
through six issue ports (five issue ports are shown in Figure 2-5; store operation
involves separate ports for store address and store data but is depicted as one in the
diagram).
The out-of-order engine has many execution units that are arranged in three execution
clusters shown in Figure 2-5. It can retire four micro-ops in one cycle, same as
its predecessor."
Если учесть, что одна микрооперация может представлять собой две команды уровня Instruction set architecture или одна команда может разбита на несколько микроопераций.
Rouse_ © (14.10.11 21:38) [46]
> Тогда, думаю будет интересно: http://www.beaengine.org/
Благодарю.
← →
Игорь Шевченко © (2011-10-14 22:12) [51]Rouse_ © (14.10.11 21:57) [49]
> Необходимо написать функцию, которая принимает на вход параметр
> и выдает результат модификации оного, при этом занимает
> всего один байт, если посчитать размер асм инструкций, из
> которых она состоит.
первое, что приходит в голову - CBW/CWD
второе - INC EAX
третье - AAA/AAS/DAA/DAS
Что я упустил ? ;)
← →
Rouse_ © (2011-10-14 22:19) [52]
> Игорь Шевченко © (14.10.11 22:12) [51]
>
> Rouse_ © (14.10.11 21:57) [49]
>
>
> > Необходимо написать функцию, которая принимает на вход
> параметр
> > и выдает результат модификации оного, при этом занимает
> > всего один байт, если посчитать размер асм инструкций,
> из
> > которых она состоит.
>
>
> первое, что приходит в голову - CBW/CWD
> второе - INC EAX
> третье - AAA/AAS/DAA/DAS
>
> Что я упустил ? ;)
Минимум постфикс функции :)
← →
Rouse_ © (2011-10-14 22:20) [53]
> Rouse_ © (14.10.11 22:19) [52]
> Минимум постфикс функции :)
Но блин, это не честная подсказка :)
← →
Rouse_ © (2011-10-14 22:21) [54]ЗЫ: Игорь - решение не публикуй плз, бо не интересно будет...
← →
Игорь Шевченко © (2011-10-14 22:28) [55]
> Минимум постфикс функции :)
Понял :)
← →
Anatoly Podgoretsky © (2011-10-15 01:18) [56]> Игорь Шевченко (14.10.2011 22:05:50) [50]
Это что за железо страшное?
← →
Игорь Шевченко © (2011-10-15 10:38) [57]
> Это что за железо страшное?
corei7
← →
Slym © (2011-10-17 09:31) [58]злые Вы, чтож вы прикопались к этой REPNE SCASB
я же сам над собой посмеялся в своем посте...
ветка про чтение файла, а не про асм
← →
Slym © (2011-10-17 09:40) [59]а выпилено сие из Delphi7.SysUtils.StrLen с минимальными модификациями
← →
DevilDevil © (2011-10-20 15:56) [60]All
сложность задачи не в ассемблере, а в дисковых операциях. Это очень медленно
если говорить хранение результата - то лучше писать в TMemoryStream например <Size><Память>. Там буферизация по 8кб - удастся избежать реаллоков
Nucer
существует возможность хранить все файлы одним zip архивом ?
если да то имеет смысл читать данные из архива
← →
Nucer (2011-10-20 23:55) [61]Спасибо за ответы. Думал, что тема себя почти сразу исчерпала, поэтому сюда не заглядывал.
Поставлю точку :)
В итоге, я склеиваю все файлы в один большой (размер, файл, размер, файл, ...), а потом уже работаю с этим "архивом".
← →
Игорь Шевченко © (2011-10-21 20:17) [62]Жаль, что так и не удалось послушать начальника транспортного цеха.
Ты тратишь время на "склейку" (чтение + запись) и на последующее чтение. В итоге в скорости ты проигрываешь.
Удачи.
← →
Nucer (2011-10-22 23:38) [63]> Ты тратишь время на "склейку" (чтение + запись) и на последующее чтение.
> В итоге в скорости ты проигрываешь.
Это я прекрасно понимаю. Но в моем случае набор файлов меняется редко, а анализировать текущий набор приходится часто, поэтому целесообразно сначала один раз склеить файлы в один временный, а потом уже работать с одним временным.
← →
БезымянныйСтудент (2011-11-02 11:06) [64]Какая связь между "самый быстрый способ считать файл" и "необходимо обрабатывать около 50 тысяч файлов"?
У вас же все время уходит на поиск и и открытие файлов, а не на само чтение...
Храните всё в одном файле... Или в БД...
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2012.02.12;
Скачать: [xml.tar.bz2];
Память: 0.64 MB
Время: 0.004 c