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


Автор: XperT 1.5.2012, 11:51
Бьюсь сейчас над решением задачи быстрого копирования большого количества файлов. Ситуация такова, что при использовании функции CopyFileEx на файлах в количестве 30 000 программе необходимо около 6 мин, в то время, как копирование этих же файлов с помощью проводника отнимает чуть больше полторы минуты.

Подозреваю, что всё дело в том, что ОС это делает в несколько потоков, потому вижу 2 варианта решения проблемы:
1. Самому делать в это в несколько потоков (не факт, что сработает, так как возможно, что все запросы на копирование файлов с помощью функции CopyFileEx становятся в очередь)

2. Найти функцию, которая отправляет на копирование не один файл, а группу файлов, чтобы ОС сама могла распределять свои ресурсы для самого оптимального копирования

Более предпочтительным есть второй вариант, но не уверен, что в WinAPI найдется подобная функция. Если кто-то знает о её существовании или имеет еще какие-то варианты решения проблемы - буду очень признателен.

Автор: kami 1.5.2012, 14:10
Цитата(XperT @  1.5.2012,  11:51 Найти цитируемый пост)
но не уверен, что в WinAPI найдется подобная функция. 

http://msdn.microsoft.com/en-us/library/windows/desktop/bb762164(v=vs.85).aspx

Автор: XperT 1.5.2012, 17:31
Цитата(kami @ 1.5.2012,  14:10)
Цитата(XperT @  1.5.2012,  11:51 Найти цитируемый пост)
но не уверен, что в WinAPI найдется подобная функция. 

http://msdn.microsoft.com/en-us/library/windows/desktop/bb762164(v=vs.85).aspx

К сожалению скорость от использования этой функции не выросла: во-первых очень медленно проходит подготовка файлов (секунд 30-40), по всей видимости в это время парсится входящая строка, а во вторых скорость копирования такая же как при использовании CopyFileEx (в окне копирования пишет 1,26 МБ/сек, при том, что во время копирования через проводник скорость 5 МБ/сек).

Может кто-то знает другие способы быстрого копирования файлов?

Автор: kami 1.5.2012, 20:49
Цитата(XperT @  1.5.2012,  17:31 Найти цитируемый пост)
при том, что во время копирования через проводник скорость 5 МБ/сек).

Как происходит тестирование?
Сперва - своя программа, потом - проводник?

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

Автор: Keeper89 1.5.2012, 20:51
Цитата(kami @  1.5.2012,  20:49 Найти цитируемый пост)
Для чистоты эксперимента не помешает перезагрузка компьютера между замерами. В противном случае винда может закешировать файлы, и в результате второй "забег" окажется гораздо быстрее. 

А можно попробовать наоборот, сначала проводник, а потом программа.

Автор: XperT 1.5.2012, 21:06
Попробовал наоборот, ничего не поменялось, проводник копирует минимум в 5 раз быстрее

Автор: Keeper89 1.5.2012, 22:42
XperT, покажи код.

Автор: XperT 1.5.2012, 22:58
Класс делал с заделом на будущее:
Код

unit FastFileProccessingClass;

interface

uses SysUtils, Windows, Classes, ShellApi;

type
  TBuffer = array of Char;

  TFileProccessing = class
  private
    FMultiDest : boolean;
    FHandle : Cardinal;

    FilesList : TStringList;
    DestList : TStringList;

    procedure CreateBuffer( Names : TStringList; var P : TBuffer );
  public
    constructor Create(Hwnd : Cardinal; multi_dest : boolean = false);  overload;
    destructor Destroy(); override;

    procedure Clear();
    function  Count():integer;

    procedure AddFileDest(file_path, dest_folder:string);
    procedure AddFile(file_path : string);
    procedure SetDestination(dest_folder : string);

    function CopyFiles():boolean;
  end;

implementation

{ TFileProccessing }

procedure TFileProccessing.AddFile(file_path: string);
begin
  if FMultiDest then raise Exception.Create('Can''t use single adding files in multidest mode');

  FilesList.Add(file_path);
end;

procedure TFileProccessing.AddFileDest(file_path, dest_folder: string);
begin
  FilesList.Add(file_path);
  DestList.Add(dest_folder);
end;

procedure TFileProccessing.Clear;
begin
  FilesList.Clear;
  DestList.Clear;
end;

function TFileProccessing.CopyFiles: boolean;
var
  SHFileOpStruct : TSHFileOpStruct;
  SrcBuf, DestBuf : TBuffer;
