//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt 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 #include #include #include #include #include #include #include using vtkm::cont::cuda::internal::CudaAllocator; namespace { template ValueType* AllocateManagedPointer(vtkm::Id numValues) { void* result; VTKM_CUDA_CALL(cudaMallocManaged(&result, static_cast(numValues) * sizeof(ValueType))); // Some sanity checks: VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(result), "Allocated pointer is not a device pointer."); VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(result), "Allocated pointer is not managed."); return static_cast(result); } template ValueType* AllocateDevicePointer(vtkm::Id numValues) { void* result; VTKM_CUDA_CALL(cudaMalloc(&result, static_cast(numValues) * sizeof(ValueType))); // Some sanity checks: VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(result), "Allocated pointer is not a device pointer."); VTKM_TEST_ASSERT(!CudaAllocator::IsManagedPointer(result), "Allocated pointer is managed."); return static_cast(result); } template vtkm::cont::ArrayHandle CreateArrayHandle(vtkm::Id numValues, bool managed) { ValueType* ptr = managed ? AllocateManagedPointer(numValues) : AllocateDevicePointer(numValues); return vtkm::cont::make_ArrayHandle(ptr, numValues); } template void TestPrepareForInput(bool managed) { vtkm::cont::ArrayHandle handle = CreateArrayHandle(32, managed); handle.PrepareForInput(vtkm::cont::DeviceAdapterTagCuda()); auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after PrepareForInput."); VTKM_TEST_ASSERT(execArray != nullptr, "No execution array after PrepareForInput."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(execArray), "PrepareForInput execution array not device pointer."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "PrepareForInput control array not device pointer."); if (managed) { VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(execArray), "PrepareForInput execution array unmanaged."); VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(contArray), "PrepareForInput control array unmanaged."); } VTKM_TEST_ASSERT(execArray == contArray, "PrepareForInput managed arrays not shared."); } template void TestPrepareForInPlace(bool managed) { vtkm::cont::ArrayHandle handle = CreateArrayHandle(32, managed); handle.PrepareForInPlace(vtkm::cont::DeviceAdapterTagCuda()); auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after PrepareForInPlace."); VTKM_TEST_ASSERT(execArray != nullptr, "No execution array after PrepareForInPlace."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(execArray), "PrepareForInPlace execution array not device pointer."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "PrepareForInPlace control array not device pointer."); if (managed) { VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(execArray), "PrepareForInPlace execution array unmanaged."); VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(contArray), "PrepareForInPlace control array unmanaged."); } VTKM_TEST_ASSERT(execArray == contArray, "PrepareForInPlace managed arrays not shared."); } template void TestPrepareForOutput(bool managed) { // Should reuse a managed control pointer if buffer is large enough. vtkm::cont::ArrayHandle handle = CreateArrayHandle(32, managed); handle.PrepareForOutput(32, vtkm::cont::DeviceAdapterTagCuda()); auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after PrepareForOutput."); VTKM_TEST_ASSERT(execArray != nullptr, "No execution array after PrepareForOutput."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(execArray), "PrepareForOutput execution array not device pointer."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "PrepareForOutput control array not device pointer."); if (managed) { VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(execArray), "PrepareForOutput execution array unmanaged."); VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(contArray), "PrepareForOutput control array unmanaged."); } VTKM_TEST_ASSERT(execArray == contArray, "PrepareForOutput managed arrays not shared."); } template void TestReleaseResourcesExecution(bool managed) { vtkm::cont::ArrayHandle handle = CreateArrayHandle(32, managed); handle.PrepareForInput(vtkm::cont::DeviceAdapterTagCuda()); void* origArray; { auto lock = handle.Internals->GetLock(); origArray = handle.Internals->Internals->GetExecutionArray(lock); } handle.ReleaseResourcesExecution(); auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after ReleaseResourcesExecution."); VTKM_TEST_ASSERT(execArray == nullptr, "Execution array not cleared after ReleaseResourcesExecution."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "ReleaseResourcesExecution control array not device pointer."); if (managed) { VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(contArray), "ReleaseResourcesExecution control array unmanaged."); } VTKM_TEST_ASSERT(origArray == contArray, "Control array changed after ReleaseResourcesExecution."); } template void TestRoundTrip(bool managed) { vtkm::cont::ArrayHandle handle = CreateArrayHandle(32, managed); handle.PrepareForOutput(32, vtkm::cont::DeviceAdapterTagCuda()); void* origContArray; { auto lock = handle.Internals->GetLock(); origContArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); } { auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after PrepareForOutput."); VTKM_TEST_ASSERT(execArray != nullptr, "No execution array after PrepareForOutput."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(execArray), "PrepareForOutput execution array not device pointer."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "PrepareForOutput control array not device pointer."); if (managed) { VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(execArray), "PrepareForOutput execution array unmanaged."); VTKM_TEST_ASSERT(CudaAllocator::IsManagedPointer(contArray), "PrepareForOutput control array unmanaged."); } VTKM_TEST_ASSERT(execArray == contArray, "PrepareForOutput managed arrays not shared."); } try { handle.GetPortalControl(); } catch (vtkm::cont::ErrorBadValue&) { if (managed) { throw; // Exception is unexpected } // If !managed, this exception is intentional to indicate that the control // array is a non-managed device pointer, and thus unaccessible from the // control environment. Return because we've already validated correct // behavior by catching this exception. return; } if (!managed) { VTKM_TEST_FAIL("Expected exception not thrown."); } { auto lock = handle.Internals->GetLock(); void* contArray = handle.Internals->Internals->GetControlArray(lock)->GetBasePointer(); void* execArray = handle.Internals->Internals->GetExecutionArray(lock); VTKM_TEST_ASSERT(contArray != nullptr, "No control array after GetPortalConst."); VTKM_TEST_ASSERT(execArray == nullptr, "Execution array not cleared after GetPortalConst."); VTKM_TEST_ASSERT(CudaAllocator::IsDevicePointer(contArray), "GetPortalConst control array not device pointer."); VTKM_TEST_ASSERT(origContArray == contArray, "GetPortalConst changed control array."); } } template void DoTests() { TestPrepareForInput(false); TestPrepareForInPlace(false); TestPrepareForOutput(false); TestReleaseResourcesExecution(false); TestRoundTrip(false); // If this device does not support managed memory, skip the managed tests. if (!CudaAllocator::UsingManagedMemory()) { std::cerr << "Skipping some tests -- device does not support managed memory.\n"; } else { TestPrepareForInput(true); TestPrepareForInPlace(true); TestPrepareForOutput(true); TestReleaseResourcesExecution(true); TestRoundTrip(true); } } struct ArgToTemplateType { template void operator()(ValueType) const { DoTests(); } }; void Launch() { using Types = vtkm::List, vtkm::Float32, vtkm::Vec, vtkm::Float64, vtkm::Vec>; vtkm::testing::Testing::TryTypes(ArgToTemplateType(), Types()); } } // end anon namespace int UnitTestCudaShareUserProvidedManagedMemory(int argc, char* argv[]) { auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); tracker.ForceDevice(vtkm::cont::DeviceAdapterTagCuda{}); int ret = vtkm::cont::testing::Testing::Run(Launch, argc, argv); return vtkm::cont::cuda::internal::Testing::CheckCudaBeforeExit(ret); }