Szybki sposob na zliczanie danych w plikach

Szybki sposob na zliczanie danych w plikach
Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@furious programming nawet gdy w łańcuchu zamienię dane bez ozdobników - niepotrzebne dane Czas wykonywania Bedzie bardzo długo trwał nawet, gdy wyrzucę śmieci, Delphi i pascal nie radzi sobie z przetwarzaniem to samo przez to samo. 13 tyś zmiennych.I żadnego algorytmu mi nie polecicie, A wiesz dlaczego bo to pętla pod obciążeniem ,,,,

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12270
2
Mariusz Bruniewski napisał(a):

@furious programming nawet gdy w łańcuchu zamienię dane bez ozdobników - niepotrzebne dane Czas wykonywania Bedzie bardzo długo trwał nawet, gdy wyrzucę śmieci, […]

Te dane nie mają racji bytu, więc niepotrzebnie marnują miejsce na dysku — setki megabajtów, jeśli nie wiele gigabajtów. Cały czas mam wrażanie, że z jakiegoś powodu czerpiesz satysfakcję z zapychania dysku danymi — im więcej tym lepiej, bo lepiej brzmi, że masz pierdyliony linii w odcholerionach plików. Ty naprawdę myślisz, że im więcej tym lepiej? :D

Delphi i pascal nie radzi sobie z przetwarzaniem to samo przez to samo.

Chyba Ty. Jeśli nie umiesz napisać wydajnego algorytmu, to nie zwalaj winy na język.

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
4

No i zrobiłem. Musiałem poszukać jakiś fajnych kolekcji. Znalazłem. Pierwszy raz w życiu użyłem tej klasy hashlisty. Ogólnie kod jest gówniany, sporo konkatenuje stringi, samo zapisywanie do pliku zabiera połowę czasu. Program działa ok 4-5 sek. Odpalam go w 1 wątku na 9 letnim laptopie. Na współczesnym sprzęcie, z szybszym dyskiem i odpalając parsowanie na n wątkach zejdzie się spokojnie poniżej 1s, a troszkę kod picując (chociażby ekstrakcja kluczy robię przechodząc o(n), ale nie wiem czy warunki nie zjadają czasu bo być może Pos() RPos() byłby szybszy, oraz zapis do pliku - można by to zrobić lepiej bez łączenia stringów przez + i splity), można by jeszcze więcej wycisnąć, ale nie chce mi się za free picować kodu dla kogoś, kto nie potrafi po polsku pisać. Chciałem kodu nie wrzucać tylko filmik, ale to chyba jeden sposób na udowodnienie racji i tego, że to nie Pascal jest problemem a czynnik między monitorem a fotelem. No i porównajcie objętość tego kodu, gdzie mój już jest sporo nadmiarowy i można by go skrócić. Dodatkowo dane testowe wygenerowałem więc powtórzenia są na niskim poziomie - po kilka powtórzeń. Im więcej powtarzalnych krotek to będzie się szybciej liczyło, więc mam zestaw testowy bardzo pesymistyczny.

Serio poniższy kod jest gówniany i CR bym nie przepuścił, ale nie ma sensu na potrzeby tego wątku robić refactoru prototypu.

Kopiuj
...

type

  TMap = specialize TGHashMapLP<string, Integer>;    

...


function THolyGrail.ReadExtractKeysAndCountInMap: string;
var
  i,j: integer;
  openBracketPos, closeBracketPos, firstSpacePos, lastSpacePost : integer;
  txtf,f: TextFile;
  s: String;
  s1,s2,s3,k : String;
  values: TMap;
  e: string;
  rec: TStringArray;
