Как реализовать эффективный метод filecopy в C # из общего сетевого ресурса? - PullRequest
8 голосов
/ 06 июля 2010

Я пытаюсь реализовать метод filecopy, который может сопоставить производительность копии, сделанной с помощью проводника Windows.

Например, копия (с помощью проводника Windows) из нашего NAS на мой компьютер, выполняется выше100 МБ / с.

Моя текущая реализация делает ту же копию со скоростью около 55 МБ / с, что уже лучше, чем System.IO.File.Copy (), которая работает со скоростью 29 МБ / с.

static void Main(string[] args)
    {
        String src = @"";
        String dst = @"";

        Int32 buffersize = 1024 * 1024;
        FileStream input = new FileStream(src, FileMode.Open, FileAccess.Read, FileShare.None, 8, FileOptions.Asynchronous | FileOptions.SequentialScan);
        FileStream output = new FileStream(dst, FileMode.CreateNew, FileAccess.Write, FileShare.None, 8, FileOptions.Asynchronous | FileOptions.SequentialScan);

        Int32 readsize = -1;
        Byte[] readbuffer = new Byte[buffersize];
        IAsyncResult asyncread;
        Byte[] writebuffer = new Byte[buffersize];
        IAsyncResult asyncwrite;

        DateTime Start = DateTime.Now;

        output.SetLength(input.Length);

        readsize = input.Read(readbuffer, 0, readbuffer.Length);
        readbuffer = Interlocked.Exchange(ref writebuffer, readbuffer);

        while (readsize > 0)
        {
            asyncwrite = output.BeginWrite(writebuffer, 0, readsize, null, null);
            asyncread = input.BeginRead(readbuffer, 0, readbuffer.Length, null, null);

            output.EndWrite(asyncwrite);
            readsize = input.EndRead(asyncread);
            readbuffer = Interlocked.Exchange(ref writebuffer, readbuffer);
        }

        DateTime Stop = DateTime.Now;

        TimeSpan Duration = Stop - Start;
        Double speed = input.Length / Duration.TotalSeconds; // bytes/s

        System.Console.WriteLine("MY Speed : " + (speed / 1024 / 1024).ToString() + " mo/sec");

        input.Close();
        output.Close();
        System.IO.File.Delete(dst);
    }

Есть идеи, как повысить производительность?

РЕДАКТИРОВАТЬ:

Файл читается из NAS на основе Linux с интерфейсом 10 Gigabit Ethernet с 60 дисков позади (неНе беспокойтесь о его производительности, он работает очень хорошо) и записывается в локальный raid0, который может записывать данные со скоростью около 140 МБ / с.

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

Кроме того, удаление записи не сделает чтение более быстрым, поэтому я не могу превысить этот предел чтения 55 МБ / с.

РЕДАКТИРОВАТЬ 2:

Скорость выдачиЭто связано с тем, что исходный файл хранится в общей сетевой папке.Только чтение с моего локального диска с моим фрагментом кода дает мне скорость 112 МБ / с.

РЕДАКТИРОВАТЬ 3:

Samba, похоже, не проблема.Я заменил общий ресурс cifs (samba) на общий ресурс nfs на моем linux nas и получил худшие результаты, чем samba на моем клиенте win7.

С nfs мой метод копирования и проводник Windows имели одинаковую производительность, примерно42 МБ / с.

У меня нет идей ...

РЕДАКТИРОВАТЬ 4:

Просто чтобы быть уверенным, что проблема была в Windows, я установил debian lenny, смонтировал nas через nfs и получил 79 МБ / с с тем же кодом под моно.

Ответы [ 6 ]

5 голосов
/ 06 июля 2010

Попробуйте изменить размер буфера, чтобы он равнялся размеру сектора на жестком диске - вероятно, 4 КБ.Также используйте класс System.Diagnostics.Stopwatch для синхронизации.

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

Опять же, используйте оператор using для управления удалением ваших потоков.Тем не менее, обратите внимание, что это изменит ваше время, так как вы в настоящее время утилизируете объекты после остановки таймера.

4 голосов
/ 06 июля 2010

Вы пробовали меньший размер буфера? Размер буфера 1 МБ ужасно велик, и обычно размер буфера 4-64 КБ обеспечивает наилучшую производительность.

Кроме того, это может быть связано с вашим вопросом: Как написать сверхбыстрый код потоковой передачи файлов на C #?

И, возможно, вы можете улучшить производительность, используя файлы с отображенной памятью: http://weblogs.asp.net/gunnarpeipman/archive/2009/06/21/net-framework-4-0-using-memory-mapped-files.aspx

1 голос
/ 06 июля 2010

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

  • Размер пакета протокола SMB
  • Сколько памяти вы готовы использовать для кэширования (что может замедлить другие процессы)
  • Можете ли вы пожертвовать надежностью ради скорости
  • Хотите ли вы увеличить воспринимаемую скорость или фактическое время до завершения
  • Где и сколько вы хотите кэшировать на нескольких возможных уровнях
  • Если вы заинтересованы в предоставлении надежной обратной связи и оценки времени
1 голос
/ 06 июля 2010

File.Copy() просто вызывает CopyFile() API, вы можете попробовать p / invoke SHFileOperation(), что и используется оболочкой - часто это кажется быстрее.

1 голос
/ 06 июля 2010

Есть обычные подозреваемые для увеличения скорости по сети:

  • Есть несколько тем для скачивания
  • Предварительно выделить блок на диске, где будет находиться файл

Кроме этого, вы зависите от ограничений вашего оборудования.

0 голосов
/ 06 июля 2010

Вероятно, единственным более быстрым вариантом будет использование unbuffered IO: Функция ReadFile , Функция CreateFile , Функция WriteFile с флагом FILE_FLAG_NO_BUFFERING с 2-6 МБ буфера.

Также таким образом вам придется выровнять размер буфера с размером сектора файловой системы и т. Д.

Это будет значительно быстрее - особенно в Windows XP.

кстати.Таким образом, я получил полосу пропускания ~ 400 МБ для чередующегося массива RAID 0 (используя буфер 4 МБ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...