2011-02-02 19 views
9

Estoy trabajando en un programa que realiza un acceso aleatorio pesado de lectura/escritura en un archivo enorme (hasta 64 GB). Los archivos están estructurados específicamente y para hacer que accedan a ellos. He creado un marco; después de un rato intenté probar el rendimiento en él y me di cuenta de que en el archivo preasignado las operaciones de escritura secuencial son demasiado lentas para ser aceptables. Después de muchas pruebas repliqué el comportamiento sin mi framework (solo métodos FileStream); aquí está la parte del código que (con mi hardware) replica el tema:Comportamiento extraño con FileStream.WriteFile

FileStream fs = new FileStream("test1.vhd", FileMode.Open); 
byte[] buffer = new byte[256 * 1024]; 
Random rand = new Random(); 
rand.NextBytes(buffer); 
DateTime start, end; 
double ellapsed = 0.0; 
long startPos, endPos; 

BinaryReader br = new BinaryReader(fs); 
br.ReadUInt32(); 
br.ReadUInt32(); 
for (int i = 0; i < 65536; i++) 
    br.ReadUInt16(); 

br = null; 

startPos = 0; // 0 
endPos = 4294967296; // 4GB 
for (long index = startPos; index < endPos; index += buffer.Length) 
{ 
    start = DateTime.Now; 
    fs.Write(buffer, 0, buffer.Length); 
    end = DateTime.Now; 
    ellapsed += (end - start).TotalMilliseconds; 
} 

Por desgracia, la cuestión parece ser impredecible, así que a veces "obras", a veces no lo hace. Sin embargo, utilizando el monitor de procesos que he cogido los siguientes eventos:

 
Operation Result Detail 
WriteFile SUCCESS Offset: 1.905.655.816, Length: 262.144 
WriteFile SUCCESS Offset: 1.905.917.960, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.180.104, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.442.248, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.704.392, Length: 262.144 
WriteFile SUCCESS Offset: 1.906.966.536, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.228.672, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.228.680, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.355.648, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
ReadFile SUCCESS Offset: 1.907.490.816, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.490.824, Length: 262.144 
ReadFile SUCCESS Offset: 1.907.617.792, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
ReadFile SUCCESS Offset: 1.907.752.960, Length: 32.768, I/O Flags: Non-cached, Paging I/O, Synchronous Paging I/O, Priority: Normal 
WriteFile SUCCESS Offset: 1.907.752.968, Length: 262.144 

Eso es, después de sobre-escritura casi 2 GB, FileStream.Write aperturas para llamar ReadFile después de cada WriteFile, y este tema continuará hasta el final de la proceso; Además, el desplazamiento en el que comienza el problema parece ser aleatorio. He depurado paso a paso dentro del método FileStream.Write y he verificado que en realidad es el WriteFile (API de Win32) que, internamente, llama al ReadFile.

Última nota; No creo que sea un problema de fragmentación de archivos: ¡he desfragmentado el archivo personalmente con contig!

+1

Considerar la memoria de conmutación [archivos mapeados] (http://msdn.microsoft.com/en-us/library/dd997372.aspx). – gor

+0

¿Quiere decir que debería crear referencias desde Win32 API o utilizar .NET4? En el primer caso, será mejor crear todo el framework en C/C++ (¡y realmente estoy considerando esta posibilidad!); en este último también debería actualizar a VS2010 o usar SharpDevelop: ¡prefiero usar lo que tengo! – Atropo

+0

Podría ser un problema de almacenamiento en el sistema operativo, no puedo replicar las lecturas en Win7 x64 y .Net 4.0. (Además, utilice bloques 'using', no quiero llorar hoy) – user7116

Respuesta

1

Creo que esto tiene que ver con FileStream.Write/Read y un límite de 2 GB. ¿Estás ejecutando esto en un proceso de 32 bits? No pude encontrar ninguna documentación específica sobre esto, pero aquí hay una pregunta MSDN forum que suena igual. Podría intentar ejecutar esto en un proceso de 64 bits.

Sin embargo, estoy de acuerdo en que utilizar un archivo mapeado en memoria puede ser una mejor opción.

+0

¡Estoy en un sistema Win7 de 64 bits! Sin embargo, no creo que sea un problema de FileStream. Escrito: ¡Lo he depurado (después de una descompilación de mscorlib)! – Atropo

+0

Eso es muy posible. Tengo entendido que .NET aún está limitado a procesos de 32 bits o al límite de 2 GB de memoria. Pero no estás asignando más de 2 GB, así que dudo que ese sea el problema. –

+0

¿La aplicación .net apunta a "Cualquier CPU" o x86? –

1

Encontré esto de MSDN. ¿Podría estar relacionado? Me parece que cada archivo tiene un puntero compartido globalmente.

Cuando un objeto FileStream no tiene una retención exclusiva en su identificador, otro subproceso podría tener acceso al identificador de archivo al mismo tiempo y cambiar la posición del puntero de archivo del sistema operativo asociado con el identificador de archivo. En este caso, la posición almacenada en caché en el objeto FileStream y los datos en caché en el búfer podrían verse comprometidos. El objeto FileStream realiza comprobaciones de forma rutinaria en los métodos que acceden al almacenamiento intermedio almacenado en caché para garantizar que la posición del control del sistema operativo sea la misma que la posición almacenada en caché utilizada por el objeto FileStream.

http://msdn.microsoft.com/en-us/library/system.io.filestream.aspx

+0

Según la documentación, parece ser suficiente usar 'FileOptions.WriteThrough' para deshabilitar cada caché entre' FileStream.Write' y el disco; pero todavía observo la presencia de 'ReadFile' durante las pruebas. – Atropo