2014-09-24 15 views
9

ho generare un file molto grande .csv da un database utilizzando il metodo descritto nelUnbuffered uscita molto lenta

https://stackoverflow.com/a/13456219/141172

Funziona bene, fino a un certo punto. Quando il file esportato è troppo grande, ottengo uno OutOfMemoryException.

se spengo buffer di uscita che modificando il codice come questo:

protected override void WriteFile(System.Web.HttpResponseBase response) 
{ 
    response.BufferOutput = false; // <--- Added this 
    this.Content(response.OutputStream); 
} 

il file download. Tuttavia, sono diversi gli ordini di grandezza più lenti di quando è stato abilitato il buffering dell'output (misurato per lo stesso file con buffering true/false, su localhost).

Capisco che è più lento, ma perché rallenterebbe a una scansione relativa? C'è qualcosa che posso fare per migliorare la velocità di elaborazione?

UPDATE

sarebbe anche la possibilità di utilizzare File(Stream stream, String contentType) come suggerito nei commenti. Tuttavia, non sono sicuro di come creare stream. I dati vengono assemblati dinamicamente in base a una query DB e MemoryStream esaurisce la memoria fisica contigua. I suggerimenti sono ben accetti

UPDATE 2

È stato suggerito nei commenti che alternativamente la lettura dal database e la scrittura al flusso sta causando un degrado. Ho modificato il codice per eseguire la scrittura del flusso in un thread separato (utilizzando il pattern produttore/consumatore). Non c'è una differenza apprezzabile nelle prestazioni.

+0

Non capisco il motivo per cui è necessario che la risposta collegata. Cosa c'è di sbagliato semplicemente restituendo 'File (myStream," text/csv ")' dalla tua azione? http://msdn.microsoft.com/en-us/library/dd493017(v=vs.100).aspx –

+0

Sto leggendo quello sbagliato? Stai trasmettendo il flusso di output della risposta come input per il contenuto? Che quindi scrive nuovamente sul flusso di output della risposta? Di quale classe stai sovrascrivendo il metodo WriteFile? –

+0

@ ta.speot.è: non sono sicuro di come creare 'myStream'. I dati vengono assemblati dinamicamente in base a una query DB e MemoryStream esaurisce la memoria fisica contigua. Puoi mostrare un pattern per creare un 'myStream' che può essere passato a' File (myStream, "text/csv") '? –

risposta

6

Non so cosa ASP.NET e IIS stiano facendo esattamente con lo streaming di output ma potrebbero essere utilizzati troppi pezzi troppo piccoli. Collega in un BufferedStream con un buffer molto grande, come 4 MB.

Secondo i vostri commenti ha funzionato. Ora, abbassa le dimensioni del buffer per risparmiare memoria e avere un set di lavoro più piccolo. Buono per la cache.

Come commento soggettivo sono deluso dal fatto che sia addirittura necessario. IIS dovrebbe utilizzare automaticamente i buffer corretti, il che è estremamente semplice con le connessioni TCP.

Modifica dal OP

Ecco il codice derivato da questa risposta

public ActionResult Export() 
{ 
    // Domain specific stuff here 
    return new FileGeneratingResult("MyFile.txt", "text/text", 
      stream => this.StreamExport(stream), false); 
} 

private void StreamExport(Stream stream) 
{ 
    using (BufferedStream bs = new BufferedStream(stream, 256*1024)) 
    using (StreamWriter sw = new StreamWriter(bs)) 
    foreach (var stuff in MyData()) 
    { 
     sw.Write(stuff); 
    } 
} 
+0

Il guadagno di prestazioni è dell'ordine di 100x. Anch'io sono sorpreso che sia necessario un 'BufferedStream 'esterno. –

+1

Un buffer da 256 KB è soggettivamente veloce come un buffer da 4 MB. Poiché si tratta di una funzione usata raramente, non ho intenzione di sintonizzarmi ulteriormente. Chiunque stia leggendo questo problema con un volume elevato di richieste di questo tipo sarebbe opportuno consigliare di testare buffer più piccoli per trovare la dimensione ottimale del buffer per ottenere buone prestazioni e un minimo ingombro di memoria. –

+0

@EricJ. Mi rendo conto che questo è un po 'di tempo fa, ma mi sto imbattendo in un problema simile e lo trovo illuminante. Puoi aggiornare la tua domanda per mostrare come hai usato 'BufferedStream'? Sono confuso da come collegarlo. Grazie. – LoJo

0

In ultimo aggiornamento di Eric, ha ricordato con un altro thread. Anch'io ho avuto questo problema per l'implementazione delle esportazioni di database. Ecco qualche esempio di codice per la soluzione che ho usato:

Handling with temporary file stream

Problemi correlati