int main(int argc, char* argv[]) { osmscout::CmdLineParser argParser("PerformanceTest", argc,argv); Arguments args; osmscout::DatabaseParameter databaseParameter; argParser.AddOption(osmscout::CmdLineFlag([&args](const bool& value) { args.help=value; }), std::vector<std::string>{"h","help"}, "Display help", true); argParser.AddOption(osmscout::CmdLineFlag([&args](const bool& value) { args.debug=value; }), "debug", "Enable debug output", false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.startZoom=osmscout::MagnificationLevel(value); }), "start-zoom", "Start zoom, default: " + std::to_string(args.startZoom.Get()), false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.endZoom=osmscout::MagnificationLevel(value); }), "end-zoom", "End zoom, default: " + std::to_string(args.endZoom.Get()), false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.tileDimension=std::make_tuple(value, std::get<1>(args.tileDimension)); }), "tile-width", "Tile width, default: " + std::to_string(std::get<0>(args.tileDimension)), false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.tileDimension=std::make_tuple(std::get<0>(args.tileDimension), value); }), "tile-height", "Tile height, default: " + std::to_string(std::get<1>(args.tileDimension)), false); argParser.AddOption(osmscout::CmdLineStringOption([&args](const std::string& value) { args.driver = value; }), "driver", "Rendering driver (cairo|Qt|ag|opengl|noop|none), default: " + args.driver, false); argParser.AddOption(osmscout::CmdLineDoubleOption([&args](const double& value) { if (value > 0) { args.dpi = value; } else { std::cerr << "DPI can't be negative or zero" << std::endl; } }), "dpi", "Rendering DPI, default: " + std::to_string(args.dpi), false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.drawRepeat = value; }), "draw-repeat", "Repeat every draw call, default: " + std::to_string(args.drawRepeat), false); argParser.AddOption(osmscout::CmdLineUIntOption([&args](const unsigned int& value) { args.loadRepeat = value; }), "load-repeat", "Repeat every load call, default: " + std::to_string(args.loadRepeat), false); argParser.AddOption(osmscout::CmdLineFlag([&args](const bool& value) { args.flushCache=value; }), "flush-cache", "Flush data caches after each data load, default: " + std::to_string(args.flushCache), false); argParser.AddOption(osmscout::CmdLineFlag([&args](const bool& value) { args.flushDiskCache=value; }), "flush-disk", "Flush system disk caches after each data load, default: " + std::to_string(args.flushDiskCache) + " (It work just on Linux with admin rights.)", false); argParser.AddOption(osmscout::CmdLineUIntOption([&databaseParameter](const unsigned int& value) { databaseParameter.SetNodeDataCacheSize(value); }), "cache-nodes", "Cache size for nodes, default: " + std::to_string(databaseParameter.GetNodeDataCacheSize()), false); argParser.AddOption(osmscout::CmdLineUIntOption([&databaseParameter](const unsigned int& value) { databaseParameter.SetWayDataCacheSize(value); }), "cache-ways", "Cache size for ways, default: " + std::to_string(databaseParameter.GetWayDataCacheSize()), false); argParser.AddOption(osmscout::CmdLineUIntOption([&databaseParameter](const unsigned int& value) { databaseParameter.SetAreaDataCacheSize(value); }), "cache-areas", "Cache size for areas, default: " + std::to_string(databaseParameter.GetAreaDataCacheSize()), false); #if defined(HAVE_LIB_GPERFTOOLS) argParser.AddOption(osmscout::CmdLineStringOption([&args](const std::string& value) { args.heapProfilePrefix = value; args.heapProfile = !args.heapProfilePrefix.empty(); }), "heap-profile", "GPerf heap profile prefix, profiler is disabled by default", false); #endif argParser.AddPositional(osmscout::CmdLineStringOption([&args](const std::string& value) { args.databaseDirectory=value; }), "databaseDir", "Database directory"); argParser.AddPositional(osmscout::CmdLineStringOption([&args](const std::string& value) { args.style=value; }), "stylesheet", "Map stylesheet"); argParser.AddPositional(osmscout::CmdLineGeoCoordOption([&args](const osmscout::GeoCoord& coord) { args.coordTopLeft = coord; }), "lat_top lon_left", "Bounding box top-left coordinate"); argParser.AddPositional(osmscout::CmdLineGeoCoordOption([&args](const osmscout::GeoCoord& coord) { args.coordBottomRight = coord; }), "lat_bottom lon_right", "Bounding box bottom-right coordinate"); osmscout::CmdLineParseResult argResult=argParser.Parse(); if (argResult.HasError()) { std::cerr << "ERROR: " << argResult.GetErrorDescription() << std::endl; std::cout << argParser.GetHelp() << std::endl; return 1; } else if (args.help) { std::cout << argParser.GetHelp() << std::endl; return 0; } osmscout::log.Debug(args.debug); //databaseParameter.SetDebugPerformance(true); osmscout::DatabaseRef database=std::make_shared<osmscout::Database>(databaseParameter); osmscout::MapServiceRef mapService=std::make_shared<osmscout::MapService>(database); if (!database->Open(args.databaseDirectory)) { std::cerr << "Cannot open database" << std::endl; return 1; } osmscout::StyleConfigRef styleConfig=std::make_shared<osmscout::StyleConfig>(database->GetTypeConfig()); if (!styleConfig->Load(args.style)) { std::cerr << "Cannot open style" << std::endl; return 1; } PerformanceTestBackendPtr backendPtr = PrepareBackend(argc, argv, args, styleConfig); if (!backendPtr){ return 1; } #if defined(HAVE_LIB_GPERFTOOLS) if (args.heapProfile){ HeapProfilerStart(args.heapProfilePrefix.c_str()); } #endif osmscout::TileProjection projection; osmscout::MapParameter drawParameter; osmscout::AreaSearchParameter searchParameter; std::list<LevelStats> statistics; // TODO: Use some way to find a valid font on the system (Agg display a ton of messages otherwise) drawParameter.SetFontName("/usr/share/fonts/TTF/DejaVuSans.ttf"); searchParameter.SetUseMultithreading(true); for (osmscout::MagnificationLevel level=osmscout::MagnificationLevel(std::min(args.startZoom,args.endZoom)); level<=osmscout::MagnificationLevel(std::max(args.startZoom,args.endZoom)); level++) { LevelStats stats(level.Get()); osmscout::Magnification magnification(level); osmscout::OSMTileId tileA(osmscout::OSMTileId::GetOSMTile(magnification, osmscout::GeoCoord(args.LatBottom(),args.LonLeft()))); osmscout::OSMTileId tileB(osmscout::OSMTileId::GetOSMTile(magnification, osmscout::GeoCoord(args.LatTop(),args.LonRight()))); osmscout::OSMTileIdBox tileArea(tileA,tileB); std::cout << "----------" << std::endl; std::cout << "Drawing level " << level << ", " << tileArea.GetCount() << " tiles " << tileArea.GetDisplayText() << std::endl; size_t current=1; size_t tileCount=tileArea.GetCount(); size_t delta=tileCount/20; if (delta==0) { delta=1; } for (const auto& tile : tileArea) { osmscout::MapData data; osmscout::OSMTileIdBox tileBox(osmscout::OSMTileId(tile.GetX()-1,tile.GetY()-1), osmscout::OSMTileId(tile.GetX()+1,tile.GetY()+1)); osmscout::GeoBox boundingBox; if ((current % delta)==0) { std::cout << current*100/tileCount << "% " << current; if (stats.tileCount>0) { std::cout << " " << stats.dbTotalTime/stats.tileCount; std::cout << " " << stats.drawTotalTime/stats.tileCount; } std::cout << std::endl; } projection.Set(tile, magnification, args.dpi, args.TileWidth(), args.TileHeight()); projection.GetDimensions(boundingBox); projection.SetLinearInterpolationUsage(level.Get() >= 10); for (size_t i=0; i<args.loadRepeat; i++) { data.nodes.clear(); data.ways.clear(); data.areas.clear(); osmscout::StopClock dbTimer; osmscout::GeoBox dataBoundingBox(tileBox.GetBoundingBox(magnification)); std::list<osmscout::TileRef> tiles; // set cache size almost unlimited, // for better estimate of peak memory usage by tile loading mapService->SetCacheSize(10000000); mapService->LookupTiles(magnification, dataBoundingBox, tiles); mapService->LoadMissingTileData(searchParameter, *styleConfig, tiles); mapService->AddTileDataToMapData(tiles, data); #if defined(HAVE_LIB_GPERFTOOLS) if (args.heapProfile) { std::ostringstream buff; buff << "load-" << level << "-" << tile.GetX() << "-" << tile.GetY(); HeapProfilerDump(buff.str().c_str()); } struct mallinfo alloc_info = tc_mallinfo(); #else #if defined(HAVE_MALLINFO) struct mallinfo alloc_info = mallinfo(); #endif #endif #if defined(HAVE_MALLINFO) || defined(HAVE_LIB_GPERFTOOLS) std::cout << "memory usage: " << formatAlloc(alloc_info.uordblks) << std::endl; stats.allocMax = std::max(stats.allocMax, (double) alloc_info.uordblks); stats.allocSum = stats.allocSum + (double) alloc_info.uordblks; #endif // set cache size back to default mapService->SetCacheSize(25); dbTimer.Stop(); double dbTime = dbTimer.GetMilliseconds(); stats.dbMinTime = std::min(stats.dbMinTime, dbTime); stats.dbMaxTime = std::max(stats.dbMaxTime, dbTime); stats.dbTotalTime += dbTime; if (args.flushCache) { tiles.clear(); // following flush method removes only tiles with use_count() == 1 mapService->FlushTileCache(); // simplest way howto flush database caches is close it and open again database->Close(); if (!database->Open(args.databaseDirectory)) { std::cerr << "Cannot open database" << std::endl; return 1; } } if (args.flushDiskCache) { // Linux specific if (osmscout::ExistsInFilesystem("/proc/sys/vm/drop_caches")){ osmscout::FileWriter f; try { f.Open("/proc/sys/vm/drop_caches"); f.Write(std::string("3")); f.Close(); }catch(const osmscout::IOException &e){ std::cerr << "Can't flush disk cache: " << e.what() << std::endl; } }else{ std::cerr << "Can't flush disk cache, \"/proc/sys/vm/drop_caches\" file don't exists" << std::endl; } } } stats.nodeCount+=data.nodes.size(); stats.wayCount+=data.ways.size(); stats.areaCount+=data.areas.size(); stats.tileCount++; for (size_t i=0; i<args.drawRepeat; i++) { osmscout::StopClock drawTimer; backendPtr->DrawMap(projection, drawParameter, data); drawTimer.Stop(); double drawTime = drawTimer.GetMilliseconds(); stats.drawMinTime = std::min(stats.drawMinTime, drawTime); stats.drawMaxTime = std::max(stats.drawMaxTime, drawTime); stats.drawTotalTime += drawTime; } current++; } statistics.push_back(stats); } #if defined(HAVE_LIB_GPERFTOOLS) if (args.heapProfile){ HeapProfilerStop(); } #endif std::cout << "==========" << std::endl; for (const auto& stats : statistics) { std::cout << "Level: " << stats.level << std::endl; std::cout << "Tiles: " << stats.tileCount << " (load " << args.loadRepeat << "x, drawn " << args.drawRepeat << "x)" << std::endl; #if defined(HAVE_MALLINFO) || defined(HAVE_LIB_GPERFTOOLS) std::cout << " Used memory: "; std::cout << "max: " << formatAlloc(stats.allocMax) << " "; std::cout << "avg: " << formatAlloc(stats.allocSum / (stats.tileCount * args.loadRepeat)) << std::endl; #endif std::cout << " Tot. data : "; std::cout << "nodes: " << stats.nodeCount << " "; std::cout << "way: " << stats.wayCount << " "; std::cout << "areas: " << stats.areaCount << std::endl; if (stats.tileCount>0) { std::cout << " Avg. data : "; std::cout << "nodes: " << stats.nodeCount/stats.tileCount << " "; std::cout << "way: " << stats.wayCount/stats.tileCount << " "; std::cout << "areas: " << stats.areaCount/stats.tileCount << std::endl; } std::cout << " DB : "; std::cout << "total: " << stats.dbTotalTime << " "; std::cout << "min: " << stats.dbMinTime << " "; if (stats.tileCount>0) { std::cout << "avg: " << stats.dbTotalTime/(stats.tileCount * args.loadRepeat) << " "; } std::cout << "max: " << stats.dbMaxTime << " " << std::endl; std::cout << " Map : "; std::cout << "total: " << stats.drawTotalTime << " "; std::cout << "min: " << stats.drawMinTime << " "; if (stats.tileCount>0) { std::cout << "avg: " << stats.drawTotalTime/(stats.tileCount * args.drawRepeat) << " "; } std::cout << "max: " << stats.drawMaxTime << std::endl; } database->Close(); return 0; }
PerformanceTestBackendPtr PrepareBackend(int argc, char* argv[], const Arguments &args, osmscout::StyleConfigRef styleConfig) { if (args.driver=="cairo") { std::cout << "Using driver 'cairo'..." << std::endl; #if defined(HAVE_LIB_OSMSCOUTMAPCAIRO) try{ return std::make_shared<PerformanceTestBackendCairo>(args.TileWidth(),args.TileHeight(),styleConfig); } catch (std::runtime_error &e){ std::cerr << e.what() << std::endl; return nullptr; } #else std::cerr << "Driver 'cairo' is not enabled" << std::endl; return nullptr; #endif } else if (args.driver=="Qt") { std::cout << "Using driver 'Qt'..." << std::endl; #if defined(HAVE_LIB_OSMSCOUTMAPQT) SailfishApp::application(argc, argv); std::cout << "QGuiApplication created..." << std::endl; return std::make_shared<PerformanceTestBackendQt>(argc, argv, args.TileWidth(), args.TileHeight(), styleConfig); #else std::cerr << "Driver 'Qt' is not enabled" << std::endl; return nullptr; #endif } else if (args.driver == "agg") { std::cout << "Using driver 'Agg'..." << std::endl; #if defined(HAVE_LIB_OSMSCOUTMAPAGG) return std::make_shared<PerformanceTestBackendAGG>(args.TileWidth(), args.TileHeight(), styleConfig); #else std::cerr << "Driver 'Agg' is not enabled" << std::endl; return nullptr; #endif } else if (args.driver == "opengl") { std::cout << "Using driver 'OpenGL'..." << std::endl; #if defined(HAVE_LIB_OSMSCOUTMAPOPENGL) try{ return std::make_shared<PerformanceTestBackendOGL>(args.TileWidth(), args.TileHeight(), args.dpi, styleConfig); } catch (std::runtime_error &e){ std::cerr << e.what() << std::endl; return nullptr; } #else std::cerr << "Driver 'OpenGL' is not enabled" << std::endl; return nullptr; #endif } else if (args.driver=="noop") { std::cout << "Using driver 'noop'..." << std::endl; return std::make_shared<PerformanceTestBackendNoOp>(styleConfig); } else if (args.driver=="none") { std::cout << "Using driver 'none'..." << std::endl; return std::make_shared<PerformanceTestBackend>(); } else { std::cerr << "Unsupported driver '" << args.driver << "'" << std::endl; return nullptr; } }