open_files_op.cc 7.8 KB
Newer Older
F
fengjiayi 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
//   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// 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.

15
#include <cmath>
Y
yuyang18 已提交
16
#include <stdexcept>
F
fengjiayi 已提交
17
#include <thread>  // NOLINT
18 19
#include "ThreadPool.h"
#include "paddle/fluid/framework/blocking_queue.h"
20
#include "paddle/fluid/operators/reader/blocking_queue.h"
F
fengjiayi 已提交
21 22 23 24 25 26
#include "paddle/fluid/operators/reader/reader_op_registry.h"

namespace paddle {
namespace operators {
namespace reader {

27
class IReaderContainer {
F
fengjiayi 已提交
28
 public:
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
  virtual ~IReaderContainer() {}
  virtual void AppendReader(
      std::unique_ptr<framework::ReaderBase>&& readers) = 0;
  virtual void Stop() = 0;
  virtual void Start() = 0;
  virtual void ReadNext(std::vector<framework::LoDTensor>* out) = 0;
};

class OrderedReaderContainer : public IReaderContainer {
 public:
  void AppendReader(std::unique_ptr<framework::ReaderBase>&& reader) override {
    pending_.emplace(std::move(reader));
  }

  void Stop() override {
    while (!pending_.empty()) {
      MoveFrontPendingToDone();
46
    }
F
fengjiayi 已提交
47 48
  }

49
  void Start() override { std::swap(done_, pending_); }
F
fengjiayi 已提交
50

51 52 53 54 55 56 57 58 59 60 61
  void ReadNext(std::vector<framework::LoDTensor>* out) override {
    if (!pending_.empty()) {
      pending_.front()->ReadNext(out);
      if (out->empty()) {
        MoveFrontPendingToDone();
        ReadNext(out);
      }
    } else {
      out->clear();
    }
  }
F
fengjiayi 已提交
62

F
fengjiayi 已提交
63
 private:
64 65 66 67 68 69 70 71 72
  void MoveFrontPendingToDone() {
    pending_.front()->Shutdown();
    pending_.front()->Start();
    done_.emplace(move(pending_.front()));
    pending_.pop();
  }

  std::queue<std::unique_ptr<framework::ReaderBase>> pending_;
  std::queue<std::unique_ptr<framework::ReaderBase>> done_;
F
fengjiayi 已提交
73 74
};

75 76
class PreemptiveReaderContainer : public IReaderContainer {
  using ReaderList = std::list<std::unique_ptr<framework::ReaderBase>>;
F
fengjiayi 已提交
77

78 79 80
  struct FutureItem {
    std::vector<framework::LoDTensor> data_;
    ReaderList::iterator reader_it_;
Y
yuyang18 已提交
81
    std::exception_ptr exception_;
82
  };
F
fengjiayi 已提交
83

84
  using FutureList = std::list<std::future<FutureItem>>;
F
fengjiayi 已提交
85

86 87
 public:
  explicit PreemptiveReaderContainer(size_t thread_num) : pool_(thread_num) {}
F
fengjiayi 已提交
88

89 90 91 92 93 94 95 96 97 98 99 100
  void Stop() override {
    if (!pending_.empty()) {
      for (auto& reader : pending_) {
        reader->Shutdown();
      }
      for (auto& fu : futures_) {
        fu.wait();
      }
      futures_.clear();
      for (auto& reader : pending_) {
        reader->Start();
        done_.emplace_back(std::move(reader));
F
fengjiayi 已提交
101
      }
102 103 104 105
      pending_.clear();
      bool timeout;
      complete_queue_.PopAll(1000, &timeout);
      PADDLE_ENFORCE(!timeout);
F
fengjiayi 已提交
106 107
    }
  }
108 109 110 111

  void Start() override {
    for (auto& reader : done_) {
      AppendReader(std::move(reader));
F
fengjiayi 已提交
112
    }
113
    done_.clear();
F
fengjiayi 已提交
114
  }
115 116 117 118 119

  void ReadNext(std::vector<framework::LoDTensor>* out) override {
    if (!pending_.empty()) {
      auto future_it = complete_queue_.Pop();
      FutureItem item = future_it->get();
Y
yuyang18 已提交
120 121 122 123 124 125 126 127 128
      if (item.exception_) {
        for (auto it = futures_.begin(); it != futures_.end(); ++it) {
          if (it != future_it) {
            it->wait();  // Wait all other threads complete.
          }
        }
        std::rethrow_exception(item.exception_);

      } else if (item.data_.empty()) {  // reader done.
129 130 131 132 133 134 135 136 137 138 139
        done_.emplace_back(std::move(*item.reader_it_));
        pending_.erase(item.reader_it_);
        futures_.erase(future_it);
        ReadNext(out);
      } else {
        *out = item.data_;
        // continue read async
        AsyncRead(item.reader_it_, &future_it);
      }
    } else {
      out->clear();
F
fengjiayi 已提交
140
    }
141 142 143
  }

