Kopiowanie danych, optymalizacja

0

Stworzyłem kod do kopiowania (właściwie jest to tylko fragment większej całości - ma za zadanie skopiować cały plik graficzny, do pliku mojego własnego formatu). Czy można zoptymalizować mój kod? Czy jest to może w miarę optymalne rozwiązanie (duże pliki kopiują sie dość długo, co zapewne jest nieuniknione ale i tak czuje że można to rozwiązać wydajniej).

var Buff : char;
...
// osadzanie pliku grafiicznego do wlasnego formatu pliku
  pozycja := 0;
  Plik_Save := TFileStream.Create('C:\zapisanoto.123', fmOpenReadWrite);
  Plik_Open := TFileStream.Create('C:\plik.bmp', fmOpenRead);
  rozmiarPliku := plik_open.Size;

    repeat
     plik_Save.Seek(pozycja, soFromBeginning);
     plik_Open.Seek(pozycja, soFromBeginning);
       plik_Open.ReadBuffer(Buff, length(Buff) );
       plik_Save.WriteBuffer(Buff, length(Buff));
       pozycja := pozycja + length(Buff);
    until pozycja = rozmiarPliku;

    Plik_Open.Free;
    Plik_Save.Free;

Pozdrawiam.

0

Oj można. I to o wiele. Pamiętaj że operacje dyskowe IO należą do najwolniejszych, tak więc trzeba minimalizować ich liczbę. Ponadto wszystko co zbędne trzeba wytrzasnąć poza pętlę. Twoje błędy:
1). Ustawianie pozycji pliku podczas odczytu - nie trzeba, dzieje się automatycznie
2). Ustawianie pozycji pliku podczas zapisu - nie trzeba, dzieje się automatycznie
3). Co krok pętli pobieranie rozmiaru bufora i to trzy razy!!!. Char ma rozmiar 1 bajtu, od razu podaje się wartość a jeżeli już chcesz ustalać programowo to oblicz raz przed pętlą.
4). Inc(pozycja) działa szybciej niż pozycja := pozycja + 1;
5). Nie czyta się pliku po bajcie bo to strasznie nieefektywne. Stosuje się bufory 2kB, 4kB, 8kB, 16kB, najlepiej kolejną potęgę dwójki.

Mój algorytm kopiował plik 150.1MB dla bufora

  • 4kB :8.39064s
  • 8kB :2.76632s

Twój kopiował plik 1.6 MB w 20.56901s.

Ładna masakra...

uses
  SysUtils, Classes;

const
  BUF_SIZE = 4096;

type
  TBuffer = array [0..BUF_SIZE-1] of Byte;

var
  OpenFile   :TFileStream;
  SaveFile   :TFileStream;
  Buffer     :TBuffer;
  BufferLeft :LongInt;
  BytesLeft  :LongInt;

begin
  OpenFile := TFileStream.Create('d:\OpenFile.ext', fmOpenRead);
  SaveFile := TFileStream.Create('d:\SaveFile.ext', fmCreate);
  BytesLeft := OpenFile.Size;

  repeat
    if BytesLeft >= BUF_SIZE then
       BufferLeft := BUF_SIZE
    else
       BufferLeft := BytesLeft;

    BufferLeft := OpenFile.Read(Buffer, BufferLeft);
    SaveFile.Write(Buffer, BufferLeft);
    Dec(BytesLeft, BufferLeft);
  until BytesLeft = 0;

  SaveFile.Free();
  OpenFile.Free();
end.
0

Bardzo dziękuje, po tak obszernych wyjaśnieniach i wskazaniu błędów zobaczyłem jak nieefektywny kod zapisu stworzyłem [glowa].

Dziękuje i Pozdrawiam.

0
Oleksy_Adam napisał(a)

3). Co krok pętli pobieranie rozmiaru bufora i to trzy razy!!!. Char ma rozmiar 1 bajtu, od razu podaje się wartość a jeżeli już chcesz ustalać programowo to oblicz raz przed pętlą.

sizeof(typ) jest liczone przez kompilator, więc nie ma znaczenia czy poda się jako 1 czy sizeof(char).

Oleksy_Adam napisał(a)

4). Inc(pozycja) działa szybciej niż pozycja := pozycja + 1;

Działa dokładnie tak samo szybko.

Oleksy_Adam napisał(a)

Mój algorytm kopiował plik 150.1MB dla bufora

  • 4kB :8.39064s
  • 8kB :2.76632s

Szkoda, że wyniki podałeś po zbuforowaniu pliku przez system.

Problem polega na użyciu śmiesznie małego bufora, reszta kodu jest ok. Proponuję jako bufora użyć tablicy dużo większej, niż pisze Adam - o rozmiarze kilku-kilkudziesięciu MB, z tej racji, że w ten sposób ogranicza się (przynajmniej częściowo) ilość zapisów przemieszanych z odczytem, które wymagają przesuwania głowic dysku (a jest to najbardziej kosztowna operacja przy zapisie/odczycie z dysku). Cache dysku niestety i tak wie lepiej, w jakiej kolejności zapisywać i odczytywać dane, jednak zastosowanie dużych buforów pomaga częściowo wymusić przyspieszenie kopiowania.

Dodatkowo dobrym pomysłem byłoby ustawić rozmiar pliku przed rozpoczęciem zapisu do niego, oszczędzi to niepotrzebnych zapisów do MFT (też buforowanych przez system - ale bufor kiedyś się kończy).

0

Co do Inc() już nie pamiętam gdzie czytałem, że jest minimalnie szybsze. Może faktycznie było to widoczne za czasów i486 teraz ginie w GHz. Co do SizeOf() - to już z bardziej eleganckiego punktu widzenia. Jeden kompilator może to optymalizować inny nie musi, czasami wolę pewne rzeczy wymusić. A co do bufora - wiele zależy od wielkości pliku. Najlepiej zrobić program inteligentnie dobierajacy jego rozmiar.

1 użytkowników online, w tym zalogowanych: 0, gości: 1