begin
  values := TMap.Create;

  for i := 1 to 100 do
  begin
    AssignFile(txtf, IntToStr(i)+'.txt');
    Reset(txtf);

    while true do
    begin
       ReadLn(txtf, s) ;

       if eof(txtf) then
       begin
         break;
       end;

       firstSpacePos := -1;

       for j := 1 to 100 do
       begin
         if s[j]=' ' then
         begin
           if firstSpacePos = -1 then
           begin
             firstSpacePos := j;
           end else begin
             lastSpacePost := j;
           end;
         end else if s[j] = '[' then
         begin
           openBracketPos := j;
         end else if s[j] = ']' then
         begin
           closeBracketPos := j;
         end;
       end;

       s1 := Copy (s,1,firstSpacePos-1);
       s2 := Copy (s,openBracketPos+1,closeBracketPos-openBracketPos-1);
       s3 := Copy (s, lastSpacePost+1,Length(s)-lastSpacePost);
       k:= s1 + ' ' + s2 + ' ' +s3;

       if values.Contains(k) then
       begin
         values[k] := values[k]+1;
       end else begin
         values.Add(k,1);
       end;
    end;
    Close(txtf);
  end;

  j :=  values.Count;

  AssignFile(F, 'output.txt');
  Rewrite(F);

  for e in values.Keys do
  begin
    rec := e.Split (' ');
    s1 := rec[0];
    s2 := rec[1];
    s3 := rec[2];
    writeln(F, IntToStr(values.Items[e]) + ' - ' + rec[0] + ' razy wystąpiło powtórzenie w rzędzie [' + rec[1]+ '] => Powtórzenia liczb z sortowaniem pliki PLBS.txt Poz.: ' + rec[2]);
  end;

  CloseFile(F);
  values.Destroy;
end; 

screecast:
zliczanie.gif

Tak więc weź sobie ten mój kod i dalej baw się w oszukiwanie się i granie w lotto. Jeśli natomiast chcesz po swojemu to koniecznie oczyszczaj ten shitowy format i używaj porządnych map do liczenia. TStringLiata jest gówniana. Podobnie TFPGMap. No i trzeba by to przekazać w jakimś streamie do dalszego przetwarzania albo zoptymalizować zapis do pliku.

btw. przez ten moment nieoptymalny kod napisany na kolanie zliczył 1,4mln linii. W pierwszych postach twierdziłeś, że miałeś problemy już z tysiącami...

kolejna edyta - zostały zmienne do debuggu, ale ich już nie usuwam.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

https://github.com/avk959/LGenerics to, lecz problem z instalacją

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
1

A próbowałeś otworzyć w swojej solucji projektu perpetum tototlotkera, skąpilować i kliknąć dodaj do projektu ;) ?

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
2

screenshot-20210411110533.png

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@pragmaticdev Ciekawi mnie Twój kod i za wszelką cenę chce go sprawdzić. TGHashMapLPunit1.pas Error: Identifier not found "TGHashMapLP" Chce, aby kod działał nie w konsoli lecz w unicie.

Kopiuj
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, LGenerics; // nawet gdy dodam tutaj LGenerics

  type

  TMap = specialize TGHashMapLP < string, Integer>; 
Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

W żaden sposób nie mogę dodać TGHashMapLP czy to wina fpc: 3.2.0

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
0

Ja cie... Przecież ja wczoraj też nie znałem tej bibliotek i i odpaliłem exampla, który tam jest. Wystarczy dodać uses lgHashMap ....

Szalony Programista
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 227
3

Rzadki paradygmat programowania dysfunkcyjnego, mało kto się w tych czasach na niego decyduje.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@pragmaticdev dopiero teraz testuję Twój kod. Czy nie ma przekłamań.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
1

Po to było mi to :-)

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
0

Czyli rozumiem, że udało Ci się wpiąć mój kod do Twojego programu i działa tak jak oczekiwałeś?

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@pragmaticdev PROCES MYŚLENIA I WIARY.

Wyobraź sobie, że taka lina jest blisko Ziemi. Powiesz sobie przejdę po niej. A niech teraz ona będzie bardzo wysoko. Powiesz nie dam rady. Choć lina tak samo długa jak i gruba. To jest strach. Aby go przezwyciężyć niektórzy muszą mieć opaski na oczach. Być w zaślepieniu, aby otworzyć oczy, że wszystko jest możliwe!

