2150 lines
66 KiB
C++
2150 lines
66 KiB
C++
/* 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. */
|
|
|
|
#pragma once
|
|
|
|
#if defined _WIN32 || defined __APPLE__
|
|
#else
|
|
#define _LINUX
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <future> // NOLINT
|
|
#include <memory>
|
|
#include <mutex> // NOLINT
|
|
#include <random>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <thread> // NOLINT
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "paddle/common/macros.h"
|
|
#include "paddle/fluid/framework/archive.h"
|
|
#include "paddle/fluid/framework/blocking_queue.h"
|
|
#include "paddle/fluid/framework/channel.h"
|
|
#include "paddle/fluid/framework/fleet/fleet_wrapper.h"
|
|
#include "paddle/fluid/framework/lod_tensor.h"
|
|
#include "paddle/fluid/framework/variable.h"
|
|
#include "paddle/phi/core/framework/data_feed.pb.h"
|
|
#include "paddle/phi/core/framework/reader.h"
|
|
#include "paddle/phi/core/platform/timer.h"
|
|
#include "paddle/utils/string/string_helper.h"
|
|
#if defined(PADDLE_WITH_CUDA)
|
|
#include "paddle/fluid/framework/fleet/heter_ps/gpu_graph_utils.h"
|
|
#include "paddle/phi/core/cuda_stream.h"
|
|
#include "paddle/phi/core/platform/cuda_device_guard.h"
|
|
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
|
|
#endif
|
|
#include "paddle/common/flags.h"
|
|
|
|
COMMON_DECLARE_int32(record_pool_max_size);
|
|
COMMON_DECLARE_int32(slotpool_thread_num);
|
|
COMMON_DECLARE_bool(enable_slotpool_wait_release);
|
|
COMMON_DECLARE_bool(enable_slotrecord_reset_shrink);
|
|
|
|
namespace paddle {
|
|
namespace framework {
|
|
class DataFeedDesc;
|
|
class Scope;
|
|
class Variable;
|
|
class NeighborSampleResult;
|
|
class NodeQueryResult;
|
|
template <typename KeyType, typename ValType>
|
|
class HashTable;
|
|
} // namespace framework
|
|
} // namespace paddle
|
|
|
|
namespace phi {
|
|
class DenseTensor;
|
|
} // namespace phi
|
|
|
|
namespace paddle {
|
|
namespace framework {
|
|
|
|
// DataFeed is the base virtual class for all other DataFeeds.
|
|
// It is used to read files and parse the data for subsequent trainer.
|
|
// Example:
|
|
// DataFeed* reader =
|
|
// paddle::framework::DataFeedFactory::CreateDataFeed(data_feed_name);
|
|
// reader->Init(data_feed_desc); // data_feed_desc is a protobuf object
|
|
// reader->SetFileList(filelist);
|
|
// const std::vector<std::string> & use_slot_alias =
|
|
// reader->GetUseSlotAlias();
|
|
// for (auto name: use_slot_alias){ // for binding memory
|
|
// reader->AddFeedVar(scope->Var(name), name);
|
|
// }
|
|
// reader->Start();
|
|
// while (reader->Next()) {
|
|
// // trainer do something
|
|
// }
|
|
|
|
template <typename T>
|
|
struct SlotValues {
|
|
std::vector<T> slot_values;
|
|
std::vector<uint32_t> slot_offsets;
|
|
|
|
void add_values(const T* values, uint32_t num) {
|
|
if (slot_offsets.empty()) {
|
|
slot_offsets.push_back(0);
|
|
}
|
|
if (num > 0) {
|
|
slot_values.insert(slot_values.end(), values, values + num);
|
|
}
|
|
slot_offsets.push_back(static_cast<uint32_t>(slot_values.size()));
|
|
}
|
|
T* get_values(int idx, size_t* size) {
|
|
uint32_t& offset = slot_offsets[idx];
|
|
(*size) = slot_offsets[idx + 1] - offset;
|
|
return &slot_values[offset];
|
|
}
|
|
void add_slot_feasigns(const std::vector<std::vector<T>>& slot_feasigns,
|
|
uint32_t fea_num) {
|
|
slot_values.reserve(fea_num);
|
|
int slot_num = static_cast<int>(slot_feasigns.size());
|
|
slot_offsets.resize(slot_num + 1);
|
|
for (int i = 0; i < slot_num; ++i) {
|
|
auto& slot_val = slot_feasigns[i];
|
|
slot_offsets[i] = static_cast<uint32_t>(slot_values.size());
|
|
uint32_t num = static_cast<uint32_t>(slot_val.size());
|
|
if (num > 0) {
|
|
slot_values.insert(slot_values.end(), slot_val.begin(), slot_val.end());
|
|
}
|
|
}
|
|
slot_offsets[slot_num] = slot_values.size();
|
|
}
|
|
void clear(bool shrink) {
|
|
slot_offsets.clear();
|
|
slot_values.clear();
|
|
if (shrink) {
|
|
slot_values.shrink_to_fit();
|
|
slot_offsets.shrink_to_fit();
|
|
}
|
|
}
|
|
};
|
|
union FeatureFeasign {
|
|
uint64_t uint64_feasign_;
|
|
float float_feasign_;
|
|
};
|
|
|
|
struct FeatureItem {
|
|
FeatureItem() {}
|
|
FeatureItem(FeatureFeasign sign, uint16_t slot) {
|
|
this->sign() = sign;
|
|
this->slot() = slot;
|
|
}
|
|
FeatureFeasign& sign() {
|
|
return *(reinterpret_cast<FeatureFeasign*>(sign_buffer()));
|
|
}
|
|
const FeatureFeasign& sign() const {
|
|
const FeatureFeasign* ret =
|
|
reinterpret_cast<FeatureFeasign*>(sign_buffer());
|
|
return *ret;
|
|
}
|
|
uint16_t& slot() { return slot_; }
|
|
const uint16_t& slot() const { return slot_; }
|
|
|
|
private:
|
|
char* sign_buffer() const { return const_cast<char*>(sign_); }
|
|
char sign_[sizeof(FeatureFeasign)];
|
|
uint16_t slot_;
|
|
};
|
|
|
|
struct AllSlotInfo {
|
|
std::string slot;
|
|
std::string type;
|
|
int used_idx;
|
|
int slot_value_idx;
|
|
};
|
|
struct UsedSlotInfo {
|
|
int idx;
|
|
int slot_value_idx;
|
|
std::string slot;
|
|
std::string type;
|
|
bool dense;
|
|
std::vector<int> local_shape;
|
|
int total_dims_without_inductive;
|
|
int inductive_shape_index;
|
|
};
|
|
struct SlotRecordObject {
|
|
uint64_t search_id;
|
|
uint32_t rank;
|
|
uint32_t cmatch;
|
|
std::string ins_id_;
|
|
SlotValues<uint64_t> slot_uint64_feasigns_;
|
|
SlotValues<float> slot_float_feasigns_;
|
|
|
|
~SlotRecordObject() { clear(true); }
|
|
void reset(void) { clear(FLAGS_enable_slotrecord_reset_shrink); }
|
|
void clear(bool shrink) {
|
|
slot_uint64_feasigns_.clear(shrink);
|
|
slot_float_feasigns_.clear(shrink);
|
|
}
|
|
};
|
|
using SlotRecord = SlotRecordObject*;
|
|
// sizeof Record is much less than std::vector<MultiSlotType>
|
|
struct Record {
|
|
std::vector<FeatureItem> uint64_feasigns_;
|
|
std::vector<FeatureItem> float_feasigns_;
|
|
std::string ins_id_;
|
|
std::string content_;
|
|
uint64_t search_id;
|
|
uint32_t rank;
|
|
uint32_t cmatch;
|
|
std::string uid_;
|
|
};
|
|
|
|
inline SlotRecord make_slotrecord() {
|
|
static const size_t slot_record_byte_size = sizeof(SlotRecordObject);
|
|
void* p = malloc(slot_record_byte_size);
|
|
new (p) SlotRecordObject;
|
|
return reinterpret_cast<SlotRecordObject*>(p);
|
|
}
|
|
|
|
inline void free_slotrecord(SlotRecordObject* p) {
|
|
p->~SlotRecordObject();
|
|
free(p);
|
|
}
|
|
|
|
template <class T>
|
|
class SlotObjAllocator {
|
|
public:
|
|
explicit SlotObjAllocator(std::function<void(T*)> deleter)
|
|
: free_nodes_(NULL), capacity_(0), deleter_(deleter) {}
|
|
~SlotObjAllocator() { clear(); }
|
|
|
|
void clear() {
|
|
T* tmp = NULL;
|
|
while (free_nodes_ != NULL) {
|
|
tmp = reinterpret_cast<T*>(reinterpret_cast<void*>(free_nodes_));
|
|
free_nodes_ = free_nodes_->next;
|
|
deleter_(tmp);
|
|
--capacity_;
|
|
}
|
|
PADDLE_ENFORCE_EQ(capacity_,
|
|
static_cast<size_t>(0),
|
|
common::errors::InvalidArgument(
|
|
"There still are some nodes are not deleted"));
|
|
}
|
|
T* acquire(void) {
|
|
T* x = NULL;
|
|
x = reinterpret_cast<T*>(reinterpret_cast<void*>(free_nodes_));
|
|
free_nodes_ = free_nodes_->next;
|
|
--capacity_;
|
|
return x;
|
|
}
|
|
void release(T* x) {
|
|
Node* node = reinterpret_cast<Node*>(reinterpret_cast<void*>(x));
|
|
node->next = free_nodes_;
|
|
free_nodes_ = node;
|
|
++capacity_;
|
|
}
|
|
size_t capacity(void) { return capacity_; }
|
|
|
|
private:
|
|
struct alignas(T) Node {
|
|
union {
|
|
Node* next;
|
|
char data[sizeof(T)];
|
|
};
|
|
};
|
|
Node* free_nodes_; // a list
|
|
size_t capacity_;
|
|
std::function<void(T*)> deleter_ = nullptr;
|
|
};
|
|
static const int OBJPOOL_BLOCK_SIZE = 10000;
|
|
class SlotObjPool {
|
|
public:
|
|
SlotObjPool()
|
|
: max_capacity_(FLAGS_record_pool_max_size), alloc_(free_slotrecord) {
|
|
ins_chan_ = MakeChannel<SlotRecord>();
|
|
ins_chan_->SetBlockSize(OBJPOOL_BLOCK_SIZE);
|
|
for (int i = 0; i < FLAGS_slotpool_thread_num; ++i) {
|
|
threads_.push_back(std::thread([this]() { run(); }));
|
|
}
|
|
disable_pool_ = false;
|
|
count_ = 0;
|
|
}
|
|
~SlotObjPool() {
|
|
ins_chan_->Close();
|
|
for (auto& t : threads_) {
|
|
t.join();
|
|
}
|
|
}
|
|
void disable_pool(bool disable) { disable_pool_ = disable; }
|
|
void set_max_capacity(size_t max_capacity) { max_capacity_ = max_capacity; }
|
|
void get(std::vector<SlotRecord>* output, int n) {
|
|
output->resize(n);
|
|
return get(&(*output)[0], n);
|
|
}
|
|
void get(SlotRecord* output, int n) {
|
|
int size = 0;
|
|
mutex_.lock();
|
|
int left = static_cast<int>(alloc_.capacity());
|
|
if (left > 0) {
|
|
size = (left >= n) ? n : left;
|
|
for (int i = 0; i < size; ++i) {
|
|
output[i] = alloc_.acquire();
|
|
}
|
|
}
|
|
mutex_.unlock();
|
|
count_ += n;
|
|
if (size == n) {
|
|
return;
|
|
}
|
|
for (int i = size; i < n; ++i) {
|
|
output[i] = make_slotrecord();
|
|
}
|
|
}
|
|
void put(std::vector<SlotRecord>* input) {
|
|
size_t size = input->size();
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
put(&(*input)[0], size);
|
|
input->clear();
|
|
}
|
|
void put(SlotRecord* input, size_t size) {
|
|
PADDLE_ENFORCE_EQ(ins_chan_->WriteMove(size, input),
|
|
size,
|
|
common::errors::InvalidArgument(
|
|
"Incompatible size of input with given size"));
|
|
}
|
|
void run(void) {
|
|
std::vector<SlotRecord> input;
|
|
while (ins_chan_->ReadOnce(input, OBJPOOL_BLOCK_SIZE)) {
|
|
if (input.empty()) {
|
|
continue;
|
|
}
|
|
// over max capacity
|
|
size_t n = input.size();
|
|
count_ -= n;
|
|
if (disable_pool_ || n + capacity() > max_capacity_) {
|
|
for (auto& t : input) {
|
|
free_slotrecord(t);
|
|
}
|
|
} else {
|
|
for (auto& t : input) {
|
|
t->reset();
|
|
}
|
|
mutex_.lock();
|
|
for (auto& t : input) {
|
|
alloc_.release(t);
|
|
}
|
|
mutex_.unlock();
|
|
}
|
|
input.clear();
|
|
}
|
|
}
|
|
void clear(void) {
|
|
platform::Timer timeline;
|
|
timeline.Start();
|
|
mutex_.lock();
|
|
alloc_.clear();
|
|
mutex_.unlock();
|
|
// wait release channel data
|
|
if (FLAGS_enable_slotpool_wait_release) {
|
|
while (!ins_chan_->Empty()) {
|
|
sleep(1);
|
|
}
|
|
}
|
|
timeline.Pause();
|
|
VLOG(3) << "clear slot pool data size=" << count_.load()
|
|
<< ", span=" << timeline.ElapsedSec();
|
|
}
|
|
size_t capacity(void) {
|
|
mutex_.lock();
|
|
size_t total = alloc_.capacity();
|
|
mutex_.unlock();
|
|
return total;
|
|
}
|
|
|
|
private:
|
|
size_t max_capacity_;
|
|
Channel<SlotRecord> ins_chan_;
|
|
std::vector<std::thread> threads_;
|
|
std::mutex mutex_;
|
|
SlotObjAllocator<SlotRecordObject> alloc_;
|
|
bool disable_pool_;
|
|
std::atomic<long> count_; // NOLINT
|
|
};
|
|
|
|
inline SlotObjPool& SlotRecordPool() {
|
|
static SlotObjPool pool;
|
|
return pool;
|
|
}
|
|
struct PvInstanceObject {
|
|
std::vector<Record*> ads;
|
|
void merge_instance(Record* ins) { ads.push_back(ins); }
|
|
};
|
|
|
|
using PvInstance = PvInstanceObject*;
|
|
|
|
inline PvInstance make_pv_instance() { return new PvInstanceObject(); }
|
|
|
|
struct SlotConf {
|
|
std::string name;
|
|
std::string type;
|
|
int use_slots_index;
|
|
int use_slots_is_dense;
|
|
};
|
|
|
|
class CustomParser {
|
|
public:
|
|
CustomParser() {}
|
|
virtual ~CustomParser() {}
|
|
virtual void Init(const std::vector<SlotConf>& slots) = 0;
|
|
virtual bool Init(const std::vector<AllSlotInfo>& slots) = 0;
|
|
virtual bool PreLoad(const std::vector<AllSlotInfo>& slots) { return true; }
|
|
virtual void Reset() {}
|
|
virtual void ParseOneInstance(const char* str, Record* instance) = 0;
|
|
virtual int ParseInstance(int len UNUSED,
|
|
const char* str UNUSED,
|
|
std::vector<Record>* instances UNUSED) {
|
|
return 0;
|
|
}
|
|
virtual bool ParseOneInstance(
|
|
const std::string& line UNUSED,
|
|
std::function<void(std::vector<SlotRecord>&, int)> GetInsFunc
|
|
UNUSED) { // NOLINT
|
|
return true;
|
|
}
|
|
virtual bool ParseFileInstance(
|
|
std::function<int(char* buf, int len)> ReadBuffFunc UNUSED,
|
|
std::function<void(std::vector<SlotRecord>&, int, int)> PullRecordsFunc
|
|
UNUSED, // NOLINT
|
|
int& lines UNUSED) { // NOLINT
|
|
return false;
|
|
}
|
|
};
|
|
|
|
struct UsedSlotGpuType {
|
|
int is_uint64_value;
|
|
int slot_value_idx;
|
|
};
|
|
|
|
#if defined(PADDLE_WITH_CUDA) && defined(PADDLE_WITH_HETERPS)
|
|
template <typename T>
|
|
struct CudaBuffer {
|
|
T* cu_buffer;
|
|
uint64_t buf_size;
|
|
|
|
CudaBuffer<T>() {
|
|
cu_buffer = NULL;
|
|
buf_size = 0;
|
|
}
|
|
~CudaBuffer<T>() { free(); }
|
|
T* data() { return cu_buffer; }
|
|
uint64_t size() { return buf_size; }
|
|
void malloc(uint64_t size) {
|
|
buf_size = size;
|
|
CUDA_CHECK(
|
|
cudaMalloc(reinterpret_cast<void**>(&cu_buffer), size * sizeof(T)));
|
|
}
|
|
void free() {
|
|
if (cu_buffer != NULL) {
|
|
CUDA_CHECK(cudaFree(cu_buffer));
|
|
cu_buffer = NULL;
|
|
}
|
|
buf_size = 0;
|
|
}
|
|
void resize(uint64_t size) {
|
|
if (size <= buf_size) {
|
|
return;
|
|
}
|
|
free();
|
|
malloc(size);
|
|
}
|
|
};
|
|
template <typename T>
|
|
struct HostBuffer {
|
|
T* host_buffer;
|
|
size_t buf_size;
|
|
size_t data_len;
|
|
|
|
HostBuffer<T>() {
|
|
host_buffer = NULL;
|
|
buf_size = 0;
|
|
data_len = 0;
|
|
}
|
|
~HostBuffer<T>() { free(); }
|
|
|
|
T* data() { return host_buffer; }
|
|
const T* data() const { return host_buffer; }
|
|
size_t size() const { return data_len; }
|
|
void clear() { free(); }
|
|
T& back() { return host_buffer[data_len - 1]; }
|
|
|
|
T& operator[](size_t i) { return host_buffer[i]; }
|
|
const T& operator[](size_t i) const { return host_buffer[i]; }
|
|
void malloc(size_t len) {
|
|
buf_size = len;
|
|
CUDA_CHECK(cudaHostAlloc(reinterpret_cast<void**>(&host_buffer),
|
|
buf_size * sizeof(T),
|
|
cudaHostAllocDefault));
|
|
PADDLE_ENFORCE_NOT_NULL(host_buffer,
|
|
common::errors::ResourceExhausted(
|
|
"Alloc memory failed on CUDA, please Check"));
|
|
}
|
|
void free() {
|
|
if (host_buffer != NULL) {
|
|
CUDA_CHECK(cudaFreeHost(host_buffer));
|
|
host_buffer = NULL;
|
|
}
|
|
buf_size = 0;
|
|
}
|
|
void resize(size_t size) {
|
|
if (size <= buf_size) {
|
|
data_len = size;
|
|
return;
|
|
}
|
|
data_len = size;
|
|
free();
|
|
malloc(size);
|
|
}
|
|
};
|
|
|
|
struct BatchCPUValue {
|
|
HostBuffer<int> h_uint64_lens;
|
|
HostBuffer<uint64_t> h_uint64_keys;
|
|
HostBuffer<int> h_uint64_offset;
|
|
|
|
HostBuffer<int> h_float_lens;
|
|
HostBuffer<float> h_float_keys;
|
|
HostBuffer<int> h_float_offset;
|
|
|
|
HostBuffer<int> h_rank;
|
|
HostBuffer<int> h_cmatch;
|
|
HostBuffer<int> h_ad_offset;
|
|
};
|
|
|
|
struct BatchGPUValue {
|
|
CudaBuffer<int> d_uint64_lens;
|
|
CudaBuffer<uint64_t> d_uint64_keys;
|
|
CudaBuffer<int> d_uint64_offset;
|
|
|
|
CudaBuffer<int> d_float_lens;
|
|
CudaBuffer<float> d_float_keys;
|
|
CudaBuffer<int> d_float_offset;
|
|
|
|
CudaBuffer<int> d_rank;
|
|
CudaBuffer<int> d_cmatch;
|
|
CudaBuffer<int> d_ad_offset;
|
|
};
|
|
|
|
class MiniBatchGpuPack {
|
|
public:
|
|
MiniBatchGpuPack(const phi::Place& place,
|
|
const std::vector<UsedSlotInfo>& infos,
|
|
phi::StreamId stream_id);
|
|
~MiniBatchGpuPack();
|
|
bool is_use() { return is_using_; }
|
|
void set_use_flag(bool is_use) { is_using_ = is_use; }
|
|
void reset(const phi::Place& place);
|
|
void pack_instance(const SlotRecord* ins_vec, int num);
|
|
int ins_num() { return ins_num_; }
|
|
int pv_num() { return pv_num_; }
|
|
BatchGPUValue& value() { return value_; }
|
|
BatchCPUValue& cpu_value() { return buf_; }
|
|
UsedSlotGpuType* get_gpu_slots(void) {
|
|
return reinterpret_cast<UsedSlotGpuType*>(gpu_slots_.data());
|
|
}
|
|
SlotRecord* get_records(void) { return &ins_vec_[0]; }
|
|
|
|
// tensor gpu memory reused
|
|
void resize_tensor(void) {
|
|
if (used_float_num_ > 0) {
|
|
int float_total_len = buf_.h_float_lens.back();
|
|
if (float_total_len > 0) {
|
|
float_tensor_.mutable_data<float>({float_total_len, 1}, this->place_);
|
|
}
|
|
}
|
|
if (used_uint64_num_ > 0) {
|
|
int uint64_total_len = buf_.h_uint64_lens.back();
|
|
if (uint64_total_len > 0) {
|
|
uint64_tensor_.mutable_data<int64_t>({uint64_total_len, 1},
|
|
this->place_);
|
|
}
|
|
}
|
|
}
|
|
phi::DenseTensor& float_tensor(void) { return float_tensor_; }
|
|
phi::DenseTensor& uint64_tensor(void) { return uint64_tensor_; }
|
|
std::vector<DenseTensor>& float_tensor_vec(void) { return float_tensor_vec_; }
|
|
std::vector<DenseTensor>& uint64_tensor_vec(void) {
|
|
return uint64_tensor_vec_;
|
|
}
|
|
|
|
HostBuffer<size_t>& offsets(void) { return offsets_; }
|
|
HostBuffer<void*>& h_tensor_ptrs(void) { return h_tensor_ptrs_; }
|
|
|
|
void* gpu_slot_offsets(void) { return gpu_slot_offsets_->ptr(); }
|
|
|
|
void* slot_buf_ptr(void) { return slot_buf_ptr_->ptr(); }
|
|
|
|
void resize_gpu_slot_offsets(const size_t slot_total_bytes) {
|
|
if (gpu_slot_offsets_ == nullptr) {
|
|
gpu_slot_offsets_ = memory::AllocShared(place_, slot_total_bytes);
|
|
} else if (gpu_slot_offsets_->size() < slot_total_bytes) {
|
|
auto buf = memory::AllocShared(place_, slot_total_bytes);
|
|
gpu_slot_offsets_.swap(buf);
|
|
buf = nullptr;
|
|
}
|
|
}
|
|
const std::string& get_lineid(int idx) {
|
|
if (enable_pv_) {
|
|
return ins_vec_[idx]->ins_id_;
|
|
}
|
|
return batch_ins_[idx]->ins_id_;
|
|
}
|
|
|
|
cudaStream_t get_stream() { return stream_; }
|
|
|
|
private:
|
|
void transfer_to_gpu(void);
|
|
void pack_all_data(const SlotRecord* ins_vec, int num);
|
|
void pack_uint64_data(const SlotRecord* ins_vec, int num);
|
|
void pack_float_data(const SlotRecord* ins_vec, int num);
|
|
|
|
public:
|
|
template <typename T>
|
|
void copy_host2device(CudaBuffer<T>* buf, const T* val, size_t size) {
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
buf->resize(size);
|
|
CUDA_CHECK(cudaMemcpyAsync(
|
|
buf->data(), val, size * sizeof(T), cudaMemcpyHostToDevice, stream_));
|
|
}
|
|
template <typename T>
|
|
void copy_host2device(CudaBuffer<T>* buf, const HostBuffer<T>& val) {
|
|
copy_host2device(buf, val.data(), val.size());
|
|
}
|
|
|
|
private:
|
|
bool is_using_ = false;
|
|
phi::Place place_;
|
|
std::unique_ptr<phi::CUDAStream> stream_holder_;
|
|
cudaStream_t stream_;
|
|
BatchGPUValue value_;
|
|
BatchCPUValue buf_;
|
|
int ins_num_ = 0;
|
|
int pv_num_ = 0;
|
|
|
|
bool enable_pv_ = false;
|
|
int used_float_num_ = 0;
|
|
int used_uint64_num_ = 0;
|
|
int used_slot_size_ = 0;
|
|
|
|
CudaBuffer<UsedSlotGpuType> gpu_slots_;
|
|
std::vector<UsedSlotGpuType> gpu_used_slots_;
|
|
std::vector<SlotRecord> ins_vec_;
|
|
const SlotRecord* batch_ins_ = nullptr;
|
|
|
|
// uint64 tensor
|
|
DenseTensor uint64_tensor_;
|
|
std::vector<DenseTensor> uint64_tensor_vec_;
|
|
// float tensor
|
|
DenseTensor float_tensor_;
|
|
std::vector<DenseTensor> float_tensor_vec_;
|
|
// batch
|
|
HostBuffer<size_t> offsets_;
|
|
HostBuffer<void*> h_tensor_ptrs_;
|
|
|
|
std::shared_ptr<phi::Allocation> gpu_slot_offsets_ = nullptr;
|
|
std::shared_ptr<phi::Allocation> slot_buf_ptr_ = nullptr;
|
|
};
|
|
class MiniBatchGpuPackMgr {
|
|
static const int MAX_DEVICE_NUM = 16;
|
|
|
|
public:
|
|
MiniBatchGpuPackMgr() {
|
|
pack_list_.resize(MAX_DEVICE_NUM);
|
|
for (int i = 0; i < MAX_DEVICE_NUM; ++i) {
|
|
pack_list_[i].clear();
|
|
}
|
|
}
|
|
~MiniBatchGpuPackMgr() {
|
|
for (int i = 0; i < MAX_DEVICE_NUM; ++i) {
|
|
for (size_t j = 0; j < pack_list_[i].size(); j++) {
|
|
if (pack_list_[i][j] == nullptr) {
|
|
continue;
|
|
}
|
|
delete pack_list_[i][j];
|
|
pack_list_[i][j] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// thread unsafe
|
|
MiniBatchGpuPack* get(const phi::Place& place,
|
|
const std::vector<UsedSlotInfo>& infos) {
|
|
int device_id = place.GetDeviceId();
|
|
for (size_t i = 0; i < pack_list_[device_id].size(); i++) {
|
|
if (!pack_list_[device_id][i]->is_use()) {
|
|
pack_list_[device_id][i]->set_use_flag(true);
|
|
pack_list_[device_id][i]->reset(place);
|
|
return pack_list_[device_id][i];
|
|
}
|
|
}
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
if (!alloc_stream_map_.count(device_id)) {
|
|
alloc_stream_map_.emplace(device_id, new phi::CUDAStream(place));
|
|
}
|
|
}
|
|
phi::StreamId alloc_stream_id = reinterpret_cast<phi::StreamId>(
|
|
alloc_stream_map_[device_id]->raw_stream());
|
|
auto* pack = new MiniBatchGpuPack(place, infos, alloc_stream_id);
|
|
pack->set_use_flag(true);
|
|
pack_list_[device_id].push_back(pack);
|
|
return pack;
|
|
}
|
|
|
|
private:
|
|
std::vector<std::vector<MiniBatchGpuPack*>> pack_list_;
|
|
std::unordered_map<int, std::unique_ptr<phi::CUDAStream>> alloc_stream_map_;
|
|
std::mutex mutex_;
|
|
};
|
|
// global mgr
|
|
inline MiniBatchGpuPackMgr& BatchGpuPackMgr() {
|
|
static MiniBatchGpuPackMgr mgr;
|
|
return mgr;
|
|
}
|
|
#endif
|
|
|
|
typedef paddle::framework::CustomParser* (*CreateParserObjectFunc)();
|
|
|
|
class DLManager {
|
|
struct DLHandle {
|
|
void* module;
|
|
paddle::framework::CustomParser* parser;
|
|
};
|
|
|
|
public:
|
|
DLManager() {}
|
|
|
|
~DLManager() {
|
|
#ifdef _LINUX
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
for (auto it = handle_map_.begin(); it != handle_map_.end(); ++it) {
|
|
delete it->second.parser;
|
|
dlclose(it->second.module);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool Close(const std::string& name) {
|
|
#ifdef _LINUX
|
|
auto it = handle_map_.find(name);
|
|
if (it == handle_map_.end()) {
|
|
return true;
|
|
}
|
|
delete it->second.parser;
|
|
dlclose(it->second.module);
|
|
#endif
|
|
VLOG(0) << "Not implement in windows";
|
|
return false;
|
|
}
|
|
|
|
paddle::framework::CustomParser* Load(const std::string& name,
|
|
const std::vector<SlotConf>& conf) {
|
|
#ifdef _LINUX
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
DLHandle handle;
|
|
std::map<std::string, DLHandle>::iterator it = handle_map_.find(name);
|
|
if (it != handle_map_.end()) {
|
|
return it->second.parser;
|
|
}
|
|
|
|
handle.module = dlopen(name.c_str(), RTLD_NOW);
|
|
if (handle.module == nullptr) {
|
|
VLOG(0) << "Create so of " << name << " fail, " << dlerror();
|
|
return nullptr;
|
|
}
|
|
|
|
CreateParserObjectFunc create_parser_func =
|
|
(CreateParserObjectFunc)dlsym(handle.module, "CreateParserObject");
|
|
handle.parser = create_parser_func();
|
|
handle.parser->Init(conf);
|
|
handle_map_.insert({name, handle});
|
|
|
|
return handle.parser;
|
|
#endif
|
|
VLOG(0) << "Not implement in windows";
|
|
return nullptr;
|
|
}
|
|
|
|
paddle::framework::CustomParser* Load(const std::string& name,
|
|
const std::vector<AllSlotInfo>& conf) {
|
|
#ifdef _LINUX
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
DLHandle handle;
|
|
std::map<std::string, DLHandle>::iterator it = handle_map_.find(name);
|
|
if (it != handle_map_.end()) {
|
|
return it->second.parser;
|
|
}
|
|
handle.module = dlopen(name.c_str(), RTLD_NOW);
|
|
if (handle.module == nullptr) {
|
|
VLOG(0) << "Create so of " << name << " fail";
|
|
exit(-1);
|
|
return nullptr;
|
|
}
|
|
|
|
CreateParserObjectFunc create_parser_func =
|
|
(CreateParserObjectFunc)dlsym(handle.module, "CreateParserObject");
|
|
handle.parser = create_parser_func();
|
|
handle.parser->Init(conf);
|
|
handle_map_.insert({name, handle});
|
|
|
|
return handle.parser;
|
|
#endif
|
|
VLOG(0) << "Not implement in windows";
|
|
return nullptr;
|
|
}
|
|
|
|
paddle::framework::CustomParser* ReLoad(const std::string& name,
|
|
const std::vector<SlotConf>& conf) {
|
|
Close(name);
|
|
return Load(name, conf);
|
|
}
|
|
|
|
private:
|
|
std::mutex mutex_;
|
|
std::map<std::string, DLHandle> handle_map_;
|
|
};
|
|
|
|
struct engine_wrapper_t {
|
|
std::default_random_engine engine;
|
|
#if !defined(_WIN32)
|
|
engine_wrapper_t() {
|
|
struct timespec tp;
|
|
clock_gettime(CLOCK_REALTIME, &tp);
|
|
double cur_time = tp.tv_sec + tp.tv_nsec * 1e-9;
|
|
static std::atomic<uint64_t> x(0);
|
|
std::seed_seq sseq = {x++, x++, x++, (uint64_t)(cur_time * 1000)};
|
|
engine.seed(sseq);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
struct BufState {
|
|
int left;
|
|
int right;
|
|
int central_word;
|
|
int step;
|
|
engine_wrapper_t random_engine_;
|
|
|
|
int len;
|
|
int cursor;
|
|
int row_num;
|
|
|
|
int batch_size;
|
|
int walk_len;
|
|
std::vector<int>* window;
|
|
|
|
BufState() {}
|
|
~BufState() {}
|
|
|
|
void Init(int graph_batch_size,
|
|
int graph_walk_len,
|
|
std::vector<int>* graph_window) {
|
|
batch_size = graph_batch_size;
|
|
walk_len = graph_walk_len;
|
|
window = graph_window;
|
|
|
|
left = 0;
|
|
right = window->size() - 1;
|
|
central_word = -1;
|
|
step = -1;
|
|
|
|
len = 0;
|
|
cursor = 0;
|
|
row_num = 0;
|
|
for (size_t i = 0; i < graph_window->size(); i++) {
|
|
VLOG(2) << "graph_window[" << i << "] = " << (*graph_window)[i];
|
|
}
|
|
}
|
|
|
|
void Reset(int total_rows) {
|
|
cursor = 0;
|
|
row_num = total_rows;
|
|
int tmp_len = cursor + batch_size > row_num ? row_num - cursor : batch_size;
|
|
len = tmp_len;
|
|
central_word = -1;
|
|
step = -1;
|
|
GetNextCentralWord();
|
|
}
|
|
|
|
int GetNextStep() {
|
|
step++;
|
|
// Checking out-of-bound by MakeInsPair
|
|
if (step <= right) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Debug() {
|
|
VLOG(2) << "left: " << left << " right: " << right
|
|
<< " central_word: " << central_word << " step: " << step
|
|
<< " cursor: " << cursor << " len: " << len
|
|
<< " row_num: " << row_num;
|
|
}
|
|
|
|
int GetNextCentralWord() {
|
|
if (++central_word >= walk_len) {
|
|
return 0;
|
|
}
|
|
int window_size = window->size() / 2;
|
|
int random_window = random_engine_.engine() % window_size + 1;
|
|
left = window_size - random_window;
|
|
right = window_size + random_window - 1;
|
|
VLOG(2) << "random window: " << random_window << " window[" << left
|
|
<< "] = " << (*window)[left] << " window[" << right
|
|
<< "] = " << (*window)[right];
|
|
// Checking out-of-bound by MakeInsPair
|
|
step = left;
|
|
return 1;
|
|
}
|
|
|
|
int GetNextBatch() {
|
|
cursor += len;
|
|
if (row_num - cursor < 0) {
|
|
return 0;
|
|
}
|
|
int tmp_len = cursor + batch_size > row_num ? row_num - cursor : batch_size;
|
|
if (tmp_len == 0) {
|
|
return 0;
|
|
}
|
|
len = tmp_len;
|
|
central_word = -1;
|
|
step = -1;
|
|
GetNextCentralWord();
|
|
return tmp_len != 0;
|
|
}
|
|
};
|
|
|
|
/// Related behaviors and events during sampling
|
|
const int EVENT_FINISH_EPOCH = 0; // End of sampling single epoch
|
|
const int EVENT_CONTINUE_SAMPLE = 1; // Continue sampling
|
|
const int EVENT_WALKBUF_FULL = 2; // d_walk is full, end current pass sampling
|
|
const int EVENT_NOT_SWITCH = 0; // Continue sampling on the current metapath.
|
|
const int EVENT_SWITCH_METAPATH =
|
|
1; // Switch to the next metapath to perform sampling
|
|
|
|
struct GraphDataGeneratorConfig {
|
|
bool need_walk_ntype;
|
|
bool enable_pair_label;
|
|
bool gpu_graph_training;
|
|
bool sage_mode;
|
|
bool get_degree;
|
|
bool weighted_sample;
|
|
bool return_weight;
|
|
bool is_multi_node;
|
|
bool is_thread_sharding;
|
|
int batch_size;
|
|
int slot_num;
|
|
int walk_degree;
|
|
int walk_len;
|
|
int window;
|
|
int gpuid;
|
|
int thread_id;
|
|
int once_sample_startid_len;
|
|
int node_type_num;
|
|
int debug_mode;
|
|
int excluded_train_pair_len;
|
|
int edge_to_id_len;
|
|
int tensor_pair_num;
|
|
uint32_t tensor_num_of_one_pair;
|
|
uint32_t tensor_num_of_one_subgraph;
|
|
size_t buf_size;
|
|
size_t once_max_sample_keynum;
|
|
int64_t reindex_table_size;
|
|
uint64_t train_table_cap;
|
|
uint64_t infer_table_cap;
|
|
int accumulate_num;
|
|
std::vector<int> window_step;
|
|
std::vector<int> samples;
|
|
std::shared_ptr<phi::Allocation> d_excluded_train_pair;
|
|
std::shared_ptr<phi::Allocation> d_pair_label_conf;
|
|
std::set<int> infer_node_type_index_set;
|
|
};
|
|
|
|
class GraphDataGenerator {
|
|
public:
|
|
GraphDataGenerator() {}
|
|
virtual ~GraphDataGenerator() {}
|
|
void SetConfig(const paddle::framework::DataFeedDesc& data_feed_desc);
|
|
void AllocResource(int thread_id, std::vector<phi::DenseTensor*> feed_vec);
|
|
void AllocTrainResource(int thread_id);
|
|
void SetFeedVec(std::vector<phi::DenseTensor*> feed_vec);
|
|
void SetFeedInfo(std::vector<UsedSlotInfo>* feed_info);
|
|
int GenerateBatch();
|
|
void DoWalkandSage();
|
|
int FillSlotFeature(uint64_t* d_walk);
|
|
int FillIdShowClkTensor(int total_instance, bool gpu_graph_training);
|
|
int FillGraphIdShowClkTensor(int uniq_instance,
|
|
int total_instance,
|
|
int index);
|
|
int FillGraphIdShowClkTensorAccum(int index);
|
|
int FillGraphSlotFeature(
|
|
int total_instance,
|
|
bool gpu_graph_training,
|
|
std::shared_ptr<phi::Allocation> final_sage_nodes = nullptr);
|
|
int FillGraphSlotFeatureAccum(bool gpu_graph_training, int index);
|
|
int FillSlotFeature(uint64_t* d_walk,
|
|
size_t key_num,
|
|
int tensor_pair_idx,
|
|
int accum = 0);
|
|
int FillFloatFeature(uint64_t* d_walk, size_t key_num, int tensor_pair_idx);
|
|
int GetPathNum() { return total_row_[0]; }
|
|
void ResetPathNum() { total_row_[0] = 0; }
|
|
int GetGraphBatchsize() { return conf_.batch_size; }
|
|
void SetNewBatchsize(int batch_num) {
|
|
if (!conf_.gpu_graph_training) {
|
|
conf_.batch_size = (total_row_[0] + batch_num - 1) / batch_num;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
bool GetSageMode() { return conf_.sage_mode; }
|
|
bool GetMultiNodeMode() { return conf_.is_multi_node; }
|
|
bool GetTrainState() { return conf_.gpu_graph_training; }
|
|
void ResetEpochFinish() { epoch_finish_ = false; }
|
|
void reset_pass_end() { pass_end_ = 0; }
|
|
void ClearSampleState();
|
|
void DumpWalkPath(std::string dump_path, size_t dump_rate);
|
|
void DumpSampleNeighbors(std::string dump_path);
|
|
void SetDeviceKeys(std::vector<uint64_t>* device_keys UNUSED,
|
|
int type UNUSED) {
|
|
// type_to_index_[type] = h_device_keys_.size();
|
|
// h_device_keys_.push_back(device_keys);
|
|
}
|
|
int GetTrainMemoryDataSize() {
|
|
// use only for is_multi_node = True, sage_mode = True, gpu_graph_training =
|
|
// True
|
|
if (!global_train_flag_) {
|
|
return 0;
|
|
} else {
|
|
return total_row_[0];
|
|
}
|
|
}
|
|
std::vector<uint64_t>& GetHostVec() { return host_vec_; }
|
|
std::vector<uint32_t>& GetHostRanks() { return host_ranks_; }
|
|
std::shared_ptr<HashTable<uint64_t, uint32_t>> GetKeys2RankTable() {
|
|
return keys2rank_table_;
|
|
}
|
|
|
|
bool get_epoch_finish() { return epoch_finish_; }
|
|
int get_pass_end() { return pass_end_; }
|
|
void clear_gpu_mem();
|
|
int dynamic_adjust_batch_num_for_sage();
|
|
|
|
protected:
|
|
bool DoWalkForInfer();
|
|
void DoSageForInfer();
|
|
bool DoWalkForTrain();
|
|
void DoSageForTrain();
|
|
|
|
// key: key id,
|
|
// value: dest machine rank id
|
|
// Two usage: dup keys and cache dest machine rank of keys
|
|
std::shared_ptr<HashTable<uint64_t, uint32_t>> keys2rank_table_;
|
|
GraphDataGeneratorConfig conf_;
|
|
std::vector<size_t> infer_cursor_;
|
|
std::vector<size_t> jump_rows_;
|
|
int64_t* id_tensor_ptr_;
|
|
int* index_tensor_ptr_;
|
|
int64_t* show_tensor_ptr_;
|
|
int64_t* clk_tensor_ptr_;
|
|
int* degree_tensor_ptr_;
|
|
int32_t* pair_label_ptr_;
|
|
|
|
cudaStream_t train_stream_;
|
|
cudaStream_t sample_stream_;
|
|
phi::Place place_;
|
|
std::vector<phi::DenseTensor*> feed_vec_;
|
|
std::vector<UsedSlotInfo>* feed_info_; // adapt for float feature
|
|
std::vector<size_t> offset_;
|
|
std::vector<std::vector<std::shared_ptr<phi::Allocation>>> d_device_keys_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_train_metapath_keys_;
|
|
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_walk_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_walk_ntype_;
|
|
std::shared_ptr<phi::Allocation> d_feature_list_;
|
|
std::shared_ptr<phi::Allocation> d_feature_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_random_row_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_random_row_col_shift_;
|
|
std::shared_ptr<phi::Allocation> d_uniq_node_num_;
|
|
std::shared_ptr<phi::Allocation> d_slot_feature_num_map_;
|
|
std::shared_ptr<phi::Allocation> d_actual_slot_id_map_;
|
|
std::shared_ptr<phi::Allocation> d_fea_offset_map_;
|
|
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_pair_label_buf_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_ins_buf_;
|
|
std::shared_ptr<phi::Allocation> d_feature_size_list_buf_;
|
|
std::shared_ptr<phi::Allocation> d_feature_size_prefixsum_buf_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> d_pair_num_;
|
|
std::shared_ptr<phi::Allocation> d_slot_tensor_ptr_;
|
|
std::shared_ptr<phi::Allocation> d_slot_lod_tensor_ptr_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> edge_type_graph_;
|
|
|
|
// sage mode batch data
|
|
std::vector<std::shared_ptr<phi::Allocation>> pair_label_vec_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> inverse_vec_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> final_sage_nodes_vec_;
|
|
std::vector<std::shared_ptr<phi::Allocation>> node_degree_vec_;
|
|
std::vector<int> uniq_instance_vec_;
|
|
std::vector<int> total_instance_vec_;
|
|
std::vector<std::vector<std::shared_ptr<phi::Allocation>>> graph_edges_vec_;
|
|
std::vector<std::vector<std::vector<int>>> edges_split_num_vec_;
|
|
|
|
int sage_batch_count_;
|
|
int sage_batch_num_;
|
|
bool global_train_flag_ = 0;
|
|
std::vector<int> ins_buf_pair_len_;
|
|
int id_offset_of_feed_vec_;
|
|
|
|
// size of a d_walk buf
|
|
int repeat_time_;
|
|
std::vector<BufState> buf_state_;
|
|
int float_slot_num_ = 0; // float slot num
|
|
int uint_slot_num_ = 0; // uint slot num
|
|
std::vector<int> h_slot_feature_num_map_;
|
|
int fea_num_per_node_;
|
|
std::vector<int> shuffle_seed_;
|
|
bool epoch_finish_;
|
|
int pass_end_ = 0;
|
|
std::vector<uint64_t> host_vec_;
|
|
std::vector<uint32_t> host_ranks_;
|
|
std::vector<std::vector<uint64_t>> h_device_keys_len_;
|
|
std::vector<uint64_t> h_train_metapath_keys_len_;
|
|
uint64_t copy_unique_len_;
|
|
std::vector<int> total_row_;
|
|
std::vector<size_t> infer_node_start_;
|
|
std::vector<size_t> infer_node_end_;
|
|
std::string infer_node_type_;
|
|
DenseTensor multi_node_sync_stat_;
|
|
};
|
|
|
|
class DataFeed {
|
|
public:
|
|
DataFeed() {
|
|
mutex_for_pick_file_ = nullptr;
|
|
file_idx_ = nullptr;
|
|
mutex_for_fea_num_ = nullptr;
|
|
total_fea_num_ = nullptr;
|
|
}
|
|
virtual ~DataFeed() {}
|
|
virtual void Init(const DataFeedDesc& data_feed_desc) = 0;
|
|
virtual bool CheckFile(const char* filename UNUSED) {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(CheckFile) is not implemented."));
|
|
}
|
|
// Set filelist for DataFeed.
|
|
// Pay attention that it must init all readers before call this function.
|
|
// Otherwise, Init() function will init finish_set_filelist_ flag.
|
|
virtual bool SetFileList(const std::vector<std::string>& files);
|
|
virtual bool Start() = 0;
|
|
|
|
// The trainer calls the Next() function, and the DataFeed will load a new
|
|
// batch to the feed_vec. The return value of this function is the batch
|
|
// size of the current batch.
|
|
virtual int Next() = 0;
|
|
// Get all slots' alias which defined in protofile
|
|
virtual const std::vector<std::string>& GetAllSlotAlias() {
|
|
return all_slots_;
|
|
}
|
|
// Get used slots' alias which defined in protofile
|
|
virtual const std::vector<std::string>& GetUseSlotAlias() {
|
|
return use_slots_;
|
|
}
|
|
// This function is used for binding feed_vec memory
|
|
virtual void AddFeedVar(Variable* var, const std::string& name);
|
|
|
|
// This function is used for binding feed_vec memory in a given scope
|
|
virtual void AssignFeedVar(const Scope& scope);
|
|
|
|
virtual std::vector<std::string> GetInputVarNames() {
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
// This function will do nothing at default
|
|
virtual void SetInputPvChannel(void* channel UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetOutputPvChannel(void* channel UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetConsumePvChannel(void* channel UNUSED) {}
|
|
|
|
// This function will do nothing at default
|
|
virtual void SetInputChannel(void* channel UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetOutputChannel(void* channel UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetConsumeChannel(void* channel UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetThreadId(int thread_id UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetThreadNum(int thread_num UNUSED) {}
|
|
// This function will do nothing at default
|
|
virtual void SetParseInsId(bool parse_ins_id UNUSED) {}
|
|
virtual void SetParseUid(bool parse_uid UNUSED) {}
|
|
virtual void SetParseContent(bool parse_content UNUSED) {}
|
|
virtual void SetParseLogKey(bool parse_logkey UNUSED) {}
|
|
virtual void SetEnablePvMerge(bool enable_pv_merge UNUSED) {}
|
|
virtual void SetCurrentPhase(int current_phase UNUSED) {}
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
virtual void InitGraphResource() {}
|
|
virtual void InitGraphTrainResource() {}
|
|
virtual void SetDeviceKeys(std::vector<uint64_t>* device_keys, int type) {
|
|
gpu_graph_data_generator_.SetDeviceKeys(device_keys, type);
|
|
}
|
|
#endif
|
|
|
|
virtual void SetGpuGraphMode(int gpu_graph_mode) {
|
|
gpu_graph_mode_ = gpu_graph_mode;
|
|
}
|
|
virtual void SetFileListMutex(std::mutex* mutex) {
|
|
mutex_for_pick_file_ = mutex;
|
|
}
|
|
virtual void SetFeaNumMutex(std::mutex* mutex) { mutex_for_fea_num_ = mutex; }
|
|
virtual void SetFileListIndex(size_t* file_index) { file_idx_ = file_index; }
|
|
virtual void SetFeaNum(uint64_t* fea_num) { total_fea_num_ = fea_num; }
|
|
virtual const std::vector<std::string>& GetInsIdVec() const {
|
|
return ins_id_vec_;
|
|
}
|
|
virtual const std::vector<std::string>& GetInsContentVec() const {
|
|
return ins_content_vec_;
|
|
}
|
|
virtual void SetCurBatchSize(const int batch_size) {
|
|
batch_size_ = batch_size;
|
|
}
|
|
virtual int GetCurBatchSize() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetGraphBatchsize();
|
|
#else
|
|
// gpu_graph_mode_ set true only when
|
|
// PADDLE_WITH_HETERPS=ON and PADDLE_WITH_PSCORE=ON
|
|
VLOG(1) << "Error: GetCurBatchSize() gpu_graph_mode_ "
|
|
<< "set true only when "
|
|
<< "PADDLE_WITH_HETERPS=ON and PADDLE_WITH_PSCORE=ON";
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return batch_size_;
|
|
}
|
|
}
|
|
virtual void SetNewBatchsize(int batch_num) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.SetNewBatchsize(batch_num);
|
|
}
|
|
#endif
|
|
}
|
|
virtual bool GetSageMode() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetSageMode();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
virtual bool GetMultiNodeMode() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetMultiNodeMode();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
virtual int GetGraphPathNum() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetPathNum();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
virtual bool GetTrainState() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetTrainState();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
virtual int GetTrainMemoryDataSize() {
|
|
if (gpu_graph_mode_) {
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
return gpu_graph_data_generator_.GetTrainMemoryDataSize();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
virtual std::vector<uint64_t>* GetHostVec() {
|
|
if (gpu_graph_mode_) {
|
|
return &(gpu_graph_data_generator_.GetHostVec());
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
virtual std::vector<uint32_t>* GetHostRanks() {
|
|
if (gpu_graph_mode_) {
|
|
return &(gpu_graph_data_generator_.GetHostRanks());
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
virtual void clear_gpu_mem() {
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.clear_gpu_mem();
|
|
}
|
|
}
|
|
|
|
virtual bool get_epoch_finish() {
|
|
if (gpu_graph_mode_) {
|
|
return gpu_graph_data_generator_.get_epoch_finish();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
virtual int get_pass_end() {
|
|
if (gpu_graph_mode_) {
|
|
return gpu_graph_data_generator_.get_pass_end();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
virtual void reset_pass_end() {
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.reset_pass_end();
|
|
}
|
|
}
|
|
|
|
virtual void ResetPathNum() {
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.ResetPathNum();
|
|
}
|
|
}
|
|
|
|
virtual void ClearSampleState() {
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.ClearSampleState();
|
|
}
|
|
}
|
|
|
|
virtual void ResetEpochFinish() {
|
|
if (gpu_graph_mode_) {
|
|
gpu_graph_data_generator_.ResetEpochFinish();
|
|
}
|
|
}
|
|
|
|
virtual void DoWalkandSage() {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(DoWalkandSage) is not implemented."));
|
|
}
|
|
|
|
std::shared_ptr<HashTable<uint64_t, uint32_t>> GetKeys2RankTable() {
|
|
if (gpu_graph_mode_) {
|
|
return gpu_graph_data_generator_.GetKeys2RankTable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
virtual bool IsTrainMode() { return train_mode_; }
|
|
virtual void LoadIntoMemory() {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(LoadIntoMemory) is not implemented."));
|
|
}
|
|
virtual void SetPlace(const phi::Place& place) { place_ = place; }
|
|
virtual const phi::Place& GetPlace() const { return place_; }
|
|
|
|
#if defined(PADDLE_WITH_CUDA) && defined(PADDLE_WITH_HETERPS)
|
|
virtual MiniBatchGpuPack* get_pack(MiniBatchGpuPack* last_pack) {
|
|
return nullptr;
|
|
}
|
|
|
|
virtual void PackToScope(MiniBatchGpuPack* pack, const Scope* scope) {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(PackToScope) is not implemented."));
|
|
}
|
|
virtual void SetInsIdVec(MiniBatchGpuPack* pack) {}
|
|
#endif
|
|
|
|
virtual void DumpWalkPath(std::string dump_path UNUSED,
|
|
size_t dump_rate UNUSED) {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(DumpWalkPath) is not implemented."));
|
|
}
|
|
virtual void DumpSampleNeighbors(std::string dump_path) {
|
|
PADDLE_THROW(common::errors::Unimplemented(
|
|
"This function(DumpSampleNeighbors) is not implemented"));
|
|
}
|
|
|
|
protected:
|
|
// The following three functions are used to check if it is executed in this
|
|
// order:
|
|
// Init() -> SetFileList() -> Start() -> Next()
|
|
virtual void CheckInit();
|
|
virtual void CheckSetFileList();
|
|
virtual void CheckStart();
|
|
virtual void SetBatchSize(
|
|
int batch); // batch size will be set in Init() function
|
|
// This function is used to pick one file from the global filelist(thread
|
|
// safe).
|
|
virtual bool PickOneFile(std::string* filename);
|
|
virtual void CopyToFeedTensor(void* dst, const void* src, size_t size);
|
|
|
|
std::vector<std::string> filelist_;
|
|
size_t* file_idx_;
|
|
std::mutex* mutex_for_pick_file_;
|
|
std::mutex* mutex_for_fea_num_ = nullptr;
|
|
uint64_t* total_fea_num_ = nullptr;
|
|
uint64_t fea_num_ = 0;
|
|
|
|
// the alias of used slots, and its order is determined by
|
|
// data_feed_desc(proto object)
|
|
std::vector<std::string> use_slots_;
|
|
std::vector<bool> use_slots_is_dense_;
|
|
|
|
// the alias of all slots, and its order is determined by data_feed_desc(proto
|
|
// object)
|
|
std::vector<std::string> all_slots_;
|
|
std::vector<std::string> all_slots_type_;
|
|
std::vector<std::vector<int>> use_slots_shape_;
|
|
std::vector<int> inductive_shape_index_;
|
|
std::vector<int> total_dims_without_inductive_;
|
|
// For the inductive shape passed within data
|
|
std::vector<std::vector<int>> multi_inductive_shape_index_;
|
|
std::vector<int>
|
|
use_slots_index_; // -1: not used; >=0: the index of use_slots_
|
|
|
|
// The data read by DataFeed will be stored here
|
|
std::vector<phi::DenseTensor*> feed_vec_;
|
|
phi::DenseTensor* rank_offset_;
|
|
|
|
// the batch size defined by user
|
|
int default_batch_size_;
|
|
// current batch size
|
|
int batch_size_;
|
|
|
|
bool finish_init_;
|
|
bool finish_set_filelist_;
|
|
bool finish_start_;
|
|
std::string pipe_command_;
|
|
std::string so_parser_name_;
|
|
std::vector<SlotConf> slot_conf_;
|
|
std::vector<std::string> ins_id_vec_;
|
|
std::vector<std::string> ins_content_vec_;
|
|
phi::Place place_;
|
|
std::string uid_slot_;
|
|
|
|
// The input type of pipe reader, 0 for one sample, 1 for one batch
|
|
int input_type_;
|
|
int gpu_graph_mode_ = 0;
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
GraphDataGenerator gpu_graph_data_generator_;
|
|
#endif
|
|
bool train_mode_;
|
|
};
|
|
|
|
// PrivateQueueDataFeed is the base virtual class for other DataFeeds.
|
|
// It use a read-thread to read file and parse data to a private-queue
|
|
// (thread level), and get data from this queue when trainer call Next().
|
|
template <typename T>
|
|
class PrivateQueueDataFeed : public DataFeed {
|
|
public:
|
|
PrivateQueueDataFeed() {}
|
|
virtual ~PrivateQueueDataFeed() {}
|
|
virtual bool Start();
|
|
virtual int Next();
|
|
|
|
protected:
|
|
// The thread implementation function for reading file and parse.
|
|
virtual void ReadThread();
|
|
// This function is used to set private-queue size, and the most
|
|
// efficient when the queue size is close to the batch size.
|
|
virtual void SetQueueSize(int queue_size);
|
|
// The reading and parsing method called in the ReadThread.
|
|
virtual bool ParseOneInstance(T* instance) = 0;
|
|
virtual bool ParseOneInstanceFromPipe(T* instance) = 0;
|
|
// This function is used to put instance to vec_ins
|
|
virtual void AddInstanceToInsVec(T* vec_ins,
|
|
const T& instance,
|
|
int index) = 0;
|
|
// This function is used to put ins_vec to feed_vec
|
|
virtual void PutToFeedVec(const T& ins_vec) = 0;
|
|
|
|
// The thread for read files
|
|
std::thread read_thread_;
|
|
// using ifstream one line and one line parse is faster
|
|
// than using fread one buffer and one buffer parse.
|
|
// for a 601M real data:
|
|
// ifstream one line and one line parse: 6034 ms
|
|
// fread one buffer and one buffer parse: 7097 ms
|
|
std::ifstream file_;
|
|
std::shared_ptr<FILE> fp_;
|
|
size_t queue_size_;
|
|
string::LineFileReader reader_;
|
|
// The queue for store parsed data
|
|
std::shared_ptr<paddle::framework::ChannelObject<T>> queue_;
|
|
};
|
|
|
|
template <typename T>
|
|
class InMemoryDataFeed : public DataFeed {
|
|
public:
|
|
InMemoryDataFeed();
|
|
virtual ~InMemoryDataFeed() {}
|
|
virtual void Init(const DataFeedDesc& data_feed_desc) = 0;
|
|
virtual bool Start();
|
|
virtual int Next();
|
|
virtual void SetInputPvChannel(void* channel);
|
|
virtual void SetOutputPvChannel(void* channel);
|
|
virtual void SetConsumePvChannel(void* channel);
|
|
|
|
virtual void SetInputChannel(void* channel);
|
|
virtual void SetOutputChannel(void* channel);
|
|
virtual void SetConsumeChannel(void* channel);
|
|
virtual void SetThreadId(int thread_id);
|
|
virtual void SetThreadNum(int thread_num);
|
|
virtual void SetParseInsId(bool parse_ins_id);
|
|
virtual void SetParseUid(bool parse_uid);
|
|
virtual void SetParseContent(bool parse_content);
|
|
virtual void SetParseLogKey(bool parse_logkey);
|
|
virtual void SetEnablePvMerge(bool enable_pv_merge);
|
|
virtual void SetCurrentPhase(int current_phase);
|
|
virtual void LoadIntoMemory();
|
|
virtual void LoadIntoMemoryFromSo();
|
|
virtual void SetRecord(T* records) { records_ = records; }
|
|
int GetDefaultBatchSize() { return default_batch_size_; }
|
|
void AddBatchOffset(const std::pair<int, int>& offset) {
|
|
batch_offsets_.push_back(offset);
|
|
}
|
|
|
|
protected:
|
|
virtual bool ParseOneInstance(T* instance) = 0;
|
|
virtual bool ParseOneInstanceFromPipe(T* instance) = 0;
|
|
virtual void ParseOneInstanceFromSo(const char* str UNUSED,
|
|
T* instance UNUSED,
|
|
CustomParser* parser UNUSED) {}
|
|
virtual int ParseInstanceFromSo(int len UNUSED,
|
|
const char* str UNUSED,
|
|
std::vector<T>* instances UNUSED,
|
|
CustomParser* parser UNUSED) {
|
|
return 0;
|
|
}
|
|
virtual void PutToFeedVec(const std::vector<T>& ins_vec) = 0;
|
|
virtual void PutToFeedVec(const T* ins_vec, int num) = 0;
|
|
|
|
std::vector<std::vector<float>> batch_float_feasigns_;
|
|
std::vector<std::vector<uint64_t>> batch_uint64_feasigns_;
|
|
std::vector<std::vector<size_t>> offset_;
|
|
std::vector<bool> visit_;
|
|
|
|
int thread_id_;
|
|
int thread_num_;
|
|
bool parse_ins_id_;
|
|
bool parse_uid_;
|
|
bool parse_content_;
|
|
bool parse_logkey_;
|
|
bool enable_pv_merge_;
|
|
int current_phase_{-1}; // only for untest
|
|
std::ifstream file_;
|
|
std::shared_ptr<FILE> fp_;
|
|
paddle::framework::ChannelObject<T>* input_channel_;
|
|
paddle::framework::ChannelObject<T>* output_channel_;
|
|
paddle::framework::ChannelObject<T>* consume_channel_;
|
|
|
|
paddle::framework::ChannelObject<PvInstance>* input_pv_channel_;
|
|
paddle::framework::ChannelObject<PvInstance>* output_pv_channel_;
|
|
paddle::framework::ChannelObject<PvInstance>* consume_pv_channel_;
|
|
|
|
std::vector<std::pair<int, int>> batch_offsets_;
|
|
uint64_t offset_index_ = 0;
|
|
bool enable_heterps_ = false;
|
|
T* records_ = nullptr;
|
|
};
|
|
|
|
// This class define the data type of instance(ins_vec) in MultiSlotDataFeed
|
|
class MultiSlotType {
|
|
public:
|
|
MultiSlotType() {}
|
|
~MultiSlotType() {}
|
|
void Init(const std::string& type, size_t reserved_size = 0) {
|
|
CheckType(type);
|
|
if (type_[0] == 'f') {
|
|
float_feasign_.clear();
|
|
if (reserved_size) {
|
|
float_feasign_.reserve(reserved_size);
|
|
}
|
|
} else if (type_[0] == 'u') {
|
|
uint64_feasign_.clear();
|
|
if (reserved_size) {
|
|
uint64_feasign_.reserve(reserved_size);
|
|
}
|
|
}
|
|
type_ = type;
|
|
}
|
|
void InitOffset(size_t max_batch_size = 0) {
|
|
if (max_batch_size > 0) {
|
|
offset_.reserve(max_batch_size + 1);
|
|
}
|
|
offset_.resize(1);
|
|
// DenseTensor' lod is counted from 0, the size of lod
|
|
// is one size larger than the size of data.
|
|
offset_[0] = 0;
|
|
}
|
|
const std::vector<size_t>& GetOffset() const { return offset_; }
|
|
std::vector<size_t>& MutableOffset() { return offset_; }
|
|
void AddValue(const float v) {
|
|
CheckFloat();
|
|
float_feasign_.push_back(v);
|
|
}
|
|
void AddValue(const uint64_t v) {
|
|
CheckUint64();
|
|
uint64_feasign_.push_back(v);
|
|
}
|
|
void CopyValues(const float* input, size_t size) {
|
|
CheckFloat();
|
|
float_feasign_.resize(size);
|
|
memcpy(float_feasign_.data(), input, size * sizeof(float));
|
|
}
|
|
void CopyValues(const uint64_t* input, size_t size) {
|
|
CheckUint64();
|
|
uint64_feasign_.resize(size);
|
|
memcpy(uint64_feasign_.data(), input, size * sizeof(uint64_t));
|
|
}
|
|
void AddIns(const MultiSlotType& ins) {
|
|
if (ins.GetType()[0] == 'f') { // float
|
|
CheckFloat();
|
|
auto& vec = ins.GetFloatData();
|
|
offset_.push_back(offset_.back() + vec.size());
|
|
float_feasign_.insert(float_feasign_.end(), vec.begin(), vec.end());
|
|
} else if (ins.GetType()[0] == 'u') { // uint64
|
|
CheckUint64();
|
|
auto& vec = ins.GetUint64Data();
|
|
offset_.push_back(offset_.back() + vec.size());
|
|
uint64_feasign_.insert(uint64_feasign_.end(), vec.begin(), vec.end());
|
|
}
|
|
}
|
|
void AppendValues(const uint64_t* input, size_t size) {
|
|
CheckUint64();
|
|
offset_.push_back(offset_.back() + size);
|
|
uint64_feasign_.insert(uint64_feasign_.end(), input, input + size);
|
|
}
|
|
void AppendValues(const float* input, size_t size) {
|
|
CheckFloat();
|
|
offset_.push_back(offset_.back() + size);
|
|
|
|
float_feasign_.insert(float_feasign_.end(), input, input + size);
|
|
}
|
|
const std::vector<float>& GetFloatData() const { return float_feasign_; }
|
|
std::vector<float>& MutableFloatData() { return float_feasign_; }
|
|
const std::vector<uint64_t>& GetUint64Data() const { return uint64_feasign_; }
|
|
std::vector<uint64_t>& MutableUint64Data() { return uint64_feasign_; }
|
|
const std::string& GetType() const { return type_; }
|
|
size_t GetBatchSize() { return offset_.size() - 1; }
|
|
std::string& MutableType() { return type_; }
|
|
|
|
std::string DebugString() {
|
|
std::stringstream ss;
|
|
|
|
ss << "\ntype: " << type_ << "\n";
|
|
ss << "offset: ";
|
|
ss << "[";
|
|
for (const size_t& i : offset_) {
|
|
ss << offset_[i] << ",";
|
|
}
|
|
ss << "]\ndata: [";
|
|
if (type_[0] == 'f') {
|
|
for (const float& i : float_feasign_) {
|
|
ss << i << ",";
|
|
}
|
|
} else {
|
|
for (const uint64_t& i : uint64_feasign_) {
|
|
ss << i << ",";
|
|
}
|
|
}
|
|
ss << "]\n";
|
|
return ss.str();
|
|
}
|
|
|
|
private:
|
|
void CheckType(const std::string& type) const {
|
|
PADDLE_ENFORCE_EQ((type == "uint64" || type == "float"),
|
|
true,
|
|
common::errors::InvalidArgument(
|
|
"MultiSlotType error, expect type is uint64 or "
|
|
"float, but received type is %s.",
|
|
type));
|
|
}
|
|
void CheckFloat() const {
|
|
PADDLE_ENFORCE_EQ(
|
|
type_[0],
|
|
'f',
|
|
common::errors::InvalidArgument(
|
|
"MultiSlotType error, add %s value to float slot.", type_));
|
|
}
|
|
void CheckUint64() const {
|
|
PADDLE_ENFORCE_EQ(
|
|
type_[0],
|
|
'u',
|
|
common::errors::InvalidArgument(
|
|
"MultiSlotType error, add %s value to uint64 slot.", type_));
|
|
}
|
|
std::vector<float> float_feasign_;
|
|
std::vector<uint64_t> uint64_feasign_;
|
|
std::string type_;
|
|
std::vector<size_t> offset_;
|
|
};
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator<<(paddle::framework::Archive<AR>& ar,
|
|
const MultiSlotType& ins) {
|
|
ar << ins.GetType();
|
|
#ifdef _LINUX
|
|
ar << ins.GetOffset();
|
|
#else
|
|
const auto& offset = ins.GetOffset();
|
|
ar << (uint64_t)offset.size();
|
|
for (const size_t& x : offset) {
|
|
ar << (const uint64_t)x;
|
|
}
|
|
#endif
|
|
ar << ins.GetFloatData();
|
|
ar << ins.GetUint64Data();
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator>>(paddle::framework::Archive<AR>& ar,
|
|
MultiSlotType& ins) {
|
|
ar >> ins.MutableType();
|
|
#ifdef _LINUX
|
|
ar >> ins.MutableOffset();
|
|
#else
|
|
auto& offset = ins.MutableOffset();
|
|
offset.resize(ar.template Get<uint64_t>());
|
|
for (size_t& x : offset) {
|
|
uint64_t t;
|
|
ar >> t;
|
|
x = static_cast<size_t>(t);
|
|
}
|
|
#endif
|
|
ar >> ins.MutableFloatData();
|
|
ar >> ins.MutableUint64Data();
|
|
return ar;
|
|
}
|
|
|
|
struct RecordCandidate {
|
|
std::string ins_id_;
|
|
std::unordered_multimap<uint16_t, FeatureFeasign> feas_;
|
|
size_t shadow_index_ = -1; // Optimization for Reservoir Sample
|
|
|
|
RecordCandidate() {}
|
|
RecordCandidate(const Record& rec,
|
|
const std::unordered_set<uint16_t>& slot_index_to_replace) {
|
|
for (const auto& fea : rec.uint64_feasigns_) {
|
|
if (slot_index_to_replace.find(fea.slot()) !=
|
|
slot_index_to_replace.end()) {
|
|
feas_.insert({fea.slot(), fea.sign()});
|
|
}
|
|
}
|
|
}
|
|
|
|
RecordCandidate& operator=(const Record& rec) {
|
|
feas_.clear();
|
|
ins_id_ = rec.ins_id_;
|
|
for (auto& fea : rec.uint64_feasigns_) {
|
|
feas_.insert({fea.slot(), fea.sign()});
|
|
}
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class RecordCandidateList {
|
|
public:
|
|
RecordCandidateList() = default;
|
|
RecordCandidateList(const RecordCandidateList& UNUSED) {}
|
|
|
|
size_t Size() { return cur_size_; }
|
|
void ReSize(size_t length);
|
|
|
|
void ReInit();
|
|
void ReInitPass() {
|
|
for (size_t i = 0; i < cur_size_; ++i) {
|
|
if (candidate_list_[i].shadow_index_ != i) {
|
|
candidate_list_[i].ins_id_ =
|
|
candidate_list_[candidate_list_[i].shadow_index_].ins_id_;
|
|
candidate_list_[i].feas_.swap(
|
|
candidate_list_[candidate_list_[i].shadow_index_].feas_);
|
|
candidate_list_[i].shadow_index_ = i;
|
|
}
|
|
}
|
|
candidate_list_.resize(cur_size_);
|
|
}
|
|
|
|
void AddAndGet(const Record& record, RecordCandidate* result);
|
|
void AddAndGet(const Record& record, size_t& index_result) { // NOLINT
|
|
// std::unique_lock<std::mutex> lock(mutex_);
|
|
size_t index = 0;
|
|
++total_size_;
|
|
auto fleet_ptr = FleetWrapper::GetInstance();
|
|
if (!full_) {
|
|
candidate_list_.emplace_back(record, slot_index_to_replace_);
|
|
candidate_list_.back().shadow_index_ = cur_size_;
|
|
++cur_size_;
|
|
full_ = (cur_size_ == capacity_);
|
|
} else {
|
|
index = fleet_ptr->LocalRandomEngine()() % total_size_;
|
|
if (index < capacity_) {
|
|
candidate_list_.emplace_back(record, slot_index_to_replace_);
|
|
candidate_list_[index].shadow_index_ = candidate_list_.size() - 1;
|
|
}
|
|
}
|
|
index = fleet_ptr->LocalRandomEngine()() % cur_size_;
|
|
index_result = candidate_list_[index].shadow_index_;
|
|
}
|
|
const RecordCandidate& Get(size_t index) const {
|
|
PADDLE_ENFORCE_LT(
|
|
index,
|
|
candidate_list_.size(),
|
|
common::errors::OutOfRange("Your index [%lu] exceeds the number of "
|
|
"elements in candidate_list[%lu].",
|
|
index,
|
|
candidate_list_.size()));
|
|
return candidate_list_[index];
|
|
}
|
|
void SetSlotIndexToReplace(
|
|
const std::unordered_set<uint16_t>& slot_index_to_replace) {
|
|
slot_index_to_replace_ = slot_index_to_replace;
|
|
}
|
|
|
|
private:
|
|
size_t capacity_ = 0;
|
|
std::mutex mutex_;
|
|
bool full_ = false;
|
|
size_t cur_size_ = 0;
|
|
size_t total_size_ = 0;
|
|
std::vector<RecordCandidate> candidate_list_;
|
|
std::unordered_set<uint16_t> slot_index_to_replace_;
|
|
};
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator<<(paddle::framework::Archive<AR>& ar,
|
|
const FeatureFeasign& fk) {
|
|
ar << fk.uint64_feasign_;
|
|
ar << fk.float_feasign_;
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator>>(paddle::framework::Archive<AR>& ar,
|
|
FeatureFeasign& fk) {
|
|
ar >> fk.uint64_feasign_;
|
|
ar >> fk.float_feasign_;
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator<<(paddle::framework::Archive<AR>& ar,
|
|
const FeatureItem& fi) {
|
|
ar << fi.sign();
|
|
ar << fi.slot();
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator>>(paddle::framework::Archive<AR>& ar,
|
|
FeatureItem& fi) {
|
|
ar >> fi.sign();
|
|
ar >> fi.slot();
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator<<(paddle::framework::Archive<AR>& ar,
|
|
const Record& r) {
|
|
ar << r.uint64_feasigns_;
|
|
ar << r.float_feasigns_;
|
|
ar << r.ins_id_;
|
|
return ar;
|
|
}
|
|
|
|
template <class AR>
|
|
paddle::framework::Archive<AR>& operator>>(paddle::framework::Archive<AR>& ar,
|
|
Record& r) {
|
|
ar >> r.uint64_feasigns_;
|
|
ar >> r.float_feasigns_;
|
|
ar >> r.ins_id_;
|
|
return ar;
|
|
}
|
|
|
|
// This DataFeed is used to feed multi-slot type data.
|
|
// The format of multi-slot type data:
|
|
// [n feasign_0 feasign_1 ... feasign_n]*
|
|
class MultiSlotDataFeed
|
|
: public PrivateQueueDataFeed<std::vector<MultiSlotType>> {
|
|
public:
|
|
MultiSlotDataFeed() {}
|
|
virtual ~MultiSlotDataFeed() {}
|
|
virtual void Init(const DataFeedDesc& data_feed_desc);
|
|
virtual bool CheckFile(const char* filename);
|
|
|
|
protected:
|
|
virtual void ReadThread();
|
|
virtual void AddInstanceToInsVec(std::vector<MultiSlotType>* vec_ins,
|
|
const std::vector<MultiSlotType>& instance,
|
|
int index);
|
|
virtual bool ParseOneInstance(std::vector<MultiSlotType>* instance);
|
|
virtual bool ParseOneInstanceFromPipe(std::vector<MultiSlotType>* instance);
|
|
virtual void PutToFeedVec(const std::vector<MultiSlotType>& ins_vec);
|
|
};
|
|
|
|
class MultiSlotInMemoryDataFeed : public InMemoryDataFeed<Record> {
|
|
public:
|
|
MultiSlotInMemoryDataFeed() {}
|
|
virtual ~MultiSlotInMemoryDataFeed() {}
|
|
virtual void Init(const DataFeedDesc& data_feed_desc);
|
|
// void SetRecord(Record* records) { records_ = records; }
|
|
|
|
protected:
|
|
virtual bool ParseOneInstance(Record* instance);
|
|
virtual bool ParseOneInstanceFromPipe(Record* instance);
|
|
virtual void ParseOneInstanceFromSo(const char* str UNUSED,
|
|
Record* instance UNUSED,
|
|
CustomParser* parser UNUSED) {}
|
|
virtual int ParseInstanceFromSo(int len,
|
|
const char* str,
|
|
std::vector<Record>* instances,
|
|
CustomParser* parser);
|
|
virtual void PutToFeedVec(const std::vector<Record>& ins_vec);
|
|
virtual void GetMsgFromLogKey(const std::string& log_key,
|
|
uint64_t* search_id,
|
|
uint32_t* cmatch,
|
|
uint32_t* rank);
|
|
virtual void PutToFeedVec(const Record* ins_vec, int num);
|
|
};
|
|
|
|
class SlotRecordInMemoryDataFeed : public InMemoryDataFeed<SlotRecord> {
|
|
public:
|
|
SlotRecordInMemoryDataFeed() = default;
|
|
virtual ~SlotRecordInMemoryDataFeed();
|
|
void Init(const DataFeedDesc& data_feed_desc) override;
|
|
void LoadIntoMemory() override;
|
|
void ExpandSlotRecord(SlotRecord* ins);
|
|
|
|
protected:
|
|
bool Start() override;
|
|
int Next() override;
|
|
bool ParseOneInstance(SlotRecord* instance UNUSED) override { return false; }
|
|
bool ParseOneInstanceFromPipe(SlotRecord* instance UNUSED) override {
|
|
return false;
|
|
}
|
|
// virtual void ParseOneInstanceFromSo(const char* str, T* instance,
|
|
// CustomParser* parser) {}
|
|
void PutToFeedVec(const std::vector<SlotRecord>& ins_vec UNUSED) override {}
|
|
|
|
virtual void LoadIntoMemoryByCommand(void);
|
|
virtual void LoadIntoMemoryByLib(void);
|
|
virtual void LoadIntoMemoryByLine(void);
|
|
virtual void LoadIntoMemoryByFile(void);
|
|
void SetInputChannel(void* channel) override {
|
|
input_channel_ = static_cast<ChannelObject<SlotRecord>*>(channel);
|
|
}
|
|
bool ParseOneInstance(const std::string& line, SlotRecord* rec);
|
|
void PutToFeedVec(const SlotRecord* ins_vec, int num) override;
|
|
void AssignFeedVar(const Scope& scope) override;
|
|
std::vector<std::string> GetInputVarNames() override {
|
|
std::vector<std::string> var_names;
|
|
for (int i = 0; i < use_slot_size_; ++i) {
|
|
var_names.push_back(used_slots_info_[i].slot);
|
|
}
|
|
return var_names;
|
|
}
|
|
#if defined(PADDLE_WITH_CUDA) && defined(PADDLE_WITH_HETERPS)
|
|
void BuildSlotBatchGPU(const int ins_num, MiniBatchGpuPack* pack);
|
|
|
|
virtual MiniBatchGpuPack* get_pack(MiniBatchGpuPack* last_pack);
|
|
|
|
virtual void PackToScope(MiniBatchGpuPack* pack,
|
|
const Scope* scope = nullptr);
|
|
|
|
void FillSlotValueOffset(const int ins_num,
|
|
const int used_slot_num,
|
|
size_t* slot_value_offsets,
|
|
const int* uint64_offsets,
|
|
const int uint64_slot_size,
|
|
const int* float_offsets,
|
|
const int float_slot_size,
|
|
const UsedSlotGpuType* used_slots,
|
|
cudaStream_t stream);
|
|
void CopyForTensor(const int ins_num,
|
|
const int used_slot_num,
|
|
void** dest,
|
|
const size_t* slot_value_offsets,
|
|
const uint64_t* uint64_feas,
|
|
const int* uint64_offsets,
|
|
const int* uint64_ins_lens,
|
|
const int uint64_slot_size,
|
|
const float* float_feas,
|
|
const int* float_offsets,
|
|
const int* float_ins_lens,
|
|
const int float_slot_size,
|
|
const UsedSlotGpuType* used_slots,
|
|
cudaStream_t stream);
|
|
#endif
|
|
|
|
#if defined(PADDLE_WITH_PSCORE) && defined(PADDLE_WITH_HETERPS)
|
|
virtual void InitGraphResource(void);
|
|
virtual void InitGraphTrainResource(void);
|
|
virtual void DoWalkandSage();
|
|
void SetInsIdVec(MiniBatchGpuPack* pack) override {
|
|
if (parse_ins_id_) {
|
|
size_t ins_num = pack->ins_num();
|
|
ins_id_vec_.clear();
|
|
ins_id_vec_.resize(ins_num);
|
|
for (size_t i = 0; i < ins_num; i++) {
|
|
ins_id_vec_[i] = pack->get_lineid(i);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
void DumpWalkPath(std::string dump_path, size_t dump_rate) override;
|
|
void DumpSampleNeighbors(std::string dump_path) override;
|
|
|
|
float sample_rate_ = 1.0f;
|
|
int use_slot_size_ = 0;
|
|
int float_use_slot_size_ = 0;
|
|
int uint64_use_slot_size_ = 0;
|
|
std::vector<AllSlotInfo> all_slots_info_;
|
|
std::vector<UsedSlotInfo> used_slots_info_;
|
|
size_t float_total_dims_size_ = 0;
|
|
std::vector<int> float_total_dims_without_inductives_;
|
|
|
|
#if defined(PADDLE_WITH_CUDA) && defined(PADDLE_WITH_HETERPS)
|
|
int pack_thread_num_{5};
|
|
std::vector<std::thread> pack_threads_;
|
|
std::vector<MiniBatchGpuPack*> pack_vec_;
|
|
BlockingQueue<MiniBatchGpuPack*> free_pack_queue_;
|
|
BlockingQueue<MiniBatchGpuPack*> using_pack_queue_;
|
|
std::atomic<bool> pack_is_end_{false};
|
|
std::atomic<uint64_t> pack_offset_index_{0};
|
|
MiniBatchGpuPack* last_pack_{nullptr};
|
|
std::atomic<bool> stop_token_{false};
|
|
std::atomic<int> thread_count_{0};
|
|
std::mutex pack_mutex_;
|
|
|
|
// async infershape
|
|
std::map<const Scope*, std::vector<phi::DenseTensor*>> scope_feed_vec_;
|
|
#endif
|
|
};
|
|
|
|
class PaddleBoxDataFeed : public MultiSlotInMemoryDataFeed {
|
|
public:
|
|
PaddleBoxDataFeed() {}
|
|
virtual ~PaddleBoxDataFeed() {}
|
|
|
|
protected:
|
|
virtual void Init(const DataFeedDesc& data_feed_desc);
|
|
virtual bool Start();
|
|
virtual int Next();
|
|
virtual void AssignFeedVar(const Scope& scope);
|
|
virtual void PutToFeedVec(const std::vector<PvInstance>& pv_vec);
|
|
virtual void PutToFeedVec(const std::vector<Record*>& ins_vec);
|
|
virtual int GetCurrentPhase();
|
|
virtual void GetRankOffset(const std::vector<PvInstance>& pv_vec,
|
|
int ins_number);
|
|
std::string rank_offset_name_;
|
|
int pv_batch_size_;
|
|
};
|
|
|
|
#if (defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)) && !defined(_WIN32)
|
|
template <typename T>
|
|
class PrivateInstantDataFeed : public DataFeed {
|
|
public:
|
|
PrivateInstantDataFeed() {}
|
|
virtual ~PrivateInstantDataFeed() {}
|
|
void Init(const DataFeedDesc& data_feed_desc) override;
|
|
bool Start() override { return true; }
|
|
int Next() override;
|
|
|
|
protected:
|
|
// The batched data buffer
|
|
std::vector<MultiSlotType> ins_vec_;
|
|
|
|
// This function is used to preprocess with a given filename, e.g. open it or
|
|
// mmap
|
|
virtual bool Preprocess(const std::string& filename) = 0;
|
|
|
|
// This function is used to postprocess system resource such as closing file
|
|
// NOTICE: Ensure that it is safe to call before Preprocess
|
|
virtual bool Postprocess() = 0;
|
|
|
|
// The reading and parsing method.
|
|
virtual bool ParseOneMiniBatch() = 0;
|
|
|
|
// This function is used to put ins_vec to feed_vec
|
|
virtual void PutToFeedVec();
|
|
};
|
|
|
|
class MultiSlotFileInstantDataFeed
|
|
: public PrivateInstantDataFeed<std::vector<MultiSlotType>> {
|
|
public:
|
|
MultiSlotFileInstantDataFeed() {}
|
|
virtual ~MultiSlotFileInstantDataFeed() {}
|
|
|
|
protected:
|
|
int fd_{-1};
|
|
char* buffer_{nullptr};
|
|
size_t end_{0};
|
|
size_t offset_{0};
|
|
|
|
bool Preprocess(const std::string& filename) override;
|
|
|
|
bool Postprocess() override;
|
|
|
|
bool ParseOneMiniBatch() override;
|
|
};
|
|
#endif
|
|
|
|
} // namespace framework
|
|
} // namespace paddle
|