[转]并行计算简介和多核CPU编程Demo

sapzj1984 2007-06-15 10:51:42
2006年是双核的普及年,双核处理器出货量开始超过单核处理器出货量;2006年的11月份Intel开始供货4核;AMD今年也将发布4核,并计划今年下半年发布8核;
按照Intel一个文档所说:"假定22纳米处理时帧上有一枚13毫米大小的处理器,其上有40亿个晶体管、48MB高速缓存,功耗为100W。利用如此数量的晶体管,我们可设计拥有12个较大内核、48个(多核)中型内核、或144个小型内核(许多个内核)的处理器。"
而且Intel已经开发完成了一款80核心处理器原型,速度达到每秒一万亿次浮点运算。

随着个人多核CPU的普及,充分利用多核CPU的性能优势摆在了众多开发人员的面前;
以前的CPU升级,很多时候软件性能都能够自动地获得相应提升,而面对多核CPU,免费的午餐没有了,开发人员必须手工的完成软件的并行化,以从爆炸性增长的CPU性能中获益;
(ps:我想,以后的CPU很可能会集成一些专门用途的核(很可能设计成比较通用的模式),比如GPU的核、图象处理的核、向量运算的核、加解密编解码的核、FFT计算的核、物理计算的核、神经网络计算的核等等:D )

先来看一下单个CPU上的并行计算:
单CPU上常见的并行计算:多级流水线(提高CPU频率的利器)、超标量执行(多条流水线并同时发送多条指令)、乱序执行(指令重排)、单指令流多数据流SIMD、超长指令字处理器(依赖于编译器分析)等

并行计算简介
并行平台的通信模型: 共享数据(POSIX、windows线程、OpenMP)、消息交换(MPI、PVM)
并行算法模型: 数据并行模型、任务依赖图模型、工作池模型、管理者-工作者模型、消费者模型
对于并行计算一个任务可能涉及到的问题: 任务分解、任务依赖关系、任务粒度分配、并发度、任务交互
并行算法性能的常见度量值: 并行开销、加速比、效率(加速比/CPU数)、成本(并行运行时间*CPU数)

一个简单的多核计算Demo
演示中主要完成的工作是:(工作本身没有什么意义 主要是消耗一些时间来代表需要做的工作)
代码:
double Sum0(double* data,long data_count)
{
double result=0;
for (long i=0;i<data_count;++i)
{
data[i]=sqrt(1-(data[i]*data[i]));
result+=data[i];
}
return result;
}

然后用OpenMP工具(vc和icc编译器支持)(函数SumOpenMP)和一个自己手工写的线程工具来并行化该函数(函数SumWTP),并求出加速比;
(在多核CPU上执行Demo才可以看到多CPU并行的优势)
OpenMP是基于编译器命令的并行编程标准,使用的共享数据模型,现在可以用在C/C++、Fortan中;OpenMP命令提供了对并发、同步、数据读写的支持;

//我测试用的编译器vc2005
//需要在项目属性中打开多线程和OpenMP支持
//TestWTP.cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
#include <math.h>

#define _IS_TEST_OpenMP
//要测试OpenMP需要编译器支持OpenMP,并在编译设置里面启用OpenMP
#ifdef _IS_TEST_OpenMP
#include <omp.h>
#endif

//使用CWorkThreadPool在多个CPU上完成计算的简单Demo
#include "WorkThreadPool.h"


double Sum0(double* data,long data_count); //单线程执行
double SumWTP(double* data,long data_count); //根据CPU数动态多线程并行执行
#ifdef _IS_TEST_OpenMP
double SumOpenMP(double* data,long data_count); //使用OpenMP来并行执行
#endif


const long g_data_count=200000;
double g_data[g_data_count];

int main()
{
long i;
double start0, start1, start2;
const long test_count=200*2;
double sumresult;

//inti
for (i=0;i<g_data_count;++i)
g_data[i]=rand()*(1.0/RAND_MAX);

//
start0=(double)clock();
sumresult=0;
for( i=0; i<test_count; i++ )
{
sumresult+=Sum0(g_data,g_data_count);
}
start0=((double)clock()-start0)/CLOCKS_PER_SEC;
printf ("<Single thread> ");
printf (" result = %10.7f ",sumresult);
printf (" Seconds = %10.7f ",start0 );


#ifdef _IS_TEST_OpenMP
start1=clock();
sumresult=0;
for( i=0; i<test_count; i++ )
{
sumresult+=SumOpenMP(g_data,g_data_count);
}
start1=((double)clock()-start1)/CLOCKS_PER_SEC;
printf (" <OpenMP> ");
printf (" result = %10.7f ",sumresult);
printf (" Seconds = %10.7f ",start1);

printf (" ");
printf ("%10.7f/%10.7f = %2.4f ",start0,start1,start0/start1);
#endif

//
start2=clock();
sumresult=0;
for( i=0; i<test_count; i++ )
{
sumresult+=SumWTP(g_data,g_data_count);
}
start2=((double)clock()-start2)/CLOCKS_PER_SEC;
printf (" <CWorkThreadPool with %d thread> ",CWorkThreadPool::best_work_count());
printf (" result = %10.7f ",sumresult);
printf (" Seconds = %10.7f ",start2);

printf (" ");
printf ("%10.7f/%10.7f = %2.4f ",start0,start2,start0/start2);


printf (" --------- ok ! ---------");
getchar();

return 0;
}


