c++ - para - introducción a android studio incluye proyectos reales y el código fuente pdf
Archivos que se cargan más despacio en la segunda ejecución de la aplicación, con el código de repro (2)
Mi mejor suposición es que Windows Indexer está creando un bloqueo de los archivos justo después de ser modificado como dijo Damon
NtfsFindPrefix asegura una buena adquisición y liberación de FCB, pero a menos que el código sea incorrecto (en cuyo caso debería fallar), ¿por qué sería más lento en la segunda ejecución? El Explorador de Windows o el Servicio de Index Server realizan algún tipo de "mierda inteligente" después de haber modificado un archivo en esa carpeta, como por ejemplo, escanear todos los archivos para ajustar el tipo de carpeta especializada y presionar un candado para hacerlo. ¿Has probado lo que sucede si esperas unos minutos sin hacer nada antes de ejecutarlo por segunda vez, o si matas a Explorer y al indexador?
Sugeriría crear una carpeta usada específicamente para guardar datos para cualquier programa que escriba:
Ejemplo
C: / Users /% yourusername% / DataAnalysis
Use esta carpeta para guardar cualquier archivo de datos al que acceda su software original. Después de crear la carpeta, debe deshabilitar la indexación.
Creo que en el menú de propiedades de la carpeta hay una opción para deshabilitar la indexación de la carpeta. Si no, siga las instrucciones here para deshabilitar la indexación de esa carpeta y evitar problemas.
La página le indicará que visite el Panel de control y visite las Opciones de indización . Allí selecciona Modificar y viaja a través de su sistema de archivos y anula la selección de la carpeta que acaba de crear para evitar indexar su contenido.
¡Déjame saber si funciona!
Descripción de la aplicación
Tengo una herramienta de procesamiento de datos fuera de línea. Esta herramienta carga cientos de miles de archivos. Para cada uno realiza algunos cálculos y cuando termina escribe un único archivo de índice. Es todo C ++ (todo IO se realiza a través de objetos / funciones de biblioteca estándar) y se está compilando con la orientación de Visual Studio 2013 amd64.
Actuación
Mi conjunto de datos de prueba tiene 115,757 archivos que deben procesarse. Los archivos tienen un tamaño total de 731 MB y el tamaño medio del archivo es de 6 KB.
- Primera ejecución: 12 segundos
- Segunda ejecución: ~ 18 minutos
¡Eso es 90 veces más lento! La segunda ejecución se extrapola desde un minuto de tiempo de ejecución. Todas las carreras posteriores, como he experimentado hasta ahora, son igualmente lentas.
¡Sorpresa!
Si cambio el nombre de la carpeta con los archivos y luego le cambio el nombre a lo que originalmente era, la próxima vez que ejecute la aplicación volverá a funcionar rápidamente.
Es la misma aplicación, máquina y datos de origen. La única diferencia es que una carpeta se renombró temporalmente.
Hasta ahora puedo reproducir esto el 100% del tiempo.
Perfilado
Naturalmente, el siguiente paso fue perfilar. Hice un perfil de la ejecución rápida y la ejecución lenta y comparé los puntos conflictivos. En la versión lenta, aproximadamente el 86% de la aplicación se gastó en una función llamada NtfsFindPrefix
. La versión rápida gasta alrededor del 0,4% de su tiempo aquí. Esta es la pila de llamadas:
Ntfs.sys!NtfsFindPrefix<itself>
Ntfs.sys!NtfsFindPrefix
Ntfs.sys!NtfsFindStartingNode
Ntfs.sys!NtfsCommonCreate
Ntfs.sys!NtfsCommonCreateCallout
ntoskrnl.exe!KySwitchKernelStackCallout
ntoskrnl.exe!KiSwitchKernelStackContinue
ntoskrnl.exe!KeExpandKernelStackAndCalloutEx
Ntfs.sys!NtfsCommonCreateOnNewStack
Ntfs.sys!NtfsFsdCreate
fltmgr.sys!FltpLegacyProcessingAfterPreCallbacksCompleted
fltmgr.sys!FltpCreate
ntoskrnl.exe!IopParseDevice
ntoskrnl.exe!ObpLookupObjectName
ntoskrnl.exe!ObOpenObjectByName
ntoskrnl.exe!NtQueryAttributesFile
ntoskrnl.exe!KiSystemServiceCopyEnd
ntdll.dll!NtQueryAttributesFile
KernelBase.dll!GetFileAttributesW
DataGenerator.exe!boost::filesystem::detail::status
La llamada de refuerzo en cuestión es una llamada exists
. Probará la versión comprimida de un archivo, no lo encontrará, y luego probará la descomprimida y la encontrará.
La creación de perfiles también mostró que el disco no fue golpeado por ninguna de las ejecuciones de la aplicación, sin embargo, se esperaba que File IO fuera alto. Creo que esto indica que los archivos ya fueron paginados a la memoria.
El archivo IO también mostró que la duración de los eventos "Crear" archivo era en promedio MUCHO más alta en la versión lenta. 26 us vs 11704 nosotros .
Máquina
- Samsung SSD 830 Series
- Intel i7 860
- Windows 7 de 64 bits
- Sistema de archivos NTFS.
- 32GB Ram
Resumen
- En la segunda ejecución, las llamadas a
NtfsFindPrefix
tardan mucho más. - Esta es una función en el controlador NTFS.
- El disco no recibió ningún golpe en ninguno de los perfiles, los archivos se publicaron desde páginas en la memoria.
- Una operación de cambio de nombre parece ser suficiente para evitar que este problema ocurra en la próxima ejecución.
Pregunta
Ahora que la información de fondo está fuera del camino, ¿Alguien reconoce lo que está pasando y sabe cómo solucionarlo?
Parece que podría solucionarlo cambiando el nombre de la carpeta, pero parece ... sucio. Además, no estoy seguro de por qué eso funciona.
¿El cambio de nombre invalida las páginas en la memoria y hace que se actualicen antes de la próxima ejecución? ¿Es esto un error en el controlador NTFS?
¡Gracias por leer!
¡¡Actualizar!!
Después de un poco más de perfilado, parece que la parte que se está ejecutando más lentamente está probando para ver si existe un archivo comprimido inexistente. Si elimino esta prueba todo parece volverse más rápido de nuevo.
También he logrado reproducir este problema en una pequeña aplicación de C ++ para que todos puedan verlo. Tenga en cuenta que El código de muestra creará 100k 6KB archivos en su máquina en el directorio actual. ¿Alguien más puede reproducirlo?
// using VS tr2 could replace with boost::filesystem
#include <filesystem>
namespace fs = std::tr2::sys;
//namespace fs = boost::filesystem;
#include <iostream>
#include <string>
#include <chrono>
#include <fstream>
void createFiles( fs::path outDir )
{
// create 100k 6KB files with junk data in them. It doesn''t matter that they are all the same.
fs::create_directory( outDir );
char buf[6144];
for( int i = 0; i < 100000; ++i )
{
std::ofstream fout( outDir / fs::path( std::to_string( i ) ), std::ios::binary );
fout.write( buf, 6144 );
}
fs::rename( outDir, fs::path( outDir.string() + "_tmp" ) );
fs::rename( fs::path( outDir.string() + "_tmp" ), outDir );
}
int main( int argc, const char* argv[] )
{
fs::path outDir = "out";
if( !fs::exists( outDir ) )
createFiles( outDir );
auto start = std::chrono::high_resolution_clock::now();
int counter = 0;
for( fs::recursive_directory_iterator i( outDir ), iEnd; i != iEnd; ++i )
{
// test the non existent one, then the other
if( !fs::exists( fs::path( i->path().string() + "z" ) ) && fs::exists( i->path() ) )
counter += 1;
if( counter % 100 == 0 )
std::cout << counter << std::endl;
}
std::cout << counter << std::endl;
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration< double, std::milli > s( end - start );
std::cout << "Time Passed: " << s.count() << "ms" << std::endl;
return 0;
}
Actualización 2
He registrado un problema con MS here . Con suerte, pueden ayudar a arrojar algo de luz sobre el tema.
Si el cuello de botella está en la llamada existente, entonces probablemente obtendrá un mejor rendimiento si lee la lista de archivos en una estructura de datos en memoria y prueba la existencia de archivos en contra de su estructura de datos.
Use FindFirstFileEx / FindNextFile para obtener los nombres de todos los archivos en su carpeta de trabajo. Cargue los resultados en un estándar :: vector (o su contenedor de elección). Ordenar. Use std :: binary_search para verificar si existe un archivo específico.
He escrito muchas herramientas que funcionan con una gran cantidad de archivos en una sola carpeta y, según mi experiencia, FindFirstFileEx / FindNextFile es la mejor opción para estos escenarios.