2015-08-26 9 views
1

Мне нужно скопировать файлы из одной директории в другую, в зависимости от наличия имени файла в таблице базы данных SQL.Самый быстрый способ скопировать файлы из одной директории в другую

Для этого я использую следующий код:

using(SqlConnection connection = new SqlConnection("datasource or route")) 
{ 

    connection.Open(); 

    using(SqlCommand cmd = new SqlCommand("SELECT idPic, namePicFile FROM DocPicFiles", connection)) 
    using (SqlDataReader reader = cmd.ExecuteReader()) 
    { 

    if (reader != null) 
    { 
     while (reader.Read()) 
     { 
     //picList IS AN ARRAY THAT Contains All the files names in a directory 
     if (picList.Any(s => s.Contains(reader["namePicFile"].ToString()))) 
     { 
      File.Copy("theFile in the Directory or array picList", "the destiny directory"+ ".jpg", false) 
     } 
     } 
    } 
    } 
} 

Есть ли способ, что это может быть сделано за меньшее время? Для этого требуется 1 час, для 20,876 записей.

+2

Сколько этого времени используется File.Copy и сколько используется для циклического поиска и поиска имен? Если вы не измеряете, вы не можете понять, где проблема, и искать жизнеспособные (и существующие) решения. – Steve

+1

Разделите свой метод на один, который возвращает список , содержащий все файлы, которые вы хотите скопировать, затем отправьте этот список в другой метод который выполняет копирование. Затем вы можете измерить оба метода и выяснить, где узкое место. – Kvam

ответ

3

Поскольку ваш I/O подсистема почти наверняка botteneck здесь, с помощью параллельной библиотеки задача, вероятно, примерно так же хорошо, как он получает:

static void Main(string[] args) 
{ 
    DirectoryInfo source  = new DirectoryInfo(args[0]) ; 
    DirectoryInfo destination = new DirectoryInfo(args[1]) ; 

    HashSet<string> filesToBeCopied = new HashSet<string>(ReadFileNamesFromDatabase() , StringComparer.OrdinalIgnoreCase) ; 

    // you'll probably have to play with MaxDegreeOfParallellism so as to avoid swamping the i/o system 
    ParallelOptions options= new ParallelOptions { MaxDegreeOfParallelism = 4 } ; 

    Parallel.ForEach(filesToBeCopied.SelectMany(fn => source.EnumerateFiles(fn)) , options , fi => { 
     string destinationPath = Path.Combine(destination.FullName , Path.ChangeExtension(fi.Name , ".jpg")) ; 
     fi.CopyTo(destinationPath , false) ; 
    }) ; 

} 

public static IEnumerable<string> ReadFileNamesFromDatabase() 
{ 
    using (SqlConnection connection = new SqlConnection("connection-string")) 
    using (SqlCommand cmd = connection.CreateCommand()) 
    { 
    cmd.CommandType = CommandType.Text ; 
    cmd.CommandText = @" 
     select idPic , 
      namePicFile 
     from DocPicFiles 
     " ; 

    connection.Open() ; 
    using (SqlDataReader reader = cmd.ExecuteReader()) 
    { 
     while (reader.Read()) 
     { 
     yield return reader.GetString(1) ; 
     } 
    } 
    connection.Close() ; 

    } 
} 
+1

У вас есть какие-либо измерения времени для поддержки того, что TPL будет быстрее, чем текущий подход OP? Я видел многопоточность как медленнее в производительности, чем одиночный поток. – displayName

+0

@ nicholas-carey У меня проблемы с этой строкой: ** Parallel.ForEach (filesToBeCopied.SelectMany (fn => source.EnumerateFiles (fn)), options, fi => { string destinationPath = Path.Combine (destination.FullName , Path.ChangeExtension (fi.Name, ".jpg")); fi.CopyTo (destinationPath, false); ** – ger

8

File.Copy так же быстро, как и он. Вы должны помнить, что вы зависите от скорости передачи файлов, продиктованной вашим оборудованием, и в 20000 файлах, латентность для доступа к данным также входит в игру. Если вы делаете это на жестком диске, вы можете увидеть значительное улучшение после перехода на SSD или какой-либо другой быстрый носитель.

Для этого случая, скорее всего, аппаратное обеспечение является вашим узким местом.

EDIT: Я считаю, что поддерживать соединение с базой данных в течение столь длительного времени, как плохая практика. Я предлагаю вам получить все необходимые данные в некоторых кэшах в памяти (массив, список, что угодно), а затем повторить это при копировании файлов. Соединение с db является ценным ресурсом и приложениями, которые должны обрабатывать высокий уровень параллелизма (но не только), освобождение соединения быстро является обязательным.

+0

да! соединение увеличивает ресурс, и я перешел на список объекта «DocPicFiles», этот сдвиг значительно уменьшается, действие копирования - 35 минут! – ger

+0

@ger: Мне было удивительно видеть такую ​​большую разницу во времени с этим небольшим изменением.Если возможно, вы можете снова запустить свой код с открытием соединения и без него. Таким образом, мы узнаем, что в игре нет другого фактора. – displayName

4

Позвольте мне сделать предположение - Mmmmm ... Нет. У нас нет способа сделать это быстрее.

Почему я так уверен? Поскольку для копирования файлов требуется разговаривать с диском, и это ужасно медленная операция. Более того, если вы попытаетесь использовать многопоточность, результаты будут медленнее, а не быстрее, потому что «механическая» работа по перемещению головы над диском уже не является последовательной, что возможно было раньше случайно.

См. answers to this question I asked earlier.

Так что да, попробуйте перейти на SSD, если вы еще не используете их, иначе вы получите лучшее уже.

Ниже приведена информация о том, что делает slow среднее значение на диске при записи по сравнению с кешами. Если доступ к кэшу занимает 10 минут, это означает, что для чтения с диска требуется 2 года. Все обращения показаны на картинке ниже. Очевидно, что когда ваш код будет выполнен, узким местом будет запись на диск. Лучшее, что вы можете сделать, чтобы записи на диске оставались последовательными.

enter image description here

 Смежные вопросы

  • Нет связанных вопросов^_^