 private:
Y
yuyang18 已提交
144 145
  void AppendReader(std::unique_ptr<framework::ReaderBase>&& reader) override {
    pending_.emplace_back(std::move(reader));
146 147 148 149 150 151 152 153 154 155 156 157 158 159
    auto reader_it = pending_.end();
    --reader_it;

    futures_.emplace_back();
    auto future_it = futures_.end();
    --future_it;

    AsyncRead(reader_it, &future_it);
  }

  void AsyncRead(const ReaderList::iterator& reader_it,
                 FutureList::iterator* future_it_ptr) {
    auto& future_it = *future_it_ptr;
    *future_it = pool_.enqueue([reader_it, future_it, this] {
Y
yuyang18 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
      try {
        FutureItem item;
        item.reader_it_ = reader_it;
        (*reader_it)->ReadNext(&item.data_);
        if (item.data_.empty()) {
          (*reader_it)->Shutdown();
          (*reader_it)->Start();
        }
        complete_queue_.Push(future_it);
        return item;
      } catch (...) {
        FutureItem item;
        item.exception_ = std::current_exception();
        complete_queue_.Push(future_it);
        return item;
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
      }
    });
  }

  FutureList futures_;
  ThreadPool pool_;
  framework::BlockingQueue<FutureList::iterator> complete_queue_;
  std::list<std::unique_ptr<framework::ReaderBase>> pending_;
  std::list<std::unique_ptr<framework::ReaderBase>> done_;
};

class MultiFileReader : public framework::ReaderBase {
 public:
  MultiFileReader(const std::vector<std::string>& file_names,
                  std::unique_ptr<IReaderContainer>&& container)
      : container_(std::move(container)) {
    for (auto& fn : file_names) {
      container_->AppendReader(CreateReaderByFileName(fn));
F
fengjiayi 已提交
193 194
    }
  }
195

196 197 198 199 200
  ~MultiFileReader() { container_->Stop(); }

 protected:
  void ReadNextImpl(std::vector<framework::LoDTensor>* out) override {
    container_->ReadNext(out);
F
fengjiayi 已提交
201
  }
202 203 204 205 206 207
  void ShutdownImpl() override { container_->Stop(); }
  void StartImpl() override { container_->Start(); }

 private:
  std::unique_ptr<IReaderContainer> container_;
};
F
fengjiayi 已提交
208 209 210 211 212 213 214 215 216 217 218 219

class OpenFilesOp : public framework::OperatorBase {
 public:
  using framework::OperatorBase::OperatorBase;

 private:
  void RunImpl(const framework::Scope& scope,
               const platform::Place& dev_place) const override {
    const auto& shape_concat = Attr<std::vector<int>>("shape_concat");
    const auto& ranks = Attr<std::vector<int>>("ranks");
    PADDLE_ENFORCE(!shape_concat.empty() && !ranks.empty());
    PADDLE_ENFORCE_EQ(std::accumulate(ranks.begin(), ranks.end(), 0),
F
fengjiayi 已提交
220
                      static_cast<int>(shape_concat.size()),
F
fengjiayi 已提交
221 222 223 224
                      "The accumulate of all ranks should be equal to the "
                      "shape concat's length.");
    const auto& file_names = Attr<std::vector<std::string>>("file_names");
    PADDLE_ENFORCE(!file_names.empty(), "No file to be read!");
225
    bool is_test = Attr<bool>("is_test");
F
fengjiayi 已提交
226 227 228

    auto* out = scope.FindVar(Output("Out"))
                    ->template GetMutable<framework::ReaderHolder>();
229 230 231 232 233 234
    std::unique_ptr<IReaderContainer> container;

    if (is_test) {
      container.reset(new OrderedReaderContainer());
    } else {
      container.reset(new PreemptiveReaderContainer(
235 236
          std::min(file_names.size(),
                   static_cast<size_t>(std::thread::hardware_concurrency()))));
237 238
    }

239
    out->Reset(
240
        std::make_shared<MultiFileReader>(file_names, std::move(container)));
F
fengjiayi 已提交
241 242 243
  }
};

244
class OpenFilesOpMaker : public FileReaderMakerBase {
Y
Yu Yang 已提交
245 246
 protected:
  void Apply() override {
247
    AddAttr<std::vector<std::string>>("file_names", "Files to be read.");
248
    AddAttr<bool>("is_test", "Used for testing data.").SetDefault(false);
249

F
fengjiayi 已提交
250 251 252
    AddComment(R"DOC(
      OpenFiles Operator

Y
Yu Yang 已提交
253
      An OpenFilesOp creates a MultiFileReader, which is able to
F
fengjiayi 已提交
254 255 256 257 258 259 260 261 262 263 264 265
      read data multi-threaded from multiple files.
    )DOC");
  }
};

}  // namespace reader
}  // namespace operators
}  // namespace paddle

namespace reader = paddle::operators::reader;

REGISTER_FILE_READER_OPERATOR(open_files, reader::OpenFilesOp,
266
                              reader::OpenFilesOpMaker);