Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Посчитать строки в текстовике


Автор: Витаминка 27.12.2006, 06:07
Привет! С праздником вас! smile  Как можно посчитать кол-во строк в txt файле, если я использую стринговый метод?

Автор: aktuba 27.12.2006, 06:16
Код

function GetStringsCount: Integer;
var
  lStr: TStringList;
begin
  lStr := TStringList.Create;
  try
    lStr.LoadFromFile(FileName);
    Result := lStr.Count;
  finally
    lStr.Free;
  end;
end;

Автор: Витаминка 27.12.2006, 07:14
aktuba спасибки  smile 

Автор: ivan219 27.12.2006, 14:25
А что будет работать быстрее этот код или тот что выше smile 
Код

function f:Integer;
var
    TF: TextFile;
    S: String;
begin
 AssignFile(TF,'file.txt');
 Reset(TF);
 try
  while not Eof(TF) do
   begin
    Readln(TF,S);
    Inc(I);
   end;
 finally
  CloseFile(TF);
 end;
 Result:=I;
end;

Автор: Matematik 27.12.2006, 19:14
ivan219, зависит от размера файла. Твой вариант вобщем-то "лучше", т.к. первый вариант (stringlist) загружает весь файл в память, там парсит на отдельные строки. 
Для небольших файлов и если не надо считать много файлов оба алгоритма "одинаковые".

Автор: W4FhLF 27.12.2006, 19:52
Хотите скорость?smile Тада смотрите:

Код