double Sum0(double* data,long data_count)
{
double result=0;
for (long i=0;i<data_count;++i)
{
data[i]=sqrt(1-(data[i]*data[i]));
result+=data[i];
}
return result;
}

#ifdef _IS_TEST_OpenMP
double SumOpenMP(double* data,long data_count)
{
double result=0;
#pragma omp parallel for schedule(static) reduction(+: result)
for (long i=0;i<data_count;++i)
{
data[i]=sqrt(1-(data[i]*data[i]));
result+=data[i];
}
return result;
}
#endif

struct TWorkData
{
long ibegin;
long iend;
double* data;
double result;
};

void sum_callback(TWorkData* wd)
{
wd->result=Sum0( &wd->data[wd->ibegin],(wd->iend-wd->ibegin) );
}

double SumWTP(double* data,long data_count)
{
static long work_count=CWorkThreadPool::best_work_count();
static std::vector<TWorkData> work_list(work_count);
static std::vector<TWorkData*> pwork_list(work_count);
long i;

static bool IS_inti=false;
if (!IS_inti)//分配任务
{
for (i=0;i<work_count;++i)
{
work_list[i].data=data;
if (0==i) work_list[i].ibegin=0;
else work_list[i].ibegin=work_list[i-1].iend;
work_list[i].iend=data_count*(i+1)/work_count;
}
for (i=0;i<work_count;++i)
pwork_list[i]=&work_list[i];
IS_inti=true;
}

//执行任务
CWorkThreadPool::work_execute((TThreadCallBack)sum_callback,(void**)&pwork_list[0],pwork_list.size());

double result=0;
for (i=0;i<work_count;++i)
result+=work_list[i].result;

return result;
}
...全文
710 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
大侠肖 2011-09-20
  • 打赏
  • 举报
回复
现在再来看你这个帖子,里面有些观点非常赞同的!
sapzj1984 2007-06-15
  • 打赏
  • 举报
回复
//CWorkThreadPool的实现文件 WorkThreadPool.cpp
/////////////////////////////////////////////////////////////
//工作线程池 TWorkThreadPool

#include <process.h>
#include <vector>
#include "windows.h"
#include "WorkThreadPool.h"

//#define _IS_SetThreadAffinity_
//定义该标志则执行不同的线程绑定到不同的CPU,减少线程切换开销; 不鼓励


class TWorkThreadPool;

//线程状态
enum TThreadState{ thrStartup=0, thrReady, thrBusy, thrTerminate, thrDeath };

class TWorkThread
{
public:
volatile HANDLE thread_handle;
volatile enum TThreadState state;
volatile TThreadCallBack func;
volatile void * pdata; //work data
volatile HANDLE waitfor_event;
TWorkThreadPool* pool;
volatile DWORD thread_ThreadAffinityMask;

TWorkThread() { memset(this,0,sizeof(TWorkThread)); }
};

void do_work_end(TWorkThread* thread_data);


void __cdecl thread_dowork(TWorkThread* thread_data) //void __stdcall thread_dowork(TWorkThread* thread_data)
{
volatile TThreadState& state=thread_data->state;
#ifdef _IS_SetThreadAffinity_
SetThreadAffinityMask(GetCurrentThread(),thread_data->thread_ThreadAffinityMask);
#endif
state = thrStartup;

while(true)
{
WaitForSingleObject(thread_data->waitfor_event, -1);
if(state == thrTerminate)
break;

state = thrBusy;
volatile TThreadCallBack& func=thread_data->func;
if (func!=0)
func((void *)thread_data->pdata);
do_work_end(thread_data);
}
state = thrDeath;
_endthread();
//ExitThread(0);
}

