From c2b41c17b8a48d71667cd26efdd36fee88a6aa99 Mon Sep 17 00:00:00 2001 From: jorgenherje Date: Tue, 19 Mar 2024 09:45:31 +0100 Subject: [PATCH] Add elapsed time info for responses - Total time - Custom named events with elapsed time (map) ddd --- .../GrpcProtos/GridGeometryExtraction.proto | 10 +- ..._geometry_extraction_cut_along_polyline.py | 11 +- ...on_cut_along_polyline_MULTIPLE_REQUESTS.py | 2 +- ...action_cut_along_polyline_TEST_RESPONSE.py | 2 +- ...id_geometry_extraction_get_grid_surface.py | 33 +++-- ...tion_get_grid_surface_MULTIPLE_REQUESTS.py | 2 +- .../RiaGrpcGridGeometryExtractionService.cpp | 127 +++++++++++++++--- .../RiaGrpcGridGeometryExtractionService.h | 19 ++- 8 files changed, 172 insertions(+), 34 deletions(-) diff --git a/GrpcInterface/GrpcProtos/GridGeometryExtraction.proto b/GrpcInterface/GrpcProtos/GridGeometryExtraction.proto index 6b795c8d5d..4b6631b926 100644 --- a/GrpcInterface/GrpcProtos/GridGeometryExtraction.proto +++ b/GrpcInterface/GrpcProtos/GridGeometryExtraction.proto @@ -11,6 +11,12 @@ service GridGeometryExtraction rpc CutAlongPolyline(CutAlongPolylineRequest) returns (CutAlongPolylineResponse); } +message TimeElapsedInfo +{ + fixed32 totalTimeElapsedMs = 1; // Total time elapsed for the entire request + map namedEventsAndTimeElapsedMs = 2; // Time elapsed for each custom named event +} + message IJKIndexFilter { int32 iMin = 1; @@ -49,6 +55,7 @@ message GetGridSurfaceResponse repeated fixed32 sourceCellIndicesArr = 3; // The originating cell index per quad, longnumQuads long Vec3i gridDimensions = 5; Vec3d originUtm = 6; + TimeElapsedInfo timeElapsedInfo = 7; } message CutAlongPolylineRequest @@ -80,6 +87,7 @@ message CutAlongPolylineResponse { repeated FenceMeshSection fenceMeshSections = 1; PolylineTestResponse polylineTestResponse = 2; - Vec3i gridDimensions = 3; + TimeElapsedInfo timeElapsedInfo = 3; + Vec3i gridDimensions = 4; } diff --git a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline.py b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline.py index d072799436..c79d63a5e8 100644 --- a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline.py +++ b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline.py @@ -15,7 +15,7 @@ grid_file_name = "MOCKED_TEST_GRID" grid_file_name = ( - "D:\\Git\\resinsight-tutorials\\model-data\\norne\\NORNE_ATW2013_RFTPLT_V2.EGRID" + "D:/Git/resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" ) # Test polylines @@ -53,6 +53,11 @@ grid_geometry_extraction_stub.CutAlongPolyline(cut_along_polyline_request) ) +total_time_elapsed = cut_along_polyline_response.timeElapsedInfo.totalTimeElapsedMs +named_events_and_time_elapsed = ( + cut_along_polyline_response.timeElapsedInfo.namedEventsAndTimeElapsedMs +) + fence_mesh_sections = cut_along_polyline_response.fenceMeshSections print(f"Number of fence mesh sections: {len(fence_mesh_sections)}") @@ -201,5 +206,9 @@ # f"Grid dimensions [I, J, K]: [{grid_dimensions.dimensions.i}, {grid_dimensions.dimensions.j}, {grid_dimensions.dimensions.k}]" # ) print(fig.data) +print(f"Total time elapsed: {total_time_elapsed} ms") +# print(f"Time elapsed per event [ms]: {named_events_and_time_elapsed}") +for message, time_elapsed in named_events_and_time_elapsed.items(): + print(f"{message}: {time_elapsed}") fig.show() diff --git a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_MULTIPLE_REQUESTS.py b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_MULTIPLE_REQUESTS.py index 4818e9d843..0a7398ecbe 100644 --- a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_MULTIPLE_REQUESTS.py +++ b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_MULTIPLE_REQUESTS.py @@ -13,7 +13,7 @@ # grid_file_name = "MOCKED_TEST_GRID" grid_file_name = ( - "D:\\Git\\resinsight-tutorials\\model-data\\norne\\NORNE_ATW2013_RFTPLT_V2.EGRID" + "D:/Git/resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" ) # Test polylines diff --git a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_TEST_RESPONSE.py b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_TEST_RESPONSE.py index 69b677f7a3..84da2874fa 100644 --- a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_TEST_RESPONSE.py +++ b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_cut_along_polyline_TEST_RESPONSE.py @@ -15,7 +15,7 @@ grid_file_name = "MOCKED_TEST_GRID" grid_file_name = ( - "D:\\Git\\resinsight-tutorials\\model-data\\norne\\NORNE_ATW2013_RFTPLT_V2.EGRID" + "D:/Git/resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" ) # Test polylines diff --git a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface.py b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface.py index 873515d1d8..01939249ca 100644 --- a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface.py +++ b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface.py @@ -6,18 +6,22 @@ from rips.generated.GridGeometryExtraction_pb2_grpc import * from rips.generated.GridGeometryExtraction_pb2 import * +# from ..instance import * +# from ..generated.GridGeometryExtraction_pb2_grpc import * +# from ..generated.GridGeometryExtraction_pb2 import * + rips_instance = Instance.find() grid_geometry_extraction_stub = GridGeometryExtractionStub(rips_instance.channel) -# grid_file_name = "MOCKED_TEST_GRID" grid_file_name = ( - "D:\\Git\\resinsight-tutorials\\model-data\\norne\\NORNE_ATW2013_RFTPLT_V2.EGRID" + "D:/Git/resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" ) +# grid_file_name = "MOCKED_TEST_GRID" -# ijk_index_filter = GridGeometryExtraction__pb2.IJKIndexFilter( -# iMin=0, iMax=1, jMin=1, jMax=3, kMin=3, kMax=3 -# ) -ijk_index_filter = None +ijk_index_filter = GridGeometryExtraction__pb2.IJKIndexFilter( + iMin=-1, iMax=-1, jMin=-1, jMax=-1, kMin=-1, kMax=-1 +) +# ijk_index_filter = None get_grid_surface_request = GridGeometryExtraction__pb2.GetGridSurfaceRequest( gridFilename=grid_file_name, @@ -29,6 +33,11 @@ grid_geometry_extraction_stub.GetGridSurface(get_grid_surface_request) ) +total_time_elapsed = get_grid_surface_response.timeElapsedInfo.totalTimeElapsedMs +named_events_and_time_elapsed = ( + get_grid_surface_response.timeElapsedInfo.namedEventsAndTimeElapsedMs +) + vertex_array = get_grid_surface_response.vertexArray quad_indices_array = get_grid_surface_response.quadIndicesArr origin_utm = get_grid_surface_response.originUtm @@ -44,9 +53,9 @@ y_array = [] z_array = [] for i in range(0, len(vertex_array), num_vertex_coords): - x_array.append(vertex_array[i + 0] + origin_utm.x) - y_array.append(vertex_array[i + 1] + origin_utm.y) - z_array.append(vertex_array[i + 2] + origin_utm.z) + x_array.append(vertex_array[i + 0]) + y_array.append(vertex_array[i + 1]) + z_array.append(vertex_array[i + 2]) # Create triangular mesh i_array = [] @@ -75,6 +84,7 @@ ] ) + print(f"Number of quads: {num_quads}") print(f"Source cell indices array length: {len(source_cell_indices_arr)}") print( @@ -85,4 +95,9 @@ ) print(fig.data) +print(f"Total time elapsed: {total_time_elapsed} ms") +# print(f"Time elapsed per event [ms]: {named_events_and_time_elapsed}") +for message, time_elapsed in named_events_and_time_elapsed.items(): + print(f"{message}: {time_elapsed}") + fig.show() diff --git a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface_MULTIPLE_REQUESTS.py b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface_MULTIPLE_REQUESTS.py index 5260ad486c..4c8c54092d 100644 --- a/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface_MULTIPLE_REQUESTS.py +++ b/GrpcInterface/Python/rips/WebvizPythonExamples/grid_geometry_extraction_get_grid_surface_MULTIPLE_REQUESTS.py @@ -9,7 +9,7 @@ # grid_file_name = "MOCKED_TEST_GRID" grid_file_name = ( - "D:\\Git\\resinsight-tutorials\\model-data\\norne\\NORNE_ATW2013_RFTPLT_V2.EGRID" + "D:/Git/resinsight-tutorials/model-data/norne/NORNE_ATW2013_RFTPLT_V2.EGRID" ) # ijk_index_filter = GridGeometryExtraction__pb2.IJKIndexFilter( diff --git a/GrpcInterface/RiaGrpcGridGeometryExtractionService.cpp b/GrpcInterface/RiaGrpcGridGeometryExtractionService.cpp index 1e97981837..7e0e9dbdbb 100644 --- a/GrpcInterface/RiaGrpcGridGeometryExtractionService.cpp +++ b/GrpcInterface/RiaGrpcGridGeometryExtractionService.cpp @@ -55,6 +55,29 @@ #include "qfileinfo.h" #include "qstring.h" +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +class ElapsedTimeCount +{ +public: + ElapsedTimeCount(): m_start( std::chrono::high_resolution_clock::now() ){}; + ~ElapsedTimeCount() = default; + + long long elapsedMsCount() const + { + const auto end = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast( end - m_start ); + return elapsed.count(); + } + +private: + const std::chrono::high_resolution_clock::time_point m_start; +}; + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- RiaGrpcGridGeometryExtractionService::RiaGrpcGridGeometryExtractionService() { } @@ -66,6 +89,9 @@ grpc::Status RiaGrpcGridGeometryExtractionService::GetGridSurface( grpc::ServerC const rips::GetGridSurfaceRequest* request, rips::GetGridSurfaceResponse* response ) { + m_elapsedTimeInfo.reset(); + auto totalTimeCount = ElapsedTimeCount(); + // Initialize pointers grpc::Status status = initializeApplicationAndEclipseCaseFromAbsoluteFilePath( request->gridfilename() ); if ( status.error_code() != grpc::StatusCode::OK ) @@ -90,33 +116,42 @@ grpc::Status RiaGrpcGridGeometryExtractionService::GetGridSurface( grpc::ServerC } } - // Ensure static geometry parts created for grid part manager in view + // Configure eclipse view + // - Ensure static geometry parts created for grid part manager in view + // - Initialize grid geometry generator + auto createGridGeometryPartsTimeCount = ElapsedTimeCount(); eclipseView->createGridGeometryParts(); + m_elapsedTimeInfo.elapsedTimePerEventMs["CreateGridGeometryParts"] = + static_cast( createGridGeometryPartsTimeCount.elapsedMsCount() ); - // Initialize grid geometry generator const bool useOpenMP = false; - auto gridGeometryGenerator = cvf::StructGridGeometryGenerator( m_eclipseCase->mainGrid(), useOpenMP ); + auto gridGeometryGenerator = cvf::StructGridGeometryGenerator( eclipseView->mainGrid(), useOpenMP ); status = initializeGridGeometryGeneratorWithEclipseViewCellVisibility( gridGeometryGenerator, eclipseView ); if ( status.error_code() != grpc::StatusCode::OK ) { return status; } - auto* gridSurfaceVertices = gridGeometryGenerator.getOrCreateVertices(); + // Create grid surface vertices + auto createVerticesTimeCount = ElapsedTimeCount(); + auto* gridSurfaceVertices = gridGeometryGenerator.getOrCreateVertices(); if ( gridSurfaceVertices == nullptr ) { return grpc::Status( grpc::StatusCode::NOT_FOUND, "No grid vertices found" ); } + m_elapsedTimeInfo.elapsedTimePerEventMs["CreateGridSurfaceVertices"] = + static_cast( createVerticesTimeCount.elapsedMsCount() ); // Set vertex_array and quadindicesarr response - for ( int i = 0; i < gridSurfaceVertices->size(); ++i ) + auto fillResponseTimeCount = ElapsedTimeCount(); + for ( size_t i = 0; i < gridSurfaceVertices->size(); ++i ) { const auto& vertex = gridSurfaceVertices->get( i ); response->add_vertexarray( vertex.x() ); response->add_vertexarray( vertex.y() ); response->add_vertexarray( vertex.z() ); - response->add_quadindicesarr( i ); + response->add_quadindicesarr( static_cast( i )); } // Origin in utm is the offset @@ -143,20 +178,34 @@ grpc::Status RiaGrpcGridGeometryExtractionService::GetGridSurface( grpc::ServerC } // Set grid dimensions - const auto countI = m_eclipseCase->mainGrid()->cellCountI(); - const auto countJ = m_eclipseCase->mainGrid()->cellCountJ(); - const auto countK = m_eclipseCase->mainGrid()->cellCountK(); rips::Vec3i* dimensions = new rips::Vec3i; - dimensions->set_i( countI ); - dimensions->set_j( countJ ); - dimensions->set_k( countK ); + dimensions->set_i( m_eclipseCase->mainGrid()->cellCountK() ); + dimensions->set_j( m_eclipseCase->mainGrid()->cellCountK() ); + dimensions->set_k( m_eclipseCase->mainGrid()->cellCountK() ); response->set_allocated_griddimensions( dimensions ); + m_elapsedTimeInfo.elapsedTimePerEventMs["FillResponse"] = + static_cast( fillResponseTimeCount.elapsedMsCount() ); + // Clear existing view tearDownExistingViewsInEclipseCase(); eclipseView = nullptr; delete eclipseView; + // Fill elapsed time info + m_elapsedTimeInfo.totalTimeElapsedMs = static_cast( totalTimeCount.elapsedMsCount() ); + + // Add elapsed time info to response + rips::TimeElapsedInfo* elapsedTimeInfo = new rips::TimeElapsedInfo; + elapsedTimeInfo->set_totaltimeelapsedms( m_elapsedTimeInfo.totalTimeElapsedMs ); + for ( const auto& event : m_elapsedTimeInfo.elapsedTimePerEventMs ) + { + const auto& message = event.first; + const auto& timeElapsed = event.second; + ( *elapsedTimeInfo->mutable_namedeventsandtimeelapsedms() )[message] = timeElapsed; + } + response->set_allocated_timeelapsedinfo( elapsedTimeInfo ); + return grpc::Status::OK; } @@ -167,6 +216,9 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve const rips::CutAlongPolylineRequest* request, rips::CutAlongPolylineResponse* response ) { + m_elapsedTimeInfo.reset(); + auto totalTimeCount = ElapsedTimeCount(); + // Initialize pointers grpc::Status status = initializeApplicationAndEclipseCaseFromAbsoluteFilePath( request->gridfilename() ); if ( status.error_code() != grpc::StatusCode::OK ) @@ -188,7 +240,11 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve return grpc::Status( grpc::StatusCode::NOT_FOUND, "No eclipse view found" ); } eclipseView->setShowInactiveCells( true ); + + auto createGridGeometryPartsTimeCount = ElapsedTimeCount(); eclipseView->createGridGeometryParts(); + m_elapsedTimeInfo.elapsedTimePerEventMs["CreateGridGeometryParts"] = + static_cast( createGridGeometryPartsTimeCount.elapsedMsCount() ); // Convert polyline to vector of cvf::Vec3d std::vector polylineUtmXy; @@ -213,13 +269,17 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve eclipseView->calculateCurrentTotalCellVisibility( &visibleCells, firstTimeStep ); // Generate intersection + auto generateIntersectionTimeCount = ElapsedTimeCount(); polylineIntersectionGenerator.generateIntersectionGeometry( &visibleCells ); if ( !polylineIntersectionGenerator.isAnyGeometryPresent() ) { return grpc::Status( grpc::StatusCode::INVALID_ARGUMENT, "No intersection geometry present" ); } + m_elapsedTimeInfo.elapsedTimePerEventMs["GenerateIntersection"] = + static_cast( generateIntersectionTimeCount.elapsedMsCount() ); - // Add fence mesh sections + // Add fence mesh sections to response + auto fillResponseTimeCount = ElapsedTimeCount(); const auto& polylineSegmentsMeshData = polylineIntersectionGenerator.polylineSegmentsMeshData(); for ( const auto& segment : polylineSegmentsMeshData ) { @@ -262,9 +322,20 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve } } + // Add grid dimensions + rips::Vec3i* dimensions = new rips::Vec3i; + dimensions->set_i( m_eclipseCase->mainGrid()->cellCountI() ); + dimensions->set_j( m_eclipseCase->mainGrid()->cellCountJ() ); + dimensions->set_k( m_eclipseCase->mainGrid()->cellCountK() ); + response->set_allocated_griddimensions( dimensions ); + + m_elapsedTimeInfo.elapsedTimePerEventMs["FillResponse"] = + static_cast( fillResponseTimeCount.elapsedMsCount() ); + // Add temporary test response { - rips::PolylineTestResponse* polylineTestResponse = new rips::PolylineTestResponse; + auto fillTestResponseTimeCount = ElapsedTimeCount(); + rips::PolylineTestResponse* polylineTestResponse = new rips::PolylineTestResponse; // Polygon vertices const auto& polygonVertices = polylineIntersectionGenerator.polygonVxes(); @@ -272,7 +343,7 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve { return grpc::Status( grpc::StatusCode::NOT_FOUND, "No polygon vertices found for polyline" ); } - for ( int i = 0; i < polygonVertices->size(); ++i ) + for ( size_t i = 0; i < polygonVertices->size(); ++i ) { const auto& vertex = polygonVertices->get( i ); polylineTestResponse->add_polygonvertexarray( vertex.x() ); @@ -295,6 +366,8 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve } response->set_allocated_polylinetestresponse( polylineTestResponse ); + m_elapsedTimeInfo.elapsedTimePerEventMs["FillResponse"] = + static_cast( fillTestResponseTimeCount.elapsedMsCount() ); } // Clear existing view @@ -302,6 +375,20 @@ grpc::Status RiaGrpcGridGeometryExtractionService::CutAlongPolyline( grpc::Serve eclipseView = nullptr; delete eclipseView; + // Fill elapsed time info + m_elapsedTimeInfo.totalTimeElapsedMs = static_cast( totalTimeCount.elapsedMsCount() ); + + // Add elapsed time info to response + rips::TimeElapsedInfo* elapsedTimeInfo = new rips::TimeElapsedInfo; + elapsedTimeInfo->set_totaltimeelapsedms( m_elapsedTimeInfo.totalTimeElapsedMs ); + for ( const auto& event : m_elapsedTimeInfo.elapsedTimePerEventMs ) + { + const auto& message = event.first; + const auto& timeElapsed = event.second; + ( *elapsedTimeInfo->mutable_namedeventsandtimeelapsedms() )[message] = timeElapsed; + } + response->set_allocated_timeelapsedinfo( elapsedTimeInfo ); + return grpc::Status::OK; } @@ -431,7 +518,7 @@ grpc::Status RiaGrpcGridGeometryExtractionService::initializeGridGeometryGenerat return grpc::Status( grpc::StatusCode::NOT_FOUND, "Uninitialized eclipse view provided" ); } - auto* mainGrid = m_eclipseCase->mainGrid(); + auto* mainGrid = view->mainGrid(); if ( mainGrid == nullptr ) { return grpc::Status( grpc::StatusCode::NOT_FOUND, "No main grid found for eclipse view" ); @@ -513,7 +600,10 @@ grpc::Status else { // Load case from file name - auto status = loadGridGeometryFromAbsoluteFilePath( filePath ); + auto loadCaseFromFileNameTimeCount = ElapsedTimeCount(); + auto status = loadGridGeometryFromAbsoluteFilePath( filePath ); + m_elapsedTimeInfo.elapsedTimePerEventMs["LoadGridFromFilePath"] = + static_cast( loadCaseFromFileNameTimeCount.elapsedMsCount() ); if ( status.error_code() != grpc::StatusCode::OK ) return status; } @@ -547,7 +637,10 @@ grpc::Status } if ( m_eclipseCase->views().empty() ) { + auto createAndAddViewTimeCount = ElapsedTimeCount(); m_eclipseCase->createAndAddReservoirView(); + m_elapsedTimeInfo.elapsedTimePerEventMs["CreateAndAddView"] = + static_cast( createAndAddViewTimeCount.elapsedMsCount() ); } return grpc::Status::OK; diff --git a/GrpcInterface/RiaGrpcGridGeometryExtractionService.h b/GrpcInterface/RiaGrpcGridGeometryExtractionService.h index 20ce4f3ce5..04a6dcf8ab 100644 --- a/GrpcInterface/RiaGrpcGridGeometryExtractionService.h +++ b/GrpcInterface/RiaGrpcGridGeometryExtractionService.h @@ -73,8 +73,21 @@ class RiaGrpcGridGeometryExtractionService final : public rips::GridGeometryExtr grpc::Status initializeGridGeometryGeneratorWithEclipseViewCellVisibility( cvf::StructGridGeometryGenerator& generator, RimEclipseView* view ); - RiaApplication* m_application = nullptr; - RimEclipseCase* m_eclipseCase = nullptr; - + RiaApplication* m_application = nullptr; + RimEclipseCase* m_eclipseCase = nullptr; std::unique_ptr m_faceVisibilityFilter = nullptr; + + struct ElapsedTimeInfo + { + std::uint32_t totalTimeElapsedMs = 0; // Total time elapsed for entire request + std::map elapsedTimePerEventMs; // Time elapsed for each custom named event + + void reset() + { + totalTimeElapsedMs = 0; + elapsedTimePerEventMs.clear(); + } + }; + + ElapsedTimeInfo m_elapsedTimeInfo; };