2009-11-04 9 views
7

Ho una directory enorme di circa 500k file jpg e mi piacerebbe archiviare tutti i file più vecchi di una certa data. Attualmente, lo script richiede ore per essere eseguito.Copia file eseguibile in C#?

Questo ha molto a che fare con le prestazioni molto pessime dei server di archiviazione GoGrid, ma allo stesso tempo, sono sicuro che c'è un modo più efficiente in cui Ram/Cpu è saggio a realizzare ciò che sto facendo.

Ecco il codice che ho:.

var dirInfo = new DirectoryInfo(PathToSource); 
var fileInfo = dirInfo.GetFiles("*.*"); 
var filesToArchive = fileInfo.Where(f => 
    f.LastWriteTime.Date < StartThresholdInDays.Days().Ago().Date 
     && f.LastWriteTime.Date >= StopThresholdInDays.Days().Ago().Date 
); 

foreach (var file in filesToArchive) 
{ 
    file.CopyTo(PathToTarget+file.Name); 
} 

The Days() fa() roba è solo zucchero sintattico.

+0

che si basa sul sistema operativo host, che dovrebbe essere di prim'ordine. –

+0

Ya, la verità è che ci potrebbero essere milioni di file, non sono nemmeno in grado di ottenere un conteggio della directory tramite Windows Explorer a causa di problemi di prestazioni simili. – Scott

+2

La grammatica nazista dice: "Performant" non è una parola :) –

risposta

3
+0

Grazie Mauricio ... questo funziona per il problema della RAM, ma non per la CPU. Ci vogliono ancora ore per realizzarlo, ma almeno la RAM non mi balla addosso. – Scott

+0

Questo funziona abbastanza bene per risolvere il mio problema. Prende circa 2 ore, ma ora può funzionare in background con un massimo di 4 mega di RAM, mentre prima usava centinaia di mega. – Scott

1

È possibile provare a utilizzare (un numero limitato di) thread per eseguire CopyTo(). In questo momento l'intera operazione è limitata a 1 core.

Ciò migliorerà le prestazioni solo se è ora associato alla CPU. Ma se funziona su un RAID, potrebbe funzionare.

+0

Credo che GoGrid sia "nel Cloud". Potrebbero esserci limitazioni sulle connessioni attive. Indipendentemente da ciò, un buon consiglio. – user7116

2

Vorrei ricordare la regola 80/20 e notare che se la maggior parte del rallentamento è file.CopyTo e questo rallentamento supera di gran lunga le prestazioni della query LINQ, quindi non mi preoccuperei. È possibile verificare questo rimuovendo la linea file.CopyTo e sostituendolo con un'operazione Console.WriteLine. Tempo rispetto alla copia reale. Troverai il sovraccarico di GoGrid rispetto al resto dell'operazione. La mia impressione è che non ci saranno grossi guadagni realistici sul tuo terminale .

EDIT: Ok, quindi l'80% è l'operazione GetFiles, il che non sorprende se in realtà ci sono un milione di file nella directory. La cosa migliore potrebbe essere quella di iniziare a utilizzare direttamente l'API Win32 (come FindFirstFile e family) e P/Invoke:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)] 
static extern IntPtr FindFirstFile(string lpFileName, 
    out WIN32_FIND_DATA lpFindFileData); 

Vorrei anche suggerire, se possibile, alterando la struttura di directory per diminuire il numero di file per directory. Ciò migliorerà immensamente la situazione.

EDIT2: Considererei anche il passaggio da GetFiles("*.*") a GetFiles(). Dal momento che stai chiedendo tutto, non ha senso applicarlo a regole di globbing ad ogni passaggio.

+0

La maggior parte dell'operazione è l'istruzione dirInfo.GetFiles ("*. *"). Sto facendo un test con solo 5 giorni di file, e ho esaurito RAM/Patience prima di poter ottenere un conteggio dei file nella directory da cui eseguire la query di linq. C'è un modo migliore per GetFiles [], come se solo GetFiles [] restituisse File che si trovano all'interno di un intervallo, invece di doverli restituire tutti? Almeno in questo modo, posso interrompere questa operazione in blocchi del 10% questa prima volta, e quindi far funzionare l'archiviatore ogni notte. Allo stato attuale, non posso arrivare da nessuna parte. – Scott

+0

Sì, modificare la struttura delle directory è quello che sto cercando di fare, ma prima devo accedere ai file senza dover aspettare tutto il giorno e temporizzare il server :) – Scott

10

L'unica parte che penso che potresti migliorare è la dirInfo.GetFiles("*.*"). In .NET 3.5 e versioni precedenti, restituisce un array con tutti i nomi di file, che richiede tempo per creare e utilizza molta RAM. In .NET 4.0, esiste un nuovo metodo Directory.EnumerateFiles che restituisce uno IEnumerable<string> e recupera immediatamente i risultati man mano che vengono letti dal disco. Questo potrebbe migliorare un po 'le prestazioni, ma non aspettarti miracoli ...

+0

In realtà è esattamente ciò che deve essere fatto, EnumerateFiles restituisce Enumerator non il tutta la lista Si salva tutta la memoria necessaria per l'array. Diciamo che i suoi file 500k * 100 byte = 50 MB di RAM. Usando Enumerate utilizzerai solo fino a 100 byte, perché ottieni 1 file alla volta. – Kugel

+0

+1, .Net 4.0 ha molte funzioni davvero interessanti in System.IO. Non sono sicuro se migliorerà la situazione con un milione di file in una directory :-D – user7116

2

È consigliabile utilizzare un'utilità di terze parti per eseguire la copia per te. Qualcosa come la robocopy può velocizzare significativamente l'elaborazione. Vedi anche https://serverfault.com/questions/54881/quickest-way-of-moving-a-large-number-of-files

+0

+1, robocopy/minage = X/maxage = Y – user7116

+2

E robocopy è incluso in Win7 e Server 2008 per impostazione predefinita! – joshperry

+0

sì, non esattamente quello che chiamerei "terze parti";) –

0

Ascoltare questo Hanselminutes podcast. Scott parla con Aaron Bockover dell'autore del media player di Banshee, si sono imbattuti in questo numero esatto e ne parlano alle 8:20 del podcast.

Se è possibile utilizzare .Net 4.0, utilizzare Directory.EnumerateFiles come indicato da Thomas Levesque. In caso contrario, potrebbe essere necessario scrivere il proprio codice di indirizzamento personale come in Mono.Posix utilizzando le API Win32 native.