class TWorkThreadPool
{
private:
volatile HANDLE thread_event;
volatile HANDLE new_thread_event;
std::vector<TWorkThread> work_threads;
mutable long cpu_count;
inline long get_cpu_count() const {
if (cpu_count>0) return cpu_count;

SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
cpu_count=SystemInfo.dwNumberOfProcessors;
return cpu_count;
}
inline long passel_count() const { return (long)work_threads.size()+1; }
void inti_threads() {
long best_count =get_cpu_count();

long newthrcount=best_count - 1;
work_threads.resize(newthrcount);
thread_event = CreateSemaphore(0, 0,newthrcount , 0);
new_thread_event = CreateSemaphore(0, 0,newthrcount , 0);
long i;
for( i= 0; i < newthrcount; ++i)
{
work_threads[i].waitfor_event=thread_event;
work_threads[i].state = thrTerminate;
work_threads[i].pool=this;
work_threads[i].thread_ThreadAffinityMask=1<<(i+1);
work_threads[i].thread_handle =(HANDLE)_beginthread((void (__cdecl *)(void *))thread_dowork, 0, (void*)&work_threads[i]);
//CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_dowork,(void*) &work_threads[i], 0, &thr_id);
//todo: _beginthread 的错误处理
}
#ifdef _IS_SetThreadAffinity_
SetThreadAffinityMask(GetCurrentThread(),0x01);
#endif
for(i = 0; i < newthrcount; ++i)
{
while(true) {
if (work_threads[i].state == thrStartup) break;
else Sleep(0);
}
work_threads[i].state = thrReady;
}
}
void free_threads(void)
{
long thr_count=(long)work_threads.size();
long i;
for(i = 0; i <thr_count; ++i)
{
while(true) {
if (work_threads[i].state == thrReady) break;
else Sleep(0);
}
work_threads[i].state=thrTerminate;
}
if (thr_count>0)
ReleaseSemaphore(thread_event,thr_count, 0);
for(i = 0; i <thr_count; ++i)
{
while(true) {
if (work_threads[i].state == thrDeath) break;
else Sleep(0);
}
}
CloseHandle(thread_event);
CloseHandle(new_thread_event);
work_threads.clear();
}
void passel_work(const TThreadCallBack* work_proc,int work_proc_inc,void** word_data_list,int work_count) {
if (work_count==1)
{
(*work_proc)(word_data_list[0]);
}
else
{
const TThreadCallBack* pthwork_proc=work_proc;
pthwork_proc+=work_proc_inc;

long i;
long thr_count=(long)work_threads.size();
for(i = 0; i < work_count-1; ++i)
{
work_threads[i].func = *pthwork_proc;
work_threads[i].pdata =word_data_list[i+1];
work_threads[i].state = thrBusy;
pthwork_proc+=work_proc_inc;
}
for(i = work_count-1; i < thr_count; ++i)
{
work_threads[i].func = 0;
work_threads[i].pdata =0;
work_threads[i].state = thrBusy;
}
if (thr_count>0)
ReleaseSemaphore(thread_event,thr_count, 0);

//current thread do a work
(*work_proc)(word_data_list[0]);


//wait for work finish
for(i = 0; i <thr_count; ++i)
{
while(true) {
if (work_threads[i].state == thrReady) break;
else Sleep(0);
}
}
std::swap(thread_event,new_thread_event);
}
}
void private_work_execute(TThreadCallBack* pwork_proc,int work_proc_inc,void** word_data_list,int work_count) {
while (work_count>0)
{
long passel_work_count;
if (work_count>=passel_count())
passel_work_count=passel_count();
else
passel_work_count=work_count;

passel_work(pwork_proc,work_proc_inc,word_data_list,passel_work_count);

pwork_proc+=(work_proc_inc*passel_work_count);
word_data_list=&word_data_list[passel_work_count];
work_count-=passel_work_count;
}
}
public:
explicit TWorkThreadPool():thread_event(0),work_threads(),cpu_count(0) { inti_threads(); }
~TWorkThreadPool() { free_threads(); }
inline long best_work_count() const { return passel_count(); }
inline void DoWorkEnd(TWorkThread* thread_data){
thread_data->waitfor_event=new_thread_event;
thread_data->func=0;
thread_data->state = thrReady;
}

inline void work_execute(TThreadCallBack* pwork_proc,void** word_data_list,int work_count) {
private_work_execute(pwork_proc,1,word_data_list,work_count);
}
inline void work_execute(TThreadCallBack work_proc,void** word_data_list,int work_count) {
private_work_execute(&work_proc,0,word_data_list,work_count);
}
};
void do_work_end(TWorkThread* thread_data)
{
thread_data->pool->DoWorkEnd(thread_data);
}

//TWorkThreadPool end;
////////////////////////////////////////

TWorkThreadPool g_work_thread_pool;//工作线程池

long CWorkThreadPool::best_work_count() { return g_work_thread_pool.best_work_count(); }

void CWorkThreadPool::work_execute(const TThreadCallBack work_proc,void** word_data_list,int work_count)
{
g_work_thread_pool.work_execute(work_proc,word_data_list,work_count);
}