begin
  if DestList.Count = 0 then raise Exception.Create('Destination folder is not selected!');
  if FilesList.Count = 0 then raise Exception.Create('There is no files for coping!');

  CreateBuffer( FilesList, SrcBuf );
  CreateBuffer( DestList, DestBuf );

  with SHFileOpStruct do
  begin
    Wnd := FHandle;
    wFunc := FO_COPY;
    pFrom := Pointer( SrcBuf );
    pTo := Pointer( DestBuf );

    fFlags := FOF_NOCONFIRMMKDIR;
    if FMultiDest then fFlags := fFlags OR FOF_MULTIDESTFILES;

    fAnyOperationsAborted := False;
    hNameMappings := nil;
    lpszProgressTitle := nil;
  end;

  Result := SHFileOperation( SHFileOpStruct ) = 0;

  SrcBuf := nil;
  DestBuf := nil;
  Clear();
end;

function TFileProccessing.Count: integer;
begin
  result := FilesList.Count;
end;

constructor TFileProccessing.Create(Hwnd : Cardinal; multi_dest : boolean = false);
begin
  FMultiDest := multi_dest;
  FHandle := Hwnd;

  FilesList := TStringList.Create;
  DestList := TStringList.Create;
end;

procedure TFileProccessing.CreateBuffer(Names: TStringList; var P: TBuffer);
var
  i, j, a : integer;
  buf : string;
begin
  SetLength(P, length(Names.Text) - Names.Count + 1);

  a := 0;
  for i := 0 to Names.Count - 1 do
  begin
    buf := Names.Strings[i];
    for j := 1 to length(buf) do
    begin
      P[a] := buf[j];
      inc(a);
    end;

    P[a] := #0;
    inc(a);
  end;

  P[a] := #0;
end;

destructor TFileProccessing.Destroy;
begin
  FilesList.Free;
  DestList.Free;

  inherited;
end;

procedure TFileProccessing.SetDestination(dest_folder: string);
begin
  if FMultiDest then raise Exception.Create('Can''t use single set destination in multidest mode');

  DestList.Text := dest_folder;
end;

end.


Проверял на таком коде:
Код

...
for i := 0 to Files.Count - 1 do
  FileProccessing.AddFile(Files.Strings[i]);

FileProccessing.SetDestination('D:\TMP');
FileProccessing.CopyFiles();
...

Автор: northener 2.5.2012, 00:27
Смущает меня то, что сравнивается не время копирования а "значение скорости копирования" выдаваемое окном визуализации процесса копирования.
И ещё одно смущает. В методе TFileProccessing.CopyFiles структура SHFileOpStruct - локальная переменная, но никакого предварительного обнуления сей структуры не производится. Следовательно некоторые поля этой структуры явно задаются в коде метода, а прочие остаются на усмотрение Александра Сергеевича.

Автор: XperT 2.5.2012, 10:45
Цитата

Смущает меня то, что сравнивается не время копирования а "значение скорости копирования" выдаваемое окном визуализации процесса копирования.

Проводник копирует за 1-1,5 мин, программа за 6-7 мин. Точных вычислений не делал, и так прекрасно видно, что разница в скорости очень большая. В посте приводил данные по скорости копирования из окна копирования потому, что соотношения соотвествуют временным.

Цитата

И ещё одно смущает. В методе TFileProccessing.CopyFiles структура SHFileOpStruct - локальная переменная, но никакого предварительного обнуления сей структуры не производится. Следовательно некоторые поля этой структуры явно задаются в коде метода, а прочие остаются на усмотрение Александра Сергеевича. 

Какие прочие? Все свойства структуры заполнены, проверял через дебагер.

Автор: XperT 2.5.2012, 14:29
После перезагрузки провел еще один эксперимент: сначала копировал проводником, потом через программу.
Проводник:
Процесс копирования запустился сразу, без задержек на оценку. Продолжительность копирования: 2 мин 50 сек

Программа:
1. Сначала запустилась оценка:
user posted image
Это продолжалось 48 сек.

2. Потом запустилось само копирование. Продолжительность ~ 2 мин

3. Под конец копирования окошко подвисло в таком состоянии:
user posted image
Длилось это 3 мин 39 сек

Итого: 6 мин 27 сек против 2 мин 50 сек

Автор: northener 2.5.2012, 17:07
Цитата(XperT @  2.5.2012,  10:45 Найти цитируемый пост)
Какие прочие? Все свойства структуры заполнены, проверял через дебагер. 

Чем они заполнены, если вы сами их не заполняли? Мусором? 

Автор: XperT 2.5.2012, 17:30
Цитата(northener @ 2.5.2012,  17:07)
Цитата(XperT @  2.5.2012,  10:45 Найти цитируемый пост)
Какие прочие? Все свойства структуры заполнены, проверял через дебагер. 

Чем они заполнены, если вы сами их не заполняли? Мусором?

Кто они? Вы видели описание структуры? Покажите мне хоть одно свойство которое я не заполняю?

Автор: northener 2.5.2012, 23:42
Цитата(XperT @  2.5.2012,  17:30 Найти цитируемый пост)
Покажите мне хоть одно свойство которое я не заполняю? 

Да. Был не прав, признаю.

Цитата(XperT @  2.5.2012,  14:29 Найти цитируемый пост)
3. Под конец копирования окошко подвисло в таком состоянии:

Значит надо попробовать разобраться из-за чего происходит это подвисание. Ибо весь остальной процесс выполняется за такое же время как и процесс копирования в проводнике.

Автор: XperT 3.5.2012, 00:11
Цитата

Значит надо попробовать разобраться из-за чего происходит это подвисание. Ибо весь остальной процесс выполняется за такое же время как и процесс копирования в проводнике. 

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

В любом случае проблему решил другим путем: копирование файлов через CopyFileEx в несколько потоков. Опытным путем было вычислено, что при разбиении списка файлов на 3 равных части и копирования каждой в отдельном потоке даёт самую оптимальную скорость. На тех же файлах скорости копирования находится в диапазоне 120-150 сек. Увеличивание количества потоков даёт очень маленький прирост (скорее всего всё дело упирается в скорость работы винта, что собственно и требовалось сделать).

Тему можно считать закрытой.

Автор: northener 3.5.2012, 00:32
Цитата(XperT @  3.5.2012,  00:11 Найти цитируемый пост)
Тему можно считать закрытой. 

Для вас да. А вот меня она заинтересовала. Ибо скорость копирования - это скорость копирования не более того. Тем более, что скорость копирования зависит ещё и от конфигурации компьютерного железа. А вот время выполнения копирования функцией SHFileOperation мне гораздо более интересно. Как, когда и от чего это время зависит?

Автор: XperT 3.5.2012, 00:35
Цитата(northener @ 3.5.2012,  00:32)
Цитата(XperT @  3.5.2012,  00:11 Найти цитируемый пост)
Тему можно считать закрытой. 

Для вас да. А вот меня она заинтересовала. Ибо скорость копирования - это скорость копирования не более того. Тем более, что скорость копирования зависит ещё и от конфигурации компьютерного железа. А вот время выполнения копирования функцией SHFileOperation мне гораздо более интересно. Как, когда и от чего это время зависит?

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

Автор: northener 3.5.2012, 00:49
Цитата(XperT @  3.5.2012,  00:35 Найти цитируемый пост)
Ну если разберетесь - буду рад услышать решение.

Ну тогда помогите мне задать начальные условия для проверки. Как вы формировали список из 30000 файлов для копирования? 
1. В случае копирования через проводник 
2. В случае копирования через вашу программу

Автор: XperT 3.5.2012, 12:10
Есть папка с файлами (пробовал по разному: и с подпапками, и все картинки в одной папке - разницу не заметил). В папке находятся аватарки в большом количестве (по сути большое количество маленьких файлов)

1. В проводнике: выделяю папку -> Ctrl+C -> открываю другую папку (на другом диске) -> Ctrl+V
2. В программе: парсится папка с файлами -> формируется список файлов -> отправляется в функцию копирования файлов

Замер скорости копирования во втором случае начинается с момента появления окошка копирования (т.е. исключается время парсинга списка файлов).

Автор: Keeper89 3.5.2012, 18:19
Ну ты в одном случае копируешь папку, а в другом список файлов. Что если попробовать делать копии одного и того же, например папку в обоих случаях?

Автор: XperT 3.5.2012, 22:02
Дело в том, что в некоторых случаях программа должна копировать не все файлы, а в некоторых даже не все файлы в одну папку, потому интересовал универальный способ быстрого копирования

Автор: northener 3.5.2012, 23:42
Цитата(XperT @  3.5.2012,  22:02 Найти цитируемый пост)
Дело в том, что в некоторых случаях программа должна копировать не все файлы, а в некоторых даже не все файлы в одну папку, потому интересовал универальный способ быстрого копирования 

Вот почему я и спросил "Как вы формировали список из 30000 файлов для копирования?".
Продолжаю настаивать на том, что нужно разобраться в причинах того самого зависания.

Автор: Keeper89 4.5.2012, 00:04
Цитата(XperT @  3.5.2012,  22:02 Найти цитируемый пост)
Дело в том, что в некоторых случаях программа должна копировать не все файлы, а в некоторых даже не все файлы в одну папку, потому интересовал универальный способ быстрого копирования 

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

Автор: XperT 4.5.2012, 13:30
Проводник:
1. Зашел в папку с картинками, выделил все файлы, копировал - произошла задержка на 5 сек
2. Открыл папку назначения, Ctrl + V,  произошла задержка в 3 сек для оценки и сразу запустилось копирование
3. Процесс копирования длился 3 мин 15 сек
4. Под конец копирования никаких зависаний не было, окно сразу же закрылось

Итого: немного медленее чем при копировании папки, но всё равно на много быстрее чем через SHFileOperation

Автор: Keeper89 4.5.2012, 15:23
Мне прям интересно стало, надо вечером вбить, попробовать

Автор: XperT 4.5.2012, 17:32
Есть подозрение, что в Вин7 проводник использует другую WinAPI функцию для копирования (которая появилась только в висте или 7).

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