ps-lite源码学习笔记

来源:互联网 发布:opencv java 高斯模糊 编辑:程序博客网 时间:2024/06/04 19:05

ps-lite代码解析


首先查看ps-lit的源码结构:


base

filter

kv

parameter

proto

ps

system

ps.h

ps_main.cc


其中,ps_main.cc是其程序入口,ps.h是其接口,其他文件夹则包含各个功能模块。下面详细阐释各部分。


ps_main.cc代码很短,如下所示:


#include "ps.h"


namespace ps {


App* App::Create(int argc,char *argv[]) {


  App* app = new App();


  if (ps::IsServerNode()) {


    CreateServerNode(argc, argv);


  }


  return app;


}


// namespace ps


int main(int argc,char *argv[]) {


  ps::StartSystem(&argc, &argv);


  int ret =ps::IsWorkerNode() ? WorkerNodeMain(argc, argv) :0;


  ps::StopSystem();


  return ret;


}

首先,只看main函数,main函数的流程很明晰,可以展示为下图: 


可以发现,main函数主要用于创建WorkerNode,在main函数上面的代码中,我们注意到有一个app类,关于app类的定义,可见ps/app.h,这个类,在这里,这个Create用于创建SeverNode。

那么,main函数的功能不难理解,它就是用来创建WorkerNode和SeverNode的。


ps.h:接下来,我们展开接口函数ps.h,理解了它,结合main函数中的流程,我们就不难理解整个ps-lite的架构,首先观察其源码,笔者将整个接口分为4部分,分别是:


Worker node APIs

Server node APIs

Scheduler Node APIs(目前为空)

More Advanced APIs


由于其中Scheduler Node APIs 为空,所以我们分三部分来研究。


第一部分——Worker node APIs :


//Syncopts是push和pull操作的选项集

struct Syncopts{

  std::vector<int> deps;

//deps是push操作的时间戳,只有当该操作被参数服务器处理时,才会修改其时间戳。


  std::function<void()> callback;

//当收到参数服务器返回的响应时执行callback。


  std::vector<Filter> filters;

//key-value过滤器用于减少通信开销


  Filter* AddFilter(Filter::Type type);

//用于添加过滤器,使用举例:AddFilter(Filter::COMPRESSING);


  int cmd = -1;

//-1表示无命令。

};


//KVWorker是用来收发参数服务器key-value对的缓存器。

template<typename Val>

class KVWorker{ 

  explicit KVWorker(int id =0) {

    cache_ = CHECK_NOTNULL((new KVCache<Key, Val>(id)));

  }

//id是用来在参数服务器中寻找KVStore的唯一标识,负数ID的KVWorker为系统专用。//注:声明为explicit表示其不可被隐式转换。

  ~KVWorker() {delete cache_; }


// Basic Push and Pull

int Push(const std::vector<Key>& keys,


           const std::vector<Val>& vals,


           const SyncOpts& opts = SyncOpts()) {


    // copy the data, then use the zero copy push


    return ZPush(std::make_shared<std::vector<Key>>(keys),


                 std::make_shared<std::vector<Val>>(vals), opts);


  }

//push操作将一串key-value对传到参数服务器。

//它是一个非阻塞调用,当要发送的信息进入系统的发送队列立刻返回。真正的push操作只在wait返回时或者提供的callback被调用时才结束。


int Pull(const std::vector<Key>& keys,


           std::vector<Val>* vals,


           const SyncOpts& opts = SyncOpts()) {


    // copy the data, then use the zero copy pull


    return ZPull(std::make_shared<std::vector<Key>>(keys), vals, opts);


  }

//pull操作根据key将value从参数服务器取出。

//它是一个非阻塞调用,当要发送的信息进入系统的发送队列立刻返回。真正的pull操作只在wait返回时或者提供的callback被调用时才结束。


void Wait(int timestamp) {

    cache_->Wait(timestamp);

  }

//Wait操作用来阻塞下一个请求,直到当前请求完成。


//  Zero-copy Push and Pull

//默认情况下,Push和pull操作都会先拷贝数据,然后用户程序可以重写或者删除数据。然而,有些情况下memcpy十分耗费时间,所以我们用ZPush和ZPull以减少时间,但需要注意的是,用户需要自己保持key和value的值不变直到请求完成。

int ZPush(const std::shared_ptr<std::vector<Key> >& keys,

           const std::shared_ptr<std::vector<Val> >& vals,

            const SyncOpts& opts = SyncOpts()) {

return cache_->Push(GetTask(opts), SArray<Key>(keys),SArray<Val>(vals), SArray<int>(),opts.callback);

  }