void CWorkThreadPool::work_execute(const TThreadCallBack* work_proc_list,void** word_data_list,int work_count)
{
g_work_thread_pool.work_execute((TThreadCallBack*)work_proc_list,word_data_list,work_count);
}
sapzj1984 2007-06-15
  • 打赏
  • 举报
回复
//CWorkThreadPool的声明文件 WorkThreadPool.h
/////////////////////////////////////////////////////////////
//工作线程池 CWorkThreadPool
//用于把一个任务拆分成多个线程任务,从而可以使用多个CPU
//HouSisong@263.net
////////////////////////////
//todo:改成任务领取模式
//todo:修改辅助线程优先级,继承自主线程
//要求:1.任务分割时分割的任务量比较接近
// 2.任务也不要太小,否则线程的开销可能会大于并行的收益
// 3.任务数最好是CPU数的倍数
// 4.主线程不能以过高优先级运行,否则其他辅助线程可能得不到时间片

#ifndef _WorkThreadPool_H_
#define _WorkThreadPool_H_

typedef void (*TThreadCallBack)(void * pData);

class CWorkThreadPool
{
public:
static long best_work_count(); //返回最佳工作分割数,现在的实现为返回CPU个数
static void work_execute(const TThreadCallBack work_proc,void** word_data_list,int work_count); //并行执行工作,并等待所有工作完成
static void work_execute(const TThreadCallBack* work_proc_list,void** word_data_list,int work_count); //同上,但不同的work调用不同的函数
static void work_execute_single_thread(const TThreadCallBack work_proc,void** word_data_list,int work_count) //单线程执行工作,并等待所有工作完成;用于调试等
{
for (long i=0;i<work_count;++i)
work_proc(word_data_list[i]);
}
static void work_execute_single_thread(const TThreadCallBack* work_proc_list,void** word_data_list,int work_count) //单线程执行工作,并等待所有工作完成;用于调试等
{
for (long i=0;i<work_count;++i)
work_proc_list[i](word_data_list[i]);
}
};


#endif //_WorkThreadPool_H_
zhsj64 2007-06-15
  • 打赏
  • 举报
