/*========================================================================= Program: Visualization Toolkit Module: TestCellIterators.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkCellIterator.h" #include "vtkCellArray.h" #include "vtkFloatArray.h" #include "vtkGenericCell.h" #include "vtkNew.h" #include "vtkPoints.h" #include "vtkSmartPointer.h" #include "vtkTestUtilities.h" #include "vtkTimerLog.h" #include "vtkUnsignedCharArray.h" #include "vtkUnstructuredGrid.h" #include "vtkUnstructuredGridReader.h" #include #include // Enable/disable code that helps/hinders profiling. #undef PROFILE //#define PROFILE // Enable benchmarks. #undef BENCHMARK //#define BENCHMARK #ifdef BENCHMARK # ifdef PROFILE # define NUM_BENCHMARKS 10 # else // PROFILE # define NUM_BENCHMARKS 100 # endif // PROFILE #endif // BENCHMARK //------------------------------------------------------------------------------ // Compare the cell type, point ids, and points in 'grid' with those returned // in 'iter'. bool testCellIterator(vtkCellIterator *iter, vtkUnstructuredGrid *grid) { vtkIdType cellId = 0; vtkNew cell; iter->InitTraversal(); while (!iter->IsDoneWithTraversal()) { grid->GetCell(cellId, cell); if (iter->GetCellType() != cell->GetCellType()) { cerr << "Type mismatch for cell " << cellId << endl; return false; } vtkIdType numPoints = iter->GetNumberOfPoints(); if (numPoints != cell->GetNumberOfPoints()) { cerr << "Number of points mismatch for cell " << cellId << endl; return false; } for (vtkIdType pointInd = 0; pointInd < numPoints; ++pointInd) { if (iter->GetPointIds()->GetId(pointInd) != cell->PointIds->GetId(pointInd)) { cerr << "Point id mismatch in cell " << cellId << endl; return false; } double iterPoint[3]; double cellPoint[3]; iter->GetPoints()->GetPoint(pointInd, iterPoint); cell->Points->GetPoint(pointInd, cellPoint); if (iterPoint[0] != cellPoint[0] || iterPoint[1] != cellPoint[1] || iterPoint[2] != cellPoint[2] ) { cerr << "Point mismatch in cell " << cellId << endl; return false; } } iter->GoToNextCell(); ++cellId; } // ensure that we checked all of the cells if (cellId != grid->GetNumberOfCells()) { cerr << "Iterator did not cover all cells in the dataset!" << endl; return false; } // cout << "Verified " << cellId << " cells with a " << iter->GetClassName() // << "." << endl; return true; } #define TEST_ITERATOR(iter_, className_) \ if (std::string(#className_) != std::string(iter->GetClassName())) \ { \ cerr << "Unexpected iterator type (expected " #className_ ", got " \ << iter_->GetClassName() << ")" << endl; \ return false; \ } \ \ if (!testCellIterator(iter_, grid)) \ { \ cerr << #className_ << " test failed." << endl; \ return false; \ } \ \ if (!testCellIterator(iter_, grid)) \ { \ cerr << #className_ << " test failed after rewind." << endl; \ return false; \ } \ bool runValidation(vtkUnstructuredGrid *grid) { // vtkDataSetCellIterator: vtkCellIterator *iter = grid->vtkDataSet::NewCellIterator(); TEST_ITERATOR(iter, vtkDataSetCellIterator); iter->Delete(); // vtkPointSetCellIterator: iter = grid->vtkPointSet::NewCellIterator(); TEST_ITERATOR(iter, vtkPointSetCellIterator); iter->Delete(); // vtkUnstructuredGridCellIterator: iter = grid->vtkUnstructuredGrid::NewCellIterator(); TEST_ITERATOR(iter, vtkUnstructuredGridCellIterator); iter->Delete(); return true; } // Benchmarking code follows: #ifdef BENCHMARK // Do-nothing function that ensures arguments passed in will not be compiled // out. Aggressive optimization will otherwise remove portions of the following // loops, throwing off the benchmark results: namespace { std::stringstream _sink; template void useData(const Type& data) { _sink << data; } } // end anon namespace // There are three signatures for each benchmark function: // - double ()(vtkUnstructuredGrid *) // Iterate through cells in an unstructured grid, using raw memory when // possible. // - double ()(vtkUnstructuredGrid *, int) // Iterator through cells in an unstructured grid, using API only // - double ()(vtkCellIterator *) // Iterator through all cells available through the iterator. double benchmarkTypeIteration(vtkUnstructuredGrid *grid) { vtkIdType numCells = grid->GetNumberOfCells(); vtkUnsignedCharArray *types = grid->GetCellTypesArray(); unsigned char *ptr = types->GetPointer(0); unsigned char range[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; vtkNew timer; timer->StartTimer(); for (int i = 0; i < numCells; ++i) { range[0] = std::min(range[0], ptr[i]); range[1] = std::max(range[1], ptr[i]); } timer->StopTimer(); useData(range[0]); useData(range[1]); return timer->GetElapsedTime(); } double benchmarkTypeIteration(vtkUnstructuredGrid *grid, int) { vtkIdType numCells = grid->GetNumberOfCells(); unsigned char tmp; unsigned char range[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; vtkNew timer; timer->StartTimer(); for (int i = 0; i < numCells; ++i) { tmp = static_cast(grid->GetCellType(i)); range[0] = std::min(range[0], tmp); range[1] = std::max(range[1], tmp); } timer->StopTimer(); useData(range[0]); useData(range[1]); return timer->GetElapsedTime(); } double benchmarkTypeIteration(vtkCellIterator *iter) { int range[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; int tmp; vtkNew timer; timer->StartTimer(); for (iter->InitTraversal(); iter->IsDoneWithTraversal(); iter->GoToNextCell()) { tmp = iter->GetCellType(); range[0] = std::min(range[0], tmp); range[1] = std::max(range[1], tmp); } timer->StopTimer(); useData(range[0]); useData(range[1]); return timer->GetElapsedTime(); } double benchmarkPointIdIteration(vtkUnstructuredGrid *grid) { vtkCellArray *cellArray = grid->GetCells(); vtkIdType numCells = cellArray->GetNumberOfCells(); vtkIdType *cellPtr = cellArray-; vtkIdType range[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType cellSize; vtkNew timer; timer->StartTimer(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { cellSize = *(cellPtr++); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { range[0] = std::min(range[0], cellPtr[pointIdx]); range[1] = std::max(range[1], cellPtr[pointIdx]); } cellPtr += cellSize; } timer->StopTimer(); useData(range[0]); useData(range[1]); return timer->GetElapsedTime(); } double benchmarkPointIdIteration(vtkUnstructuredGrid *grid, int) { vtkIdType numCells = grid->GetNumberOfCells(); vtkIdType range[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType cellSize; vtkIdList *cellPointIds = vtkIdList::New(); vtkIdType *cellPtr; vtkNew timer; timer->StartTimer(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { grid->GetCellPoints(cellId, cellPointIds); cellSize = cellPointIds->GetNumberOfIds(); cellPtr = cellPointIds->GetPointer(0); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { range[0] = std::min(range[0], cellPtr[pointIdx]); range[1] = std::max(range[1], cellPtr[pointIdx]); } } timer->StopTimer(); useData(range[0]); useData(range[1]); cellPointIds->Delete(); return timer->GetElapsedTime(); } double benchmarkPointIdIteration(vtkCellIterator *iter) { vtkIdType range[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType *cellPtr; vtkIdType *cellEnd; vtkNew timer; timer->StartTimer(); for (iter->InitTraversal(); iter->IsDoneWithTraversal(); iter->GoToNextCell()) { cellPtr = iter->GetPointIds()->GetPointer(0); cellEnd = cellPtr + iter->GetNumberOfPoints(); while (cellPtr != cellEnd) { range[0] = std::min(range[0], *cellPtr); range[1] = std::max(range[1], *cellPtr); ++cellPtr; } } timer->StopTimer(); useData(range[0]); useData(range[1]); return timer->GetElapsedTime(); } double benchmarkPointsIteration(vtkUnstructuredGrid *grid) { vtkCellArray *cellArray = grid->GetCells(); const vtkIdType numCells = cellArray->GetNumberOfCells(); vtkIdType *cellPtr = cellArray-; vtkIdType cellSize; vtkPoints *points = grid->GetPoints(); vtkFloatArray *pointDataArray = vtkArrayDownCast(points->GetData()); if (!pointDataArray) { return -1.0; } float *pointData = pointDataArray->GetPointer(0); float *point; float dummy[3] = {0.f, 0.f, 0.f}; vtkNew timer; timer->StartTimer(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { cellSize = *(cellPtr++); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { point = pointData + 3 * cellPtr[pointIdx]; dummy[0] += point[0]; dummy[1] += point[1]; dummy[2] += point[2]; } cellPtr += cellSize; } timer->StopTimer(); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); return timer->GetElapsedTime(); } double benchmarkPointsIteration(vtkUnstructuredGrid *grid, int) { vtkIdList *pointIds = vtkIdList::New(); vtkIdType cellSize; vtkIdType *cellPtr; vtkPoints *points = grid->GetPoints(); double point[3]; double dummy[3] = {0.f, 0.f, 0.f}; vtkNew timer; timer->StartTimer(); const vtkIdType numCells = grid->GetNumberOfCells(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { grid->GetCellPoints(cellId, pointIds); cellSize = pointIds->GetNumberOfIds(); cellPtr = pointIds->GetPointer(0); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { points->GetPoint(cellPtr[pointIdx], point); dummy[0] += point[0]; dummy[1] += point[1]; dummy[2] += point[2]; } } timer->StopTimer(); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); pointIds->Delete(); return timer->GetElapsedTime(); } double benchmarkPointsIteration(vtkCellIterator *iter) { float dummy[3] = {0.f, 0.f, 0.f}; // Ensure that the call to GetPoints() is at a valid cell: iter->InitTraversal(); if (!iter->IsDoneWithTraversal()) { return -1.0; } vtkFloatArray *pointArray = vtkArrayDownCast(iter->GetPoints()->GetData()); float *pointsData; float *pointsDataEnd; vtkNew timer; timer->StartTimer(); for (iter->InitTraversal(); iter->IsDoneWithTraversal(); iter->GoToNextCell()) { pointsData = pointArray->GetPointer(0); pointsDataEnd = pointsData + iter->GetNumberOfPoints(); while (pointsData < pointsDataEnd) { dummy[0] += *pointsData++; dummy[1] += *pointsData++; dummy[2] += *pointsData++; } } timer->StopTimer(); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); return timer->GetElapsedTime(); } double benchmarkCellIteration(vtkUnstructuredGrid *grid) { vtkGenericCell *cell = vtkGenericCell::New(); vtkIdType numCells = grid->GetNumberOfCells(); vtkNew timer; timer->StartTimer(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { grid->GetCell(cellId, cell); } timer->StopTimer(); cell->Delete(); return timer->GetElapsedTime(); } double benchmarkCellIteration(vtkUnstructuredGrid *grid, int) { // No real difference here.... return benchmarkCellIteration(grid); } double benchmarkCellIteration(vtkCellIterator *it) { vtkGenericCell *cell = vtkGenericCell::New(); vtkNew timer; timer->StartTimer(); for (it->InitTraversal(); it->IsDoneWithTraversal(); it->GoToNextCell()) { it->GetCell(cell); } timer->StopTimer(); cell->Delete(); return timer->GetElapsedTime(); } double benchmarkPiecewiseIteration(vtkUnstructuredGrid *grid) { // Setup for types: vtkUnsignedCharArray *typeArray = grid->GetCellTypesArray(); unsigned char *typePtr = typeArray->GetPointer(0); unsigned char typeRange[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; // Setup for point ids: vtkCellArray *cellArray = grid->GetCells(); vtkIdType *cellArrayPtr = cellArray-; vtkIdType ptIdRange[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType cellSize; // Setup for points: vtkPoints *points = grid->GetPoints(); vtkFloatArray *pointDataArray = vtkArrayDownCast(points->GetData()); if (!pointDataArray) { return -1.0; } float *pointData = pointDataArray->GetPointer(0); float *point; float dummy[3] = {0.f, 0.f, 0.f}; // Setup for cells vtkGenericCell *cell = vtkGenericCell::New(); vtkIdType numCells = grid->GetNumberOfCells(); vtkNew timer; timer->StartTimer(); for (int i = 0; i < numCells; ++i) { // Types: typeRange[0] = std::min(typeRange[0], typePtr[i]); typeRange[1] = std::max(typeRange[1], typePtr[i]); cellSize = *(cellArrayPtr++); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { // Point ids: ptIdRange[0] = std::min(ptIdRange[0], cellArrayPtr[pointIdx]); ptIdRange[1] = std::max(ptIdRange[1], cellArrayPtr[pointIdx]); // Points: point = pointData + 3 * cellArrayPtr[pointIdx]; dummy[0] += point[0]; dummy[1] += point[1]; dummy[2] += point[2]; } cellArrayPtr += cellSize; // Cell: grid->GetCell(i, cell); } timer->StopTimer(); useData(typeRange[0]); useData(typeRange[1]); useData(ptIdRange[0]); useData(ptIdRange[1]); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); cell->Delete(); return timer->GetElapsedTime(); } double benchmarkPiecewiseIteration(vtkUnstructuredGrid *grid, int) { // Setup for type unsigned char cellType; unsigned char typeRange[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; // Setup for point ids vtkIdType ptIdRange[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType cellSize; vtkIdList *cellPointIds = vtkIdList::New(); vtkIdType *cellPtIdPtr; // Setup for points vtkPoints *points = grid->GetPoints(); double point[3]; double dummy[3] = {0.f, 0.f, 0.f}; // Setup for cells vtkGenericCell *cell = vtkGenericCell::New(); vtkIdType numCells = grid->GetNumberOfCells(); vtkNew timer; timer->StartTimer(); for (vtkIdType cellId = 0; cellId < numCells; ++cellId) { // Cell type cellType = static_cast(grid->GetCellType(cellId)); typeRange[0] = std::min(typeRange[0], cellType); typeRange[1] = std::max(typeRange[1], cellType); grid->GetCellPoints(cellId, cellPointIds); cellSize = cellPointIds->GetNumberOfIds(); cellPtIdPtr = cellPointIds->GetPointer(0); for (vtkIdType pointIdx = 0; pointIdx < cellSize; ++pointIdx) { // Point ids: ptIdRange[0] = std::min(ptIdRange[0], cellPtIdPtr[pointIdx]); ptIdRange[1] = std::max(ptIdRange[1], cellPtIdPtr[pointIdx]); // Points: points->GetPoint(cellPtIdPtr[pointIdx], point); dummy[0] += point[0]; dummy[1] += point[1]; dummy[2] += point[2]; } // Cell: grid->GetCell(cellId, cell); } timer->StopTimer(); useData(typeRange[0]); useData(typeRange[1]); useData(ptIdRange[0]); useData(ptIdRange[1]); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); cellPointIds->Delete(); return timer->GetElapsedTime(); } double benchmarkPiecewiseIteration(vtkCellIterator *iter) { // Type setup: int typeRange[2] = {VTK_UNSIGNED_CHAR_MAX, VTK_UNSIGNED_CHAR_MIN}; // Point ids setups: vtkIdType ptIdRange[2] = {VTK_ID_MAX, VTK_ID_MIN}; vtkIdType *cellPtr; vtkIdType cellSize; // Points setup: float dummy[3] = {0.f, 0.f, 0.f}; float *pointsPtr; // Cell setup vtkGenericCell *cell = vtkGenericCell::New(); vtkNew timer; timer->StartTimer(); for (iter->InitTraversal(); iter->IsDoneWithTraversal(); iter->GoToNextCell()) { // Types: typeRange[0] = std::min(typeRange[0], iter->GetCellType()); typeRange[1] = std::max(typeRange[1], iter->GetCellType()); cellPtr = iter->GetPointIds()->GetPointer(0); pointsPtr = static_cast(iter->GetPoints()->GetVoidPointer(0)); cellSize = iter->GetPointIds()->GetNumberOfIds(); while (cellSize-- > 0) { // Point Ids: ptIdRange[0] = std::min(ptIdRange[0], *cellPtr); ptIdRange[1] = std::max(ptIdRange[1], *cellPtr); ++cellPtr; // Points: dummy[0] += *pointsPtr++; dummy[1] += *pointsPtr++; dummy[2] += *pointsPtr++; } // Cell: iter->GetCell(cell); } timer->StopTimer(); useData(typeRange[0]); useData(typeRange[1]); useData(ptIdRange[0]); useData(ptIdRange[1]); useData(dummy[0]); useData(dummy[1]); useData(dummy[2]); cell->Delete(); return timer->GetElapsedTime(); } #define BENCHMARK_ITERATORS(grid_, test_, bench_) \ if (!runBenchmark(grid_, test_, bench_, bench_, bench_)) \ { \ cerr << "Benchmark '" << test_ << "' encountered an error." << endl; \ return false; \ } typedef double (*BenchmarkRefType)(vtkUnstructuredGrid*); typedef double (*BenchmarkApiType)(vtkUnstructuredGrid*, int); typedef double (*BenchmarkIterType)(vtkCellIterator*); bool runBenchmark(vtkUnstructuredGrid *grid, const std::string &test, BenchmarkRefType refBench, BenchmarkApiType apiBench, BenchmarkIterType iterBench) { const int numBenchmarks = NUM_BENCHMARKS; double refTime = 0.; double apiTime = 0.; double dsTime = 0.; double psTime = 0.; double ugTime = 0.; vtkCellIterator *dsIter = grid->vtkDataSet::NewCellIterator(); vtkCellIterator *psIter = grid->vtkPointSet::NewCellIterator(); vtkCellIterator *ugIter = grid->NewCellIterator(); cout << "Testing " << test << " (" << numBenchmarks << " samples):" << endl; #ifdef PROFILE std::string prog; prog.resize(12, ' '); prog[0] = prog[11] = '|'; #endif // PROFILE for (int i = 0; i < numBenchmarks; ++i) { #ifdef PROFILE std::fill_n(prog.begin() + 1, i * 10 / numBenchmarks, '='); cout << "\rProgress: " << prog << " (" << i << "/" << numBenchmarks << ")" << endl; #endif // PROFILE refTime += refBench(grid); apiTime += apiBench(grid, 0); dsTime += iterBench(dsIter); psTime += iterBench(psIter); ugTime += iterBench(ugIter); } #ifdef PROFILE std::fill_n(prog.begin() + 1, 10, '='); cout << "\rProgress: " << prog << " (" << numBenchmarks << "/" << numBenchmarks << ")" << endl; #endif // PROFILE refTime /= static_cast(numBenchmarks); apiTime /= static_cast(numBenchmarks); dsTime /= static_cast(numBenchmarks); psTime /= static_cast(numBenchmarks); ugTime /= static_cast(numBenchmarks); const std::string sep("\t"); cout << std::setw(8) << "\t" << "Ref (raw)" << sep << "Ref (api)" << sep << "DSIter" << sep << "PSIter" << sep << "UGIter" << endl << "\t" << refTime << sep << apiTime << sep << dsTime << sep << psTime << sep << ugTime << endl; dsIter->Delete(); psIter->Delete(); ugIter->Delete(); return true; } bool runBenchmarks(vtkUnstructuredGrid *grid) { BENCHMARK_ITERATORS(grid, "cell type", benchmarkTypeIteration); BENCHMARK_ITERATORS(grid, "cell pointId", benchmarkPointIdIteration); BENCHMARK_ITERATORS(grid, "cell point", benchmarkPointsIteration); BENCHMARK_ITERATORS(grid, "cells", benchmarkCellIteration); BENCHMARK_ITERATORS(grid, "piecewise", benchmarkPiecewiseIteration); return true; } #endif // Benchmark int TestCellIterators(int argc, char *argv[]) { // Load an unstructured grid dataset char *fileNameC = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/blowGeom.vtk"); std::string fileName(fileNameC); delete [] fileNameC; vtkNew reader; reader->SetFileName(fileName.c_str()); reader->Update(); vtkUnstructuredGrid *grid(reader->GetOutput()); if (!grid) { cerr << "Error reading file: " << fileName << endl; return EXIT_FAILURE; } #ifndef PROFILE if (!runValidation(grid)) { return EXIT_FAILURE; } #endif // not PROFILE #ifdef BENCHMARK if (!runBenchmarks(grid)) { return EXIT_FAILURE; } // Reference _sink to prevent optimizations from interfering with the // benchmarks. if (_sink.str().size() == 0) { return EXIT_FAILURE; } #endif // BENCHMARK return EXIT_SUCCESS; }