  int ZPull(const std::shared_ptr<std::vector<Key> >& keys,

            std::vector<Val>* vals,

            const SyncOpts& opts = SyncOpts()) {

    return cache_->Pull(GetTask(opts), SArray<Key>(keys),

                        CHECK_NOTNULL(vals), NULL, opts.callback);

  }

 

private:

  Task GetTask(const SyncOpts& opts);

  KVCache<Key, Val>* cache_;


};


int WorkerNodeMain(int argc,char *argv[]);

//WorkerNodeMain是一个WorkerNode的Main函数。


第二部分——Server node APIs :


//IOnlineHandle是一个用于处理请求的句柄例子。

template <typename Val, typename SyncVal>

class IOnlineHandle {

public:

  IOnlineHandle() { }

  virtual ~IOnlineHandle() { }


  inline void Start(bool push, int timestamp,int cmd, void* msg) { }

//开始处理一个Worker的请求。


  inline void Finish() { }

//请求已被处理。


  inline void Init(Key key, Val& val) { }

//Handle初始化。


  inline void Push(Key recv_key, Blob<const SyncVal> recv_val, Val& my_val) {…}

//处理来自worker节点的push请求


  inline void Pull(Key recv_key, const Val& my_val, Blob<SyncVal>& send_val){…}

//处理来自worker节点的pull请求


  inline void SetCaller(void *obj) { }

//接收caller

};



//Sever节点用OnlineServer存储key-value对,key-value对从worker发过来,被Handle依次接收,然后存储进KVStore中。

template <typename Val, typename SyncVal = Val,

          typename Handle = IOnlineHandle<Val, SyncVal> >

class OnlineServer {

public:

  OnlineServer(const Handle& handle = Handle(), int pull_val_len = 1, int id = 0) {};

  ~OnlineServer() { }

  KVStore* server() { return server_; }

 private:

  KVStore* server_ = NULL;

};


//Sever节点的main函数。

int CreateServerNode(int argc,char *argv[]);


第三部分——More Advanced APIs :


inline App* MyApp() 

{ return Postoffice::instance().manager().app(); }

//返回当前节点运行的App


inline Node MyNode() 

{ return Postoffice::instance().manager().van().my_node(); }

//返回当前节点的全局ID


inline std::string MyNodeID() {return MyNode().id(); }

//返回我的所有节点的ID


inline intIsWorkerNode() 

{ return MyNode().role() == Node::WORKER; }

//判断当前节点是否为Worker节点


inline intIsServerNode() 

{ return MyNode().role() == Node::SERVER; }

//判断当前节点是否为Sever节点


inline intIsSchedulerNode() 

{ return MyNode().role() == Node::SCHEDULER; }

//判断当前节点是否为scheduler节点


inline std::string SchedulerID() {

  return Postoffice::instance().manager().van().scheduler().id();}

//返回SchedulerID


inline int MyRank() { return MyNode().rank(); }

//返回节点所在组的rankID,


inline int RankSize() {

  auto& mng =Postoffice::instance().manager();

  return IsWorkerNode() ? mng.num_workers() : (IsServerNode() ? mng.num_servers() :1);}

//返回当前组的节点数。


inline int NumWorkers() { return FLAGS_num_workers; }

inline int NumServers() { return FLAGS_num_servers; }


inline void StartSystem(int* argc, char ***argv) {

  ps::Postoffice::instance().Run(argc, argv);

}


inline voidStopSystem() {

  ps::Postoffice::instance().Stop();

}


inline int RunSystem(int* argc, char ***argv) {

  StartSystem(argc, argv); StopSystem();

  return 0;

}


0 0
原创粉丝点击