回复
收藏了
  Fourinone(中文名字“四不像”)是一个四合一分布式计算框架,在写这个框架之前,我对分布式计算进行了长时间的思考,也看了老外写的其他开源框架,当我们把复杂的hadoop当作一门学科学习时,似乎忘记了我们想解决问题的初衷:我们仅仅是想写个程序把几台甚至更多的机器一起用起来计算,把更多的cpu和内存利用上,来解决我们数量大和计算复杂的问题,当然这个过程中要考虑到分布式的协同和故障处理。如果仅仅是为了实现这个简单的初衷,为什么一切会那么复杂,我觉的自己可以写一个更简单的东西,它不需要过度设计,只需要看上去更酷一点,更小巧一点,功能更强一点。于是我将自己对分布式的理解融入到这个框架中,考虑到底层实现技术的相似性,我将Hadoop,Zookeeper,MQ,分布式缓存四大主要的分布式计算功能合为一个框架内,对复杂的分布式计算应用进行了大量简化和归纳。   首先,对分布式协同方面,它实现了Zookeeper所有的功能,并且做了很多改进,包括简化Zookeeper的树型结构,用domain/node两层结构取代,简化Watch回调多线程等待编程模型,用更直观的容易保证业务逻辑完整性的内容变化事件以及状态轮循取代,Zookeeper只能存储信息不大于1M的内容,Fourinone超过1M的内容会以内存隐射文件存储,增强了它的存储功能,简化了Zookeeper的ACL权限功能,用更为程序员熟悉rw风格取代,简化了Zookeeper的临时节点和序列节点等类型,取代为在创建节点时是否指定保持心跳,心跳断掉时节点会自动删除。Fourinone是高可用的,没有单点问题,可以有任意多个复本,它的复制不是定时而是基于内容变更复制,有更高的性能,Fourinone实现了领导者选举算法(但不是Paxos),在领导者服务器宕机情况下,会自动不延时的将请求切换到备份服务器上,选举出新的领导者进行服务,这个过程中,心跳节点仍然能保持健壮的稳定性,迅速跟新的领导者保持心跳连接。基于Fourinone可以轻松实现分布式配置信息,集群管理,故障节点检测,分布式锁,以及淘宝configserver等等协同功能。   其次, Fourinone可以提供完整的分布式缓存功能。如果对一个中小型的互联网或者企业应用,仅仅利用domain/node进行k/v的存储即可,因为domain/node都是内存操作而且读写锁分离,同时拥有复制备份,完全满足缓存的高性能与可靠性。对于大型互联网应用,高峰访问量上百万的并发读写吞吐量,会超出单台服务器的承受力,Fourinone提供了fa?ade的解决方案去解决大集群的分布式缓存,利用硬件负载均衡路由到一组fa?ade服务器上,fa?ade可以自动为缓存内容生成key,并根据key准确找到散落在背后的缓存集群的具体哪台服务器,当缓存服务器的容量到达限制时,可以自由扩容,不需要成倍扩容,因为fa?ade的算法会登记服务器扩容时间版本,并将key智能的跟这个时间匹配,这样在扩容后还能准确找到之前分配到的服务器。另外,基于Fourinone可以轻松实现web应用的session功能,只需要将生成的key写入客户端cookie即可。   Fourinone对于分布式大数据量并行计算的解决方案不同于复杂的hadoop,它不像hadoop的中间计算结果依赖于hdfs,它使用不同于map/reduce的全新设计模式解决问题。Fourinone有“包工头”,“农民工”,“手工仓库”的几个核心概念。“农民工”为一个计算节点,可以部署在多个机器,它由开发者自由实现,计算时,“农民工”到“手工仓库”获取输入资源,再将计算结果放回“手工仓库”返回给“包工头”。“包工头”负责承包一个复杂项目的一部分,可以理解为一个分配任务和调度程序,它由开发者自己实现,开发者可以自由控制调度过程,比如按照“农民工”的数量将源数据切分成多少份,然后远程分配给“农民工”节点进行计算处理,它处理完的中间结果数据不限制保存在hdfs里,而可以自由控制保存在分布式缓存、数据库、分布式文件里。如果需要结果数据的合并,可以新建立一个“包工头”的任务分配进行完成。多个“包工头”之间进行责任链式处理。总的来说,是将大数据的复杂分布式计算,设计为一个链式的多“包工头”环节去处理,每个环节包括利用多台“农民工”机器进行并行计算,无论是拆分计算任务还是合并结果,都可以设计为一个单独的“包工头”环节。这样做的好处是,开发者有更大能力去深入控制并行计算的过程,去保持使用并行计算实现业务逻辑的完整性,而且对各种不同类型的并行计算场景也能灵活处理,不会因为某些特殊场景被map/reduce的框架限制住思维,并且链式的每个环节也方便进行监控过程。   Fourinone也可以当成简单的mq来使用,将domain视为mq队列,每个node为一个队列消息,监控domain的变化事件来获取队列消息。也可以将domain视为订阅主题,将每个订阅者注册到domain的node上,发布者将消息逐一更新每个node,订阅者监控每个属于自己的node的变化事件获取订阅消息,收到后删除内容等待下一个消息。但是Fourinone不实现JMS的规范,不提供JMS的消息确认和消息过滤等特殊功能,不过开发者可以基于Fourinone自己去扩充这些功能,包括mq集群,利用一个独立的domain/node建立队列或者主题的key隐射,再仿照上面分布式缓存的智能根据key定位服务器的做法实现集群管理。   Fourinone整体代码短小精悍,跟Hadoop, Zookeeper, Memcache, ActiveMq等开源产品代码上没有任何相似性,不需要任何依赖,引用一个jar包就可以嵌入式使用,良好支持window环境,可以在一台机器上模拟分布式环境,更方便开发。   开发包里自带了一系列傻瓜上手demo,包括分布式计算、统一配置管理、集群管理、分布式锁、分布式缓存、MQ等方面, 每个demo均控制在少许行代码内,但是涵盖了Fourinone主要的功能,方便大家快速理解并掌握。   Fourinone 2.0新增功能:   Fourinone2.0提供了一个4合1分布式框架和简单易用的编程api,实现对多台计算机cpu,内存,硬盘的统一利用,从而获取到强大计算能力去解决复杂问题。Fourinone框架提供了一系列并行计算模式(农民工/包工头/职介绍/手工仓库)用于利用多机多核cpu的计算能力;提供完整的分布式缓存和小型缓存用于利用多机内存能力;提供像操作本地文件一样操作远程文件(访问,并行读写,拆分,排它,复制,解析,事务等)用于利用多机硬盘存储能力;由于多计算机物理上独立,Fourinone框架也提供完整的分布式协同和锁以及简化MQ功能,用于实现多机的协作和通讯。   一、提供了对分布式文件的便利操作, 将集群中所有机器的硬盘资源利用起来,通过统一的fttp文件路径访问,如:   windows:fttp://v020138.sqa.cm4/d:/data/a.log   linux:fttp://v020138.sqa.cm4/home/user/a.log   比如以这样的方式读取远程文件:   FttpAdapter fa = FttpAdapter("fttp://v020138.sqa.cm4/home/log/a.log");   fa.getFttpReader().readAll();   提供对集群文件的操作支持,包括:   1、元数据访问,添加删除,按块拆分, 高性能并行读写,排他读写(按文件部分内容锁定),随机读写,集群复制等   2、对集群文件的解析支持(包括按行,按分割符,按最后标识读取)   3、对整形数据的高性能读写支持(ArrayInt比ArrayList存的更多更快)   4、两阶段提交和事务补偿处理   5、自带一个集群文件浏览器,可以查看集群所有硬盘上的文件(不同于hadoop的namenode,没有单点问题和容量限制)   总的来说, 将集群看做一个操作系统,像操作本地文件一样操作远程文件。   但是fourinone并不提供一个分布式存储系统,比如文件数据的导入导出、拆分存储、负载均衡,备份容灾等存储功能,不过开发人员可以利用这些api去设计和实现这些功能,用来满足自己的特定需求。   二、自动化class和jar包部署   class和jar包只需放在工头机器上, 各工人机器会自动获取并执行,兼容操作系统,不需要进行安全密钥复杂配置   三、网络波动状况下的策略处理,设置抢救期,抢救期内网络稳定下来不判定结点死亡   本软件由www.gg265.net www.fmrpg.com站长提供