Function GetStringCount(Path: pChar): dword;
var
hFile, hMap, fSize: dword;
pMemory: pointer;
begin
  hFile := CreateFile(Path, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
  fSize := GetFileSize(hFile,0);
  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  pMemory := MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
  asm
    push eax
    push ecx
    push ebx
    mov eax,pMemory
    mov ecx,fSize
    dec ecx
    xor ebx,ebx
    @loop:
      cmp byte ptr[eax+ecx],$D
      jne @f
        inc ebx
      @f:
      dec ecx
      jne @loop
    inc ebx
    mov Result,ebx
    pop ebx
    pop ecx
    pop eax
  end;
  UnmapViewOfFile(pMemory);
  CloseHandle(hMap);
  CloseHandle(hFile);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  GetStringCount('E:\Temp\english.txt');
end;



Кол-во строк в файле размером 33 мегабайта считается абсолютно моментально, т.е. даже незаметно, что происходит. Однако, если нужно работать с большими файлами(болше 80 метров), то лучше читать файл участками по 64 Кб и считать в каждом кол-во переносов, иначе для файловых проекций, который я использовал в примере, ваш своп раздует до ужасных размеровsmile

Автор: ivan219 30.12.2006, 19:31
Да метод предложенный W4FhLF самый быстрый из 3 выше перечисленных.

Я поэксперементировал так фаил в котором 100000000 строк и размером 300000000 Байт.
Фаил такого типа:
Код

.
.
.
.
.

получается  100000000 точек

Время выполнения подщёта всего 1 Секунда smile на процесоре 2гГц и памяти 1Гб

Все остальные просто виснут smile 

Автор: W4FhLF 31.12.2006, 07:36
Ассемблер всегда нужно использоват в подобных задачах. Я думаю мой вариант не самый удачный, ибо частые кеш-промахи из-за прыжка будут постоянно перегружать конвейер процессора. 

Автор: Girder 1.1.2007, 13:44
Цитата(ivan219 @  30.12.2006,  19:31 Найти цитируемый пост)
Все остальные просто виснут
Да что-ты?

Ловкость рук и ни какого ассемблера("на прямую"):
Код

Function GetStringCount(Path: pChar): dword;
var
hFile, hMap, fSize: dword;
pMemory,i: DWord;
begin
  hFile := CreateFile(Path, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
  fSize := GetFileSize(hFile,0);
  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  pMemory := DWord(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0));
 { asm
    push eax
    push ecx
    push ebx
    mov eax,pMemory
    mov ecx,fSize
    dec ecx
    xor ebx,ebx
    @loop:
      cmp byte ptr[eax+ecx],$D
      jne @f
        inc ebx
      @f:
      dec ecx
      jne @loop
    inc ebx
    mov Result,ebx
    pop ebx
    pop ecx
    pop eax
  end; }
  Result:=0;
  for i:=0 to fSize do
   if pByte(pMemory+i)^=$0a then inc(Result);
  UnmapViewOfFile(Pointer(pMemory));
  CloseHandle(hMap);
  CloseHandle(hFile);
end;


PS: Да и результат более точен smile

Автор: TwisT_X 1.1.2007, 19:35
А что если вот так вот без всяких там функций? 

Код

var MyText: Text;
      s1, Path: string;
      k: integer;

begin
     k:= 0;
     path:= 'c:\file.txt';
     assign(MyText, path);
     reset(MyText);
while not eof(Mytext) do
begin
        readln(MyText, s1);
        inc(k);
        writeln('# stroki ', k , s1);
end;



Автор: W4FhLF 1.1.2007, 20:00
Girder, 

Цитата

PS: Да и результат более точен


Ну убери inc ebx в конце, я на автомате поставил, суть-то не в этом.

Цитата

Ловкость рук и ни какого ассемблера("на прямую"):


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


Автор: Girder 1.1.2007, 21:03
Цитата(W4FhLF @  1.1.2007,  20:00 Найти цитируемый пост)
В данном случае компилятор проиграл.
Да что-ты? smile

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

Автор: W4FhLF 2.1.2007, 09:06
Цитата

Да что-ты? 


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

Автор: Girder 2.1.2007, 11:39
Цитата(W4FhLF @  2.1.2007,  09:06 Найти цитируемый пост)
Да я ничего. Я просто открыл дизасм и посмотрел на тело цикла. А ты что? Проигрыш там незначительный, порядка 5 тактов на иттерацию, но ведь это проигрыш, а я фанат ассемблера и даже не учитывая современную архитектуру и суперскалярность процессора, где эти 5 тактов могут быть незаметны, привык выжимать из алгоритма всё.
А то... что! Приведенный тобой алгоритм хотя бы потому не рационален, потому что постоянно заставляет переключать разрядность команды(от компилятора таже вещь присутсвует, но тем не менее он более "оптимальным" получился)!

PS: И кстати... 0A - перевод строки; 0D- возврат каретки.

Ах... да! чуть не забыл... мало преимущество, а так smile 
Код

Function GetStringCount4(Path: pChar): dword;
var
hFile, hMap, fSize: dword;
pMemory: DWord;
i,j,n:DWord;
begin
  hFile := CreateFile(Path, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
  fSize := GetFileSize(hFile,0);
  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  pMemory := DWord(MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0));
 { asm
    push eax
    push ecx
    push ebx
    mov eax,pMemory
    mov ecx,fSize
    dec ecx
    xor ebx,ebx
    @loop:
      cmp byte ptr[eax+ecx],$D
      jne @f
        inc ebx
      @f:
      dec ecx
      jne @loop
    inc ebx
    mov Result,ebx
    pop ebx
    pop ecx
    pop eax
  end; }
  Result:=0;
  j:=pMemory;
  for i:=0 to (fSize div 4) do
   begin
    n:=PDWord(j)^;
    if (n and $ff)=$0a then inc(Result);
    if (n and $ff00)=$0a00 then inc(Result);
    if (n and $ff0000)=$0a0000 then inc(Result);
    if (n and $ff000000)=$0a000000 then inc(Result);
    inc(PDWord(j));
   end;
  UnmapViewOfFile(pointer(pMemory));
  CloseHandle(hMap);
  CloseHandle(hFile);
end;


PS2: И не спрашивай... почему такой длинный код быстрее(на современных компах) - думай...! smile

Добавлено @ 11:42 
На смешанном файле размером 343912250, выигрыш на AMD64(3200+) порядка 20%  smile 

Автор: W4FhLF 2.1.2007, 13:09
Цитата

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


Ну только не надо говорить о рациональности по сравнению с твоим. Посмотри в дизассемблере, на одну иттерацию в твоём случае приходится два обращения к памяти и в общем случае используется 5 регистров, в моём случае обращение к памяти одно и используется 3 регистра. Поэтому в плане рациональности по сравнению с if pByte(pMemory+i)^=$0a then inc(Result); алгоритм на ассемблере удачнее. Хотя должен признать, что на практике, если не учитывать всех прочих факторов и измерить скорость с помощью GetTickCount, хотя это очень не тоно, но работают они одинаково быстро, твой алгоритм даже на 10% быстрее, но по бенчмаркам-то он проигрывает. smile  Ещё раз убеждаюсь в том, что архитектура современных процессоров настолько сложна, что невозможно прогнозировать скорость алгоритмов с такой точностью. Ну и кстати я не утверждал, что мой алгоритм не поддаётся дальнейшей оптимизации, наоборотsmile 
Небольшой проигрышь в скорости в данном случае, скорее всего, объясняется тем, что оба цикла имеют размер меньший 32-64 байта, а значит могут полностью поместиться в одной линейке кеш-памяти, доступ к которой, по скорости, на порядки выше, чем доступ к оперативной памяти. Далее, ты идёшь от начала участка к концу, но как известно при запросе однойго байта из ОЗУ берётся кол-во байт равное длине линейки кеша(в зависимости от процессора 32 или 64 байта), значит следующие 32-64 иттерации процессор оперативную память не трогает. Я же иду от конца к началу, вероятнее всего процессор не может предвидеть такие ситуации и гораздо чаще обращается к ОЗУ. 

Что ты подразумеваешь под разрядностью команды? У команды нет такого понятия "разрядность", есть размер смещения и размер непосредственного операнда и как это "переключать разрядность команды" я тоже не понимаю. Выразись точнее.

Поэтому признаю, что алгоритм мой более медленный, эх... smile

Цитата

PS: И кстати... 0A - перевод строки; 0D- возврат каретки.


Обижаешь! Я думал для программистов под win32 стало уже стандартом использовать эти два служебных символа вместе.

Цитата

Ах... да! чуть не забыл... мало преимущество, а так
...
PS2: И не спрашивай... почему такой длинный код быстрее(на современных компах) - думай...! 


А чего тут думать? Во-первых, если в файле кол-во строк, чья длина < 3 достаточно, то в одной иттерации можно определить сразу два(ну если без возврата каретки, то три) переноса. Во-вторых, считывается сразу машинное слово в регистр, на наших процессорах работа с DWORD'ами естественно происходит быстрее, чем с байтами и младшими частями регистров. В-третьих, сравнение происходит уже со значением регистра, а не со значением в памяти/кеше. Ну и конечно гораздо меньше кеш-промахов.

За этот алгоритм твёрдая 5+ и респектsmile



Автор: Girder 2.1.2007, 13:38
Цитата(W4FhLF @  2.1.2007,  13:09 Найти цитируемый пост)
Что ты подразумеваешь под разрядностью команды? У команды нет такого понятия "разрядность", есть размер смещения и размер непосредственного операнда и как это "переключать разрядность команды" я тоже не понимаю. Выразись точнее.

см.: mov al,xx и mov ax,xxxx или mov eax,xxxxxxxx.
Постоянная их смена заставляет проц также тратить время на их переключения - называеться сие чудо: Время переключение задачи команды... Т.е. постоянная чередование заставляет проц также тратить время на переключение или... если мне не изменяет память, считывание все равно происходит полное, а потом отсекаеться лишнее(но как там точно - не помню).

Цитата(W4FhLF @  2.1.2007,  13:09 Найти цитируемый пост)
А чего тут думать? Во-первых, если в файле кол-во строк, чья длина < 3 достаточно, то в одной иттерации можно определить сразу два(ну если без возврата каретки, то три) переноса. Во-вторых, считывается сразу машинное слово в регистр, на наших процессорах работа с DWORD'ами естественно происходит быстрее, чем с байтами и младшими частями регистров. В-третьих, сравнение происходит уже со значением регистра, а не со значением в памяти/кеше. Ну и конечно гораздо меньше кеш-промахов.
Нет... основное не здесь зарыто.

PS: Но енто все не главное, я просто хотел показать то что... не язык написания кода определяет его скорость smile 

Автор: W4FhLF 2.1.2007, 15:29
Цитата

см.: mov al,xx и mov ax,xxxx или mov eax,xxxxxxxx.
Постоянная их смена заставляет проц также тратить время на их переключения - называеться сие чудо: Время переключение команды... Т.е. постоянная чередование заставляет проц также тратить время на переключение или... если мне не изменяет память, считывание все равно происходит полное, а потом отсекаеться лишнее(но как там точно - не помню).


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

Цитата

Нет... основное не здесь зарыто.


Может потому что в современных процессорах несколько АЛУ и распаралеливание некоторых расчётов позволяет исполнять их процессору одновременно? 

Цитата

PS: Но енто все не главное, я просто хотел показать то что... не язык написания кода определяет его скорость


Это лишь частный случай, в общем всё равно написание кода на чистом ассемблере,  тем более, когда это делается с умом, даёт куда более впечатляющие результатыsmile Ручная оптимизация и код на ассемблере всегда будут выигрывать компилятор.

Да, чуть не забылsmile

Код

Function GetStringCount(Path: pChar): dword;
var
hFile, hMap, fSize, a: dword;
pMemory: pointer;
begin
  hFile := CreateFile(Path, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
  fSize := GetFileSize(hFile,0);
  hMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
  pMemory := MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);

  asm
    push eax
    push ecx
    push ebx
    push edx


    mov ebx,pMemory
    mov ecx,[fSize]
    shr ecx,4
    xor edx,edx
    @loop:
      mov eax,[ebx]
      sub eax,$0a000a00
      cmp eax,$04000000
      adc edx,0
      cmp ah,4
      adc edx,0
      mov eax,[ebx+4]
      sub eax,$0a000a00
      cmp eax,$04000000
      adc edx,0
      cmp ah,4
      adc edx,0
      mov eax,[ebx+8]
      sub eax,$0a000a00
      cmp eax,$04000000
      adc edx,0
      cmp ah,4
      adc edx,0
      mov eax,[ebx+$C]
      sub eax,$0a000a00
      cmp eax,$04000000
      adc edx,0
      cmp ah,4
      adc edx,0
      add ebx,16
      dec ecx
    jnz @loop
    inc edx
    mov Result,edx

    pop edx
    pop ebx
    pop ecx
    pop eax
  end;
  UnmapViewOfFile(pMemory);
  CloseHandle(hMap);
  CloseHandle(hFile);
end;


Этот алгоритм у меня работает в 3 РАЗА быстрее самого быстрого придложенного тобою. На делфи будешь переписывать?smile
Маски, как оказалось, рулят.


Автор: W4FhLF 2.1.2007, 15:51
Да, забыл сказать, что алгоритм расчитан на то, что перенос это CrLf

Автор: Girder 2.1.2007, 15:58
На счет "задачи" енто я оговорился, естественно имелось в виду "команды"

Добавлено @ 16:02 
Цитата(W4FhLF @  2.1.2007,  15:29 Найти цитируемый пост)
На делфи будешь переписывать?
Не сейчас... после 9 числа.

PS: Уезжаю...

Автор: W4FhLF 2.1.2007, 16:05
Цитата

Не сейчас... после 9 числа.

PS: Уезжаю...


Да наверное не получится. По-крайней мере я не не знаю и ниразу не слышал о том, что на делфи есть возможность складывать числа с учётом флага переноса(команда adc).

Погуглил на тему "время переключения команды", ничё не нашёл и до этого ни в какой литературе такой характеристики у процессора не встречал, так, что не знаю о чём ты.

Автор: ivan219 2.1.2007, 16:41
Вот это да к каким баталиям привёл мой оди топик smile

Добавлено @ 16:44 
Цитата(Girder @ 1.1.2007,  13:44)
Цитата(ivan219 @  30.12.2006,  19:31 Найти цитируемый пост)
Все остальные просто виснут
Да что-ты?

Ну маленько погоричился может если подождать ещё с минуту может и былбы результат smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)