Pierwsza tajemnica. WIARA.

=====================

Musisz mieć przekonanie w 100%, że to co chcesz uzyskać zaistnieje w realności. To może być zdrowie, stosunki partnerskie - polepszenie ich lub cokolwiek. To 100% musi być realne dla Ciebie. Wystarczy jedna chwila zwątpienia w to co wierzysz. Proces stwarzania jest bardzo osłabiony. Nie o 1% myśli np. myślisz cały czas pozytywnie a tylko jedna myśl przyszła negatywna w stosunku do 99% myśli pozytywnych. Ta jedna myśl jak myślicie ma tylko 1%. Oczywiście, że nie to ma co najmniej 50% osłabienia Twojej wiary. Taka jest prawda. Jedno zwątpienie zakłóca niesamowicie proces stwarzania, gdyż w tym momencie 1% myśli negatywnej podświadomość zaczyna przedstawiać Tobie kolejne warianty negatywności. Poprzez obrazy i wyobrażenia w podświadomości. Popatrz na poniższy obrazek i zauważ o co mi chodzi w przekazie. jaki.jpg

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
1

Jak spadniesz z liny 30cm od ziemią to co najwyżej skręcisz kostkę. Jak spadniesz z 10m możesz się zabić lub zostać kaleką. Strach w tym przypadku jest racjonalny bo skutki niepowodzenia cięższe i nieuchronne. Z wiarą u mnie na bakier. Twardo stąpam po ziemi i kieruje się determinizmem. O ile zgodzę się, że konsekwencja i systematyka są potrzebne tak powinno się wybierać osiągalne cele i racjonalne metody. Jeśli ktoś pokazałby mi człowieka, który uczy się programować i kiepsko mu idzie i chce zacząć robić programy na zamówienie, zatrudnić się czy otworzyć jakiegoś SaaS to powiem, że przedsiębiorca, zaradny. Jak widzę takiego gościa co to robi by oszukać system liczbowy - powiem wariat. Nie istnieją żadne badania potwierdzające możliwość wykrycia klucza w grach liczbowych, przez fakt entropii. Równie dobrze możesz wierzyć, że kiedyś obudzisz się i wyrosną ci skrzela. Będziesz co dziennie moczył się w wodzie i doskonalił pływanie i mimo, że nie zwątpisz ani razy to prędzej się utopisz niż zostaniesz rybą. Nie chodzi o to, że wygrana jest nierealna - istnieje pewne małe aczkolwiek skonczone prawdopodobieństwo, że trafisz, ale to przez przypadek. Za to nierealnym jest stworzenie modelu, który powtarzalnie będzie przewidywał losowania. Jeśli taki jesteś pewien to zacznij od prostszych rzeczy - bez czujników wiatru, ciśnienia, dokładnej kamery tylko mając jakiś milion prób rzutu moneta opracuj sposób na przewidzenie czy wypadnie orzeł czy reszka. Potem bierz się, za bardziej skąplikowane gry.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@furious programmingerror.JPG
Nie może nie działać — to wbudowana procedura. — @furious programming

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
1

@Mariusz Bruniewski: bo wołasz to z kodu formy pewnie i myśli że chodzi k Close z customformy. Zawołaj tam System.Close - ja nie używałem customformy i nie było konfliktu. Lub to CloseFile użyj.

Btw głupie jest walenie logiki bezpośrednio do kodu formy. Zrób jakies klasy z logiką, co implementują jakiś interface i wstrzykuje to do formy jakimś kontenerem. Chyba mormot ma jakieś DI. Wtedy unikniesz konfliktów z kodem form. No i masz mniejszy coupling i możliwość wymiany implementacji na szybszą/wolną/inna.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@pragmaticdev to teraz ja dziele się wiedzą.

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
1

@Szalony Programista

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

Nie faja

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