课程介绍:本课程旨在介绍Python Ray框架的基本概念、核心组件和应用,帮助学员理解并掌握Ray在分布式计算领域的应用,并在实际工作和生活中获得职业加成和思维提升。教学设计:本课程采用系统化的教学设计,结合理论讲解和实际案例演示,以帮助学员全面理解Ray的工作原理和核心特性。课程内容根据逻辑顺序进行组织,从基础概念到高级应用,循序渐进地引导学员掌握Ray的使用技巧和最佳实践。内容特色:重点介绍Ray的任务并行模型、对象存储和Actor模型,让学员理解并运用这些特性进行高效的分布式计算。强调实际应用案例,展示Ray在分布式机器学习、强化学习、推荐系统等领域的成功应用,激发学员的创造力和应用思维。讲解方式:课程讲解采用简洁明了的语言,结合图示和示例代码,帮助学员轻松理解和掌握Ray的概念和使用方法。通过实际案例演示,学员可以看到Ray在实际项目中的应用场景和效果,进一步加深理解。与其他同类课程的差异化:本课程专注于Python Ray框架的介绍和应用,注重深入讲解任务并行、对象存储和Actor模型等核心概念。通过实际案例和应用场景的讲解,将Ray的理论知识与实际应用相结合,帮助学员将所学知识应用到实际工作和生活中。课程收益:     参与本课程后,学员将获得以下收益:职业加成:掌握Ray的分布式计算能力,提升在职场上解决复杂问题和处理大规模数据的能力。思维提升:理解任务并行、对象存储和Actor模型的思维模式,培养分布式思维和并发编程的能力。实际应用:通过实际案例演示和练习,学员将能够将Ray应用于实际工作项目中,提高工作效率和解决问题的能力。通过本课程的学习,您将掌握Python Ray框架的核心概念和应用,获得在分布式计算领域的专业知识,以及在实际工作和生活中获得的职业加成和思维提升。
淘宝Fourinone(中文名字“四不像”)是一个四合一分布式计算框架,在写这个框架之前,我对分布式计算进行了长时间的思考,也看了老外写的其他开源框架,当我们把复杂的hadoop当作一门学科学习时,似乎忘记了我们想解决问题的初衷:我们仅仅是想写个程序把几台甚至更多的机器一起用起来计算,把更多的cpu和内存利用上,来解决我们数量大和计算复杂的问题,当然这个过程中要考虑到分布式的协同和故障处理。如果仅仅是为了实现这个简单的初衷,为什么一切会那么复杂,我觉的自己可以写一个更简单的东西,它不需要过度设计,只需要看上去更酷一点,更小巧一点,功能更强一点。于是我将自己对分布式的理解融入到这个框架中,考虑到底层实现技术的相似性,我将Hadoop,Zookeeper,MQ,分布式缓存四大主要的分布式计算功能合为一个框架内,对复杂的分布式计算应用进行了大量简化和归纳。 首先,对分布式协同方面,它实现了Zookeeper所有的功能,并且做了很多改进,包括简化Zookeeper的树型结构,用domain/node两层结构取代,简化Watch回调多线程等待编程模型,用更直观的容易保证业务逻辑完整性的内容变化事件以及状态轮循取代,Zookeeper只能存储信息不大于1M的内容,Fourinone超过1M的内容会以内存隐射文件存储,增强了它的存储功能,简化了Zookeeper的ACL权限功能,用更为程序员熟悉rw风格取代,简化了Zookeeper的临时节点和序列节点等类型,取代为在创建节点时是否指定保持心跳,心跳断掉时节点会自动删除。Fourinone是高可用的,没有单点问题,可以有任意多个复本,它的复制不是定时而是基于内容变更复制,有更高的性能,Fourinone实现了领导者选举算法(但不是Paxos),在领导者服务器宕机情况下,会自动不延时的将请求切换到备份服务器上,选举出新的领导者进行服务,这个过程中,心跳节点仍然能保持健壮的稳定性,迅速跟新的领导者保持心跳连接。基于Fourinone可以轻松实现分布式配置信息,集群管理,故障节点检测,分布式锁,以及淘宝configserver等等协同功能。 其次, Fourinone可以提供完整的分布式缓存功能。如果对一个中小型的互联网或者企业应用,仅仅利用domain/node进行k/v的存储即可,因为domain/node都是内存操作而且读写锁分离,同时拥有复制备份,完全满足缓存的高性能与可靠性。对于大型互联网应用,高峰访问量上百万的并发读写吞吐量,会超出单台服务器的承受力,Fourinone提供了fa?ade的解决方案去解决大集群的分布式缓存,利用硬件负载均衡路由到一组fa?ade服务器上,fa?ade可以自动为缓存内容生成key,并根据key准确找到散落在背后的缓存集群的具体哪台服务器,当缓存服务器的容量到达限制时,可以自由扩容,不需要成倍扩容,因为fa?ade的算法会登记服务器扩容时间版本,并将key智能的跟这个时间匹配,这样在扩容后还能准确找到之前分配到的服务器。另外,基于Fourinone可以轻松实现web应用的session功能,只需要将生成的key写入客户端cookie即可。 Fourinone对于分布式大数据量并行计算的解决方案不同于复杂的hadoop,它不像hadoop的中间计算结果依赖于hdfs,它使用不同于map/reduce的全新设计模式解决问题。Fourinone有“包工头”,“农民工”,“手工仓库”的几个核心概念。“农民工”为一个计算节点,可以部署在多个机器,它由开发者自由实现,计算时,“农民工”到“手工仓库”获取输入资源,再将计算结果放回“手工仓库”返回给“包工头”。“包工头”负责承包一个复杂项目的一部分,可以理解为一个分配任务和调度程序,它由开发者自己实现,开发者可以自由控制调度过程,比如按照“农民工”的数量将源数据切分成多少份,然后远程分配给“农民工”节点进行计算处理,它处理完的中间结果数据不限制保存在hdfs里,而可以自由控制保存在分布式缓存、数据库、分布式文件里。如果需要结果数据的合并,可以新建立一个“包工头”的任务分配进行完成。多个“包工头”之间进行责任链式处理。总的来说,是将大数据的复杂分布式计算,设计为一个链式的多“包工头”环节去处理,每个环节包括利用多台“农民工”机器进行并行计算,无论是拆分计算任务还是合并结果,都可以设计为一个单独的“包工头”环节。这样做的好处是,开发者有更大能力去深入控制并行计算的过程,去保持使用并行计算实现业务逻辑的完整性,而且对各种不同类型的并行计算场景也能灵活处理,不会因为某些特殊场景被map/reduce的框架限制住思维,并且链式的每个环节也方便进行监控过程。 Fourinone也可以当成简单的mq来使用,将domain视为mq队列,每个node为一个队列消息,监控domain的变化事件来获取队列消息。也可以将domain视为订阅主题,将每个订阅者注册到domain的node上,发布者将消息逐一更新每个node,订阅者监控每个属于自己的node的变化事件获取订阅消息,收到后删除内容等待下一个消息。但是Fourinone不实现JMS的规范,不提供JMS的消息确认和消息过滤等特殊功能,不过开发者可以基于Fourinone自己去扩充这些功能,包括mq集群,利用一个独立的domain/node建立队列或者主题的key隐射,再仿照上面分布式缓存的智能根据key定位服务器的做法实现集群管理。 Fourinone整体代码短小精悍,跟Hadoop, Zookeeper, Memcache, ActiveMq等开源产品代码上没有任何相似性,不需要任何依赖,引用一个jar包就可以嵌入式使用,良好支持window环境,可以在一台机器上模拟分布式环境,更方便开发。 开发包里自带了一系列傻瓜上手demo,包括分布式计算、统一配置管理、集群管理、分布式锁、分布式缓存、MQ等方面, 每个demo均控制在少许行代码内,但是涵盖了Fourinone主要的功能,方便大家快速理解并掌握。 Fourinone 2.0新增功能: Fourinone2.0提供了一个4合1分布式框架和简单易用的编程api,实现对多台计算机cpu,内存,硬盘的统一利用,从而获取到强大计算能力去解决复杂问题。Fourinone框架提供了一系列并行计算模式(农民工/包工头/职介绍/手工仓库)用于利用多机多核cpu的计算能力;提供完整的分布式缓存和小型缓存用于利用多机内存能力;提供像操作本地文件一样操作远程文件(访问,并行读写,拆分,排它,复制,解析,事务等)用于利用多机硬盘存储能力;由于多计算机物理上独立,Fourinone框架也提供完整的分布式协同和锁以及简化MQ功能,用于实现多机的协作和通讯。 一、提供了对分布式文件的便利操作, 将集群中所有机器的硬盘资源利用起来,通过统一的fttp文件路径访问,如: windows:fttp://192.168.0.1/d:/data/a.log linux:fttp://192.168.0.1/home/user/a.log 比如以这样的方式读取远程文件: FttpAdapter fa = FttpAdapter("fttp://192.168.0.1/home/log/a.log"); fa.getFttpReader().readAll(); 提供对集群文件的操作支持,包括: 1、元数据访问,添加删除,按块拆分, 高性能并行读写,排他读写(按文件部分内容锁定),随机读写,集群复制等 2、对集群文件的解析支持(包括按行,按分割符,按最后标识读取) 3、对整形数据的高性能读写支持(ArrayInt比ArrayList存的更多更快) 4、两阶段提交和事务补偿处理 5、自带一个集群文件浏览器,可以查看集群所有硬盘上的文件(不同于hadoop的namenode,没有单点问题和容量限制) 总的来说, 将集群看做一个操作系统,像操作本地文件一样操作远程文件。 但是fourinone并不提供一个分布式存储系统,比如文件数据的导入导出、拆分存储、负载均衡,备份容灾等存储功能,不过开发人员可以利用这些api去设计和实现这些功能,用来满足自己的特定需求。 二、自动化class和jar包部署 class和jar包只需放在工头机器上, 各工人机器会自动获取并执行,兼容操作系统,不需要进行安全密钥复杂配置 三、网络波动状况下的策略处理,设置抢救期,抢救期内网络稳定下来不判定结点死亡 fourinone-3.04.25最新版升级内容: 1、编译和运行环境升级为jdk7.0版本; 2、计算中止和超时中止的支持,比如多台计算机工人同时执行查找,一旦某台计算机工人找到,其余工人全部中止并返回。以及可以由工人控制或者框架控制的计算过程超时中止。 3、一次性启动多工人进程支持,可以通过程序api一次性启动和管理“ParkServer/工头/工人”多个进程,并附带良好的日志输出功能,用于代替写批处理脚本方式,方便部署和运行。 4、增加了相应指南和demo。 本软件遵循apache2.0开源软件协议,自由分享 (C) 2007-2012 Alibaba Group Holding Limited
淘宝Fourinone2.0提供了一个4合1分布式框架和简单易用的编程API,实现对多台计算机CPU,内存,硬盘的统一利用,从而获取到强大计算能力去解决复杂问题。Fourinone框架提供了一系列并行计算模式(农民工/包工头/职介绍/手工仓库)用于利用多机多核CPU的计算能力;提供完整的分布式缓存和小型缓存用于利用多机内存能力;提供像操作本地文件一样操作远程文件(访问,并行读写,拆分,排它,复制,解析,事务等)用于利用多机硬盘存储能力;由于多计算机物理上独立,Fourinone框架也提供完整的分布式协同和锁以及简化MQ功能,用于实现多机的协作和通讯。 fourinone-2.05.28\fourinone2.0 .................\............\config.xml .................\............\fourinone-2.05.28-src.jar .................\............\fourinone-2.05.28.jar .................\............\指南和demo .................\............\..........\MQ demo .................\............\..........\.......\MQ demo.txt .................\............\..........\.......\ParkServerDemo.java .................\............\..........\.......\Publisher.java .................\............\..........\.......\Receiver.java .................\............\..........\.......\Sender.java .................\............\..........\.......\Subscriber.java .................\............\..........\WordCount .................\............\..........\.........\inputdata.txt .................\............\..........\.........\ParkServerDemo.java .................\............\..........\.........\WordCount.txt .................\............\..........\.........\WordcountCT.java .................\............\..........\.........\WordcountWK.java .................\............\..........\分布式文件访问和操作demo .................\............\..........\........................\FttpBatchWriteReadDemo.java .................\............\..........\........................\FttpCopyDemo.java .................\............\..........\........................\FttpMulCopyDemo.java .................\............\..........\........................\FttpMulWriteReadDemo.java .................\............\..........\........................\FttpOperateDemo.java .................\............\..........\........................\FttpParseDemo.java .................\............\..........\........................\FttpRo

566

社区成员

发帖
与我相关
我的任务
社区描述
英特尔® 边缘计算,聚焦于边缘计算、AI、IoT等领域,为开发者提供丰富的开发资源、创新技术、解决方案与行业活动。
社区管理员
  • 英特尔技术社区
  • shere_lin
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