/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* execViaCpu is used to do operations on GpuMatirx and/or GpuIVector through cpu functions. It can automatically make a temporary CPU copy for the gpu matrix/vector, and copy back after executing the CPU function. Examples: 1. For a function, functor or lambda: r = execViaCpu(&f, mat, vec) 2. For member function of CpuMatirx, execViaCpu2 should be used: execViaCpu2(&CpuMatrix::selectElements, *this, table, ids) */ #pragma once namespace paddle { template class CopyToCpu { public: explicit CopyToCpu(Arg& arg) : arg_(arg) {} Arg& copiedArg() const { return arg_; } private: Arg& arg_; }; template <> class CopyToCpu { public: explicit CopyToCpu(Matrix& arg) : arg_(arg) { if (arg.useGpu()) { CHECK(!arg.isTransposed()) << "Not supported"; copied_ = Matrix::create(arg.getHeight(), arg.getWidth(), /* trans= */ false, /* useGpu= */ false); copied_->copyFrom(arg); } } ~CopyToCpu() { if (copied_) { arg_.copyFrom(*copied_); } } Matrix& copiedArg() const { return copied_ ? *copied_ : arg_; } private: Matrix& arg_; MatrixPtr copied_; }; template <> class CopyToCpu { public: explicit CopyToCpu(const Matrix& arg) : arg_(arg) { if (arg.useGpu()) { CHECK(!arg.isTransposed()) << "Not supported"; copied_ = Matrix::create(arg.getHeight(), arg.getWidth(), /* trans= */ false, /* useGpu= */ false); copied_->copyFrom(arg); } } const Matrix& copiedArg() const { return copied_ ? *copied_ : arg_; } private: const Matrix& arg_; MatrixPtr copied_; }; template <> class CopyToCpu { public: explicit CopyToCpu(IVector& arg) : arg_(arg) { if (arg.useGpu()) { copied_ = IVector::create(arg.getSize(), /* useGpu= */ false); copied_->copyFrom(arg); } } ~CopyToCpu() { if (copied_) { arg_.copyFrom(*copied_); } } IVector& copiedArg() const { return copied_ ? *copied_ : arg_; } private: IVector& arg_; IVectorPtr copied_; }; template <> class CopyToCpu { public: explicit CopyToCpu(const IVector& arg) : arg_(arg) { if (arg.useGpu()) { copied_ = IVector::create(arg.getSize(), /* useGpu= */ false); copied_->copyFrom(arg); } } const IVector& copiedArg() const { return copied_ ? *copied_ : arg_; } private: const IVector& arg_; IVectorPtr copied_; }; namespace detail { template class GpuFuncWrapperImp; template class GpuFuncWrapperBase { public: typedef R ResultType; R operator()(F&& f, Args... args) { return f(CopyToCpu::type>(args) .copiedArg()...); } }; // function template class GpuFuncWrapperImp : public GpuFuncWrapperBase {}; // function pointer template class GpuFuncWrapperImp : public GpuFuncWrapperBase {}; template class GpuFuncWrapperImp2; template class GpuFuncWrapperImp2 : public GpuFuncWrapperBase {}; template class GpuFuncWrapperImp2 : public GpuFuncWrapperBase {}; // functor or lambda template class GpuFuncWrapperImp : public GpuFuncWrapperImp2 {}; template class GpuFuncWrapper2 : public GpuFuncWrapperImp< std::is_function::value, std::is_pointer::value && std::is_function::type>::value, std::is_class::value, F> {}; template class GpuFuncWrapper : public GpuFuncWrapper2::type> {}; } // namespace detail template typename detail::GpuFuncWrapper::ResultType execViaCpu(F&& f, Args&&... args) { return detail::GpuFuncWrapper()(std::move(f), args...); } // The second version is for F as member function of CpuMatrix template R execViaCpu2(R (CpuMatrix::*f)(FArgs...), Args&&... args) { auto lambda = [](R (CpuMatrix::*f)(FArgs...), Matrix& ths, FArgs... args) { return (((CpuMatrix&)ths).*f)(args...); }; return execViaCpu(lambda, f, args...); } } // namespace paddle