Okazało się, że kod przedstawiony przez @pragmaticdev nie jest szybki. Oczywiście value.add to jak TStringList.add. Postanowiłem zliczyć 5 plików po 14 mln lini. LGeneric wypadł o 1 minutę później.

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
1

Ale Ty wiesz, że zastosowałeś dziedzinę 50x większą niż pierwotnie zakładałeś na początku tego wątku? Mówiłeś o 100 plikach po 14k lini to daje 1 400 000 linii. Potem gdzieś mówiłeś o 1000 plikach po 14k linii czyli już mamy 14 000 000 linii czyli 10x więcej. Jak masz 5 plików po 14kk linii to mamy już 70 000 000 linii czyli 50x więcej... Rozumiem, że potargałem Twoje ego, tym, że sądzę, że wymyślasz sobie rzeczywistość i Twoje posty to w połowie po prostu brednie, które nie są pisane po polsku... ale co byś nie zrobił, zwiększając dziedzinę testową, to czas wykonania algorytmu też będzie wzrastał...

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
1

Natomiast jak coś działa Tobie wolno to wrzuć tutaj kod metody i proszę porównaj jego działania na tej samej co ja dziedzinie - czyli 100 plików po 14k linii. Inaczej nie jest to miarodajne, bo wzrost czasu nie jest tutaj liniowy.

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
0

Nie umiesz używać komentarzy... Twój problem, który sam zdefiniowałeś to liczenie 100 plików z 14k liniami w czasie poniżej 1h. Ja zrobiłem algorytm co liczy to w kilka sekund ;p Chyba założenia są spełnione XD Pytanie też ile czasu liczyło się to Twoje 70mln wierszy bo teraz to tylko ściemniasz historyjki jakieś. Podaj dokładny czas. Ten kwestia, że to jakieś 7GB danych... czyli na przeciętnym SSD jakaś 1 minuta to będzie wczytywanie danych do pamięci, a też pytanie ile te kolekcje spuchną i ile masz ramu na komputerze testowym... Na pewno nie zrobisz algorytmu, którego dziedzina testowa będzie dążyć do nieskończoności, a czas wykonania będzie w granicy kilku sekund...

szatkus
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 227
2

Od lat nie pisałem w Delphi, ale postanowiłem, że sobie zrobię. Szybko okazało, że czytanie plików tekstowych w FPC jest bardzo wolne. I nie ma to za bardzo uzasadnienia, bo nawet klepnięty na szybko skrypt w Pythonie iterował szybciej. Ostatecznie zaimplementowałem to na Streamach. Przy okazji odpaliły mi się znajome neurony, więc możliwe, że już to kiedyś robiłem. Tak że, ten, wygenerowałem 5 plików po 14 milionów linii w podanym wyżej formacie, gdzie pierwsza wartość to losowa liczba z zakresu 1..6, a druga to 1..50000. Odpaliłem to 6-rdzeniowym procesorze, więc żaden wątek nie był przygłodzony i program zliczył wszystko w 4 sekundy. Czyli każdy wątek przerobił ok. 360MB danych w sekundę.

Kopiuj
program LottoDestroyer;
{$mode objfpc}

uses
  Classes, SysUtils;
 
const
  MAX_ROW = 6;
  MAX_POSITION = 50000;
  FILE_COUNT = 5;
  CHUNK_SIZE = 1024 * 1024;

type
  TFileCruncher = class(TThread)
    constructor Create(Filename: String);
    procedure Execute; override;
  public
    Counts: Array[1..MAX_POSITION, 1..MAX_ROW] of Integer;
  private
    Filename: String;
  end;
 
constructor TFileCruncher.Create(Filename: String);
begin
  inherited Create(True);
  self.Filename := Filename;
end;

procedure TFileCruncher.Execute;
var 
  Handler: TFileStream;
  Row, Position: Integer;
  Buffer: Array[1..CHUNK_SIZE] of Char;
  Bytes: LongInt;
  Digits: Array[1..8] of Char;
  InNumber: Boolean;
  Counter: Integer;
  i: Integer;
