From bb1322d49510ffde956bd7d34117fced7117b636 Mon Sep 17 00:00:00 2001 From: Jason Henline Date: Wed, 24 Aug 2016 16:58:20 +0000 Subject: [StreamExecutor] Executor add synchronous methods Summary: Add Executor methods that block the host until completion. Since these methods are host-synchronous, they don't require Stream arguments. Reviewers: jlebar Subscribers: jprice, parallel_libs-commits Differential Revision: https://reviews.llvm.org/D23577 llvm-svn: 279640 --- .../streamexecutor/lib/unittests/ExecutorTest.cpp | 451 +++++++++++++++++++++ 1 file changed, 451 insertions(+) create mode 100644 parallel-libs/streamexecutor/lib/unittests/ExecutorTest.cpp (limited to 'parallel-libs/streamexecutor/lib/unittests/ExecutorTest.cpp') diff --git a/parallel-libs/streamexecutor/lib/unittests/ExecutorTest.cpp b/parallel-libs/streamexecutor/lib/unittests/ExecutorTest.cpp new file mode 100644 index 00000000000..d2d03fb6c88 --- /dev/null +++ b/parallel-libs/streamexecutor/lib/unittests/ExecutorTest.cpp @@ -0,0 +1,451 @@ +//===-- ExecutorTest.cpp - Tests for Executor -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the unit tests for Executor code. +/// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "streamexecutor/Executor.h" +#include "streamexecutor/PlatformInterfaces.h" + +#include "gtest/gtest.h" + +namespace { + +namespace se = ::streamexecutor; + +class MockPlatformExecutor : public se::PlatformExecutor { +public: + ~MockPlatformExecutor() override {} + + std::string getName() const override { return "MockPlatformExecutor"; } + + se::Expected> + createStream() override { + return se::make_error("not implemented"); + } + + se::Expected + allocateDeviceMemory(size_t ByteCount) override { + return se::GlobalDeviceMemoryBase(std::malloc(ByteCount)); + } + + se::Error freeDeviceMemory(se::GlobalDeviceMemoryBase Memory) override { + std::free(const_cast(Memory.getHandle())); + return se::Error::success(); + } + + se::Expected allocateHostMemory(size_t ByteCount) override { + return std::malloc(ByteCount); + } + + se::Error freeHostMemory(void *Memory) override { + std::free(Memory); + return se::Error::success(); + } + + se::Error synchronousCopyD2H(const se::GlobalDeviceMemoryBase &DeviceSrc, + size_t SrcByteOffset, void *HostDst, + size_t DstByteOffset, + size_t ByteCount) override { + std::memcpy(static_cast(HostDst) + DstByteOffset, + static_cast(DeviceSrc.getHandle()) + + SrcByteOffset, + ByteCount); + return se::Error::success(); + } + + se::Error synchronousCopyH2D(const void *HostSrc, size_t SrcByteOffset, + se::GlobalDeviceMemoryBase DeviceDst, + size_t DstByteOffset, + size_t ByteCount) override { + std::memcpy(static_cast(const_cast(DeviceDst.getHandle())) + + DstByteOffset, + static_cast(HostSrc) + SrcByteOffset, ByteCount); + return se::Error::success(); + } + + se::Error synchronousCopyD2D(se::GlobalDeviceMemoryBase DeviceDst, + size_t DstByteOffset, + const se::GlobalDeviceMemoryBase &DeviceSrc, + size_t SrcByteOffset, + size_t ByteCount) override { + std::memcpy(static_cast(const_cast(DeviceDst.getHandle())) + + DstByteOffset, + static_cast(DeviceSrc.getHandle()) + + SrcByteOffset, + ByteCount); + return se::Error::success(); + } +}; + +/// Test fixture to hold objects used by tests. +class ExecutorTest : public ::testing::Test { +public: + ExecutorTest() + : HostA5{0, 1, 2, 3, 4}, HostB5{5, 6, 7, 8, 9}, + HostA7{10, 11, 12, 13, 14, 15, 16}, HostB7{17, 18, 19, 20, 21, 22, 23}, + DeviceA5(se::GlobalDeviceMemory::makeFromElementCount(HostA5, 5)), + DeviceB5(se::GlobalDeviceMemory::makeFromElementCount(HostB5, 5)), + DeviceA7(se::GlobalDeviceMemory::makeFromElementCount(HostA7, 7)), + DeviceB7(se::GlobalDeviceMemory::makeFromElementCount(HostB7, 7)), + Host5{24, 25, 26, 27, 28}, Host7{29, 30, 31, 32, 33, 34, 35}, + Executor(&PExecutor) {} + + // Device memory is backed by host arrays. + int HostA5[5]; + int HostB5[5]; + int HostA7[7]; + int HostB7[7]; + se::GlobalDeviceMemory DeviceA5; + se::GlobalDeviceMemory DeviceB5; + se::GlobalDeviceMemory DeviceA7; + se::GlobalDeviceMemory DeviceB7; + + // Host memory to be used as actual host memory. + int Host5[5]; + int Host7[7]; + + MockPlatformExecutor PExecutor; + se::Executor Executor; +}; + +#define EXPECT_NO_ERROR(E) EXPECT_FALSE(static_cast(E)) +#define EXPECT_ERROR(E) \ + do { \ + se::Error E__ = E; \ + EXPECT_TRUE(static_cast(E__)); \ + consumeError(std::move(E__)); \ + } while (false) + +using llvm::ArrayRef; +using llvm::MutableArrayRef; + +// D2H tests + +TEST_F(ExecutorTest, SyncCopyD2HToMutableArrayRefByCount) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2H(DeviceA5, MutableArrayRef(Host5), 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_NO_ERROR( + Executor.synchronousCopyD2H(DeviceB5, MutableArrayRef(Host5), 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostB5[I], Host5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyD2H(DeviceA7, MutableArrayRef(Host5), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyD2H(DeviceA5, MutableArrayRef(Host7), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyD2H(DeviceA5, MutableArrayRef(Host5), 7)); +} + +TEST_F(ExecutorTest, SyncCopyD2HToMutableArrayRef) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2H(DeviceA5, MutableArrayRef(Host5))); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyD2H(DeviceA7, MutableArrayRef(Host5))); + + EXPECT_ERROR( + Executor.synchronousCopyD2H(DeviceA5, MutableArrayRef(Host7))); +} + +TEST_F(ExecutorTest, SyncCopyD2HToPointer) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2H(DeviceA5, Host5, 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA5, Host7, 7)); +} + +TEST_F(ExecutorTest, SyncCopyD2HSliceToMutableArrayRefByCount) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2H( + DeviceA5.asSlice().drop_front(1), MutableArrayRef(Host5 + 1, 4), 4)); + for (int I = 1; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_NO_ERROR(Executor.synchronousCopyD2H(DeviceB5.asSlice().drop_back(1), + MutableArrayRef(Host5), 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostB5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA7.asSlice(), + MutableArrayRef(Host5), 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA5.asSlice(), + MutableArrayRef(Host7), 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA5.asSlice(), + MutableArrayRef(Host5), 7)); +} + +TEST_F(ExecutorTest, SyncCopyD2HSliceToMutableArrayRef) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2H(DeviceA7.asSlice().slice(1, 5), + MutableArrayRef(Host5))); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA7[I + 1], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA7.asSlice().drop_back(1), + MutableArrayRef(Host5))); + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA5.asSlice(), + MutableArrayRef(Host7))); +} + +TEST_F(ExecutorTest, SyncCopyD2HSliceToPointer) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2H(DeviceA5.asSlice().drop_front(1), + Host5 + 1, 4)); + for (int I = 1; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2H(DeviceA5.asSlice(), Host7, 7)); +} + +// H2D tests + +TEST_F(ExecutorTest, SyncCopyH2DToArrayRefByCount) { + EXPECT_NO_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA5, 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_NO_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceB5, 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostB5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host7), DeviceA5, 7)); + + EXPECT_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA7, 7)); + + EXPECT_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA5, 7)); +} + +TEST_F(ExecutorTest, SyncCopyH2DToArrayRef) { + EXPECT_NO_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA7)); + + EXPECT_ERROR(Executor.synchronousCopyH2D(ArrayRef(Host7), DeviceA5)); +} + +TEST_F(ExecutorTest, SyncCopyH2DToPointer) { + EXPECT_NO_ERROR(Executor.synchronousCopyH2D(Host5, DeviceA5, 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyH2D(Host7, DeviceA5, 7)); +} + +TEST_F(ExecutorTest, SyncCopyH2DSliceToArrayRefByCount) { + EXPECT_NO_ERROR(Executor.synchronousCopyH2D( + ArrayRef(Host5 + 1, 4), DeviceA5.asSlice().drop_front(1), 4)); + for (int I = 1; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_NO_ERROR(Executor.synchronousCopyH2D( + ArrayRef(Host5), DeviceB5.asSlice().drop_back(1), 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostB5[I], Host5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host7), DeviceA5.asSlice(), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA7.asSlice(), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA5.asSlice(), 7)); +} + +TEST_F(ExecutorTest, SyncCopyH2DSliceToArrayRef) { + EXPECT_NO_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA5.asSlice())); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host5), DeviceA7.asSlice())); + + EXPECT_ERROR( + Executor.synchronousCopyH2D(ArrayRef(Host7), DeviceA5.asSlice())); +} + +TEST_F(ExecutorTest, SyncCopyH2DSliceToPointer) { + EXPECT_NO_ERROR(Executor.synchronousCopyH2D(Host5, DeviceA5.asSlice(), 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], Host5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyH2D(Host7, DeviceA5.asSlice(), 7)); +} + +// D2D tests + +TEST_F(ExecutorTest, SyncCopyD2DByCount) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB5, 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB5[I]); + } + + EXPECT_NO_ERROR(Executor.synchronousCopyD2D(DeviceA7, DeviceB7, 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostA7[I], HostB7[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB5, 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA7, DeviceB5, 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB7, 7)); +} + +TEST_F(ExecutorTest, SyncCopyD2D) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB5[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA7, DeviceB5)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB7)); +} + +TEST_F(ExecutorTest, SyncCopySliceD2DByCount) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2D(DeviceA5.asSlice().drop_front(1), + DeviceB5, 4)); + for (int I = 0; I < 4; ++I) { + EXPECT_EQ(HostA5[I + 1], HostB5[I]); + } + + EXPECT_NO_ERROR(Executor.synchronousCopyD2D(DeviceA7.asSlice().drop_back(1), + DeviceB7, 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostA7[I], HostB7[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB5, 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA7.asSlice(), DeviceB5, 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB7, 7)); +} + +TEST_F(ExecutorTest, SyncCopySliceD2D) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2D(DeviceA7.asSlice().drop_back(2), DeviceB5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA7[I], HostB5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA7.asSlice().drop_front(1), DeviceB5)); + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice().drop_back(1), DeviceB7)); +} + +TEST_F(ExecutorTest, SyncCopyD2DSliceByCount) { + EXPECT_NO_ERROR(Executor.synchronousCopyD2D( + DeviceA5, DeviceB7.asSlice().drop_front(2), 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB7[I + 2]); + } + + EXPECT_NO_ERROR(Executor.synchronousCopyD2D( + DeviceA7, DeviceB7.asSlice().drop_back(3), 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostA7[I], HostB7[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB5.asSlice(), 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA7, DeviceB5.asSlice(), 7)); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB7.asSlice(), 7)); +} + +TEST_F(ExecutorTest, SyncCopyD2DSlice) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2D(DeviceA5, DeviceB7.asSlice().drop_back(2))); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB7[I]); + } + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA7, DeviceB5.asSlice())); + + EXPECT_ERROR(Executor.synchronousCopyD2D(DeviceA5, DeviceB7.asSlice())); +} + +TEST_F(ExecutorTest, SyncCopySliceD2DSliceByCount) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB5.asSlice(), 5)); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB5[I]); + } + + EXPECT_NO_ERROR( + Executor.synchronousCopyD2D(DeviceA7.asSlice(), DeviceB7.asSlice(), 2)); + for (int I = 0; I < 2; ++I) { + EXPECT_EQ(HostA7[I], HostB7[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB5.asSlice(), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA7.asSlice(), DeviceB5.asSlice(), 7)); + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB7.asSlice(), 7)); +} + +TEST_F(ExecutorTest, SyncCopySliceD2DSlice) { + EXPECT_NO_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB5.asSlice())); + for (int I = 0; I < 5; ++I) { + EXPECT_EQ(HostA5[I], HostB5[I]); + } + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA7.asSlice(), DeviceB5.asSlice())); + + EXPECT_ERROR( + Executor.synchronousCopyD2D(DeviceA5.asSlice(), DeviceB7.asSlice())); +} + +} // namespace -- cgit v1.2.3