begin
  Handler := TFileStream.Create(Self.FileName, fmOpenRead);
  repeat
    Bytes := Handler.Read(Buffer, Length(Buffer));
    for i := 1 to Bytes do
    begin

      if Buffer[i] = ']' then
      begin
        InNumber := False;
        Val(Copy(Digits, 1, Counter-1), Row);
      end;

      if Buffer[i] = #10 then
      begin
        InNumber := False;
        Val(Copy(Digits, 1, Counter-1), Position);
        Inc(Self.Counts[Position][Row]);
      end;

      if InNumber and (Ord(Buffer[i]) >= 48) and (Ord(Buffer[i]) <= 57) then
      begin
        Digits[Counter] := Buffer[i];
        Inc(Counter);
      end;

      if Buffer[i] = '[' then
      begin
        InNumber := True;
        Counter := 1;
      end;

      if Buffer[i] = ':' then
      begin
        InNumber := True;
        Counter := 1;
      end;
      
    end;
    
  until Bytes < Length(Buffer)
end;

var
  Threads: Array[1..FILE_COUNT] of TFileCruncher;
  Start: Double;
  i, j, k: Integer;
  Counts: Array[1..MAX_POSITION, 1..MAX_ROW] of Integer;
begin
  Start := Now;

  for i := 1 to FILE_COUNT do
  begin
    Threads[i] := TFileCruncher.Create(IntToStr(i) + '.txt');
    Threads[i].Start;
  end;

  for i := 1 to FILE_COUNT do Threads[i].waitFor;

  for i := 1 to FILE_COUNT do
  begin
    for j := 1 to MAX_POSITION do
    begin
      for k := 1 to MAX_ROW do
      begin
        Counts[j][k] := Counts[j][k] + Threads[i].Counts[j][k];
      end;
    end;
  end;

  Writeln(Counts[6][4]);
  Writeln(Format('%7.2f', [(Now - Start) * 100000]));
end.

Poza tym kisnę z tego deklarowania zmiennych przed kodem :D

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12270
1
szatkus napisał(a):

Od lat nie pisałem w Delphi, ale postanowiłem, że sobie zrobię. Szybko okazało, że czytanie plików tekstowych w FPC jest bardzo wolne. I nie ma to za bardzo uzasadnienia […]

IMO jest — ReadLn czyta bajt po bajcie, co w żadnym wypadku nie jest wydajne. ;)

Poza tym kisnę z tego deklarowania zmiennych przed kodem :D

A widzisz, w Delphi można deklarować zmienne jak w C (czyli wszędzie), a we Free Pascalu nie.

I był czas, że pytałem deweloperów kompilatora, czy są plany dotyczące dodania wsparcia dla deklaracji stałych i zmiennych wewnątrz bloków kodu (w końcu FPC jest silnie kompatybilny z Delphi), ale zaczęło się jęczenie użytkowników, że „to takie niepascalowe”, że to „zniszczy czytelność kodu” i tego typu banialuki. Ostatecznie nazwałem ich zamrożonymi w przeszłości „dinozaurami hamującymi rozwój”, ci się obrazili, a wsparcia zmiennych jak nie było tak nie ma i nie zanosi się na to, że kiedykolwiek będzie (dla mnie to bardzo wielka szkoda).

Chociaż tyle dobrze, że trwają prace nad dodaniem wsparcia metod anonimowych. :D

PR
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 204
0

Wydaje mi się że żadna ilość wydajnych implementacji nie przekona autora, że to się da zrobić. Btw - gdzie Twój test z 8GB? Ile to czasu liczy ;) ?

WL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1084
1
szatkus napisał(a):

Poza tym kisnę z tego deklarowania zmiennych przed kodem :D

To Ty się na Delphi nie znasz, bo można np. tak:

Kopiuj
for var i := 1 to Bytes do

Także ten, kisnę z Twojego kiśnięcia, cokolwiek to znaczy ;-)

Mariusz Bruniewski
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Świecie
0

@wloochacz: sprawdzę i dam znać.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.