在本章的三次作业里,每次作业我都有一个主题,分别是:托盘型共享数据、单步电梯运行优化、多部电梯运行优化,因而电梯优化实际是第二、三次作业。虽然后两次作业从性能分上看做得还不错,但阅读其他大佬博客,我深深地体会到鄙人朴素拙劣的调度算法能够取得这样的分数实属幸运,因此,对于这篇博客,我将只简略地介绍一下自己的调度算法,把重点放在关于电梯调度架构的思考上。

电梯调度算法

  1. 单电梯调度:对于每部电梯,我的调度算法都是朴素的LOOK算法,并且当休眠的电梯同时收到了可上可下的请求时,其会向从接人运行路程较短的方向运行。

  2. 多电梯调度:本次作业的多电梯各数据参数已经详细给出,而且调度效果不好标准化衡量、数据稀少,不利于使用多参数的预测表达式作为决策依据,因此我选择的策略是设计基于人工先验知识的调度算法,具体来说,我的调度策略如下:

    1. 能够直达的任务必须直达

      1. 策略一:首先,C电梯能执行则给C电梯,其次,B电梯能执行的给B电梯,最后,A电梯能执行的给A电梯。
      2. 策略二:负载均衡,在ABC电梯中选择能够执行任务的电梯,并将任务委派给总量最小的电梯。
    2. 不能直达的任务至多拆解为两个

      根据Excel表格的分析,在基于直达任务必须直达的前提条件下,不可直达任务的起/终点集中于15层以上、-3层和3层,对于15层以上楼层,调度器将会把子任务目标定为15层,对于-3层和3层,调度器会把子任务目标定位1层。

    以上策略最终合成时很简单,但其实是对各类起终点情况分类考虑后综合起来形成的结果,我使用随机生成数据进行测试,数据量50条,每条指令投放间隔时间为[0.0, 1.0],运行组数20组:

    策略 平均调度时间
    基准调度:直达任务随机分配+非直达任务均放置于1层 58.2542 S
    对比调度1:直达策略一+非直达拆解 51.2311 S
    对比调度2:直达策略二+非直达拆解 52.3192 S

    可以看出优化后的固定调度有一定的提升,且在强测中也有较好效果,不过根据周围同学的反馈,固定调度还是明显有性能瓶颈的,要想进一步提升,还需要在调度算法和模式上进行大幅度改进。

关于多电梯调度架构的思考

基于任务可拒绝的分布式调度结构

经过后期思考和研讨课的讨论,同学们对电梯调度算法主要分为两类:

  1. 分配式:任务由主调度器进行拆解并指定执行电梯,电梯行为被动。

    1. 优点是能够从全局上分析,找到最合适的执行电梯。
    2. 缺点则是对每部电梯进行动态仿真评估时难度较大(电梯内部数据不安全、评价标准难定等……)。
  2. 抢占式:删去主调度器,由电梯根据自身运行状态从请求列表中抢夺合适的任务,电梯行为主动。
    1. 优点是减少了动态仿真评估的复杂度(例如请求列表按照楼层分布,先到的电梯先得,免去了许多对运行的预测)。
    2. 缺点是缺乏全局分析,存在任务分配不均衡的可能性,且对于不可直达的任务,从电梯线程的角度拆分任务比较困难。

本着将两种调度优势结合(和稀泥)的考虑,提出了一种“任务可拒绝的分布式调度”结构,结构图就像这样:

具体来说,框架主要改编自“分配式”的架构,新增了由电梯线程向请求池反馈任务完成情况和退回拒绝的任务

  1. 主调度对任务的分配显然是调度,而电梯对某个任务的拒绝并释放也是另一种角度的调度,两个阶段对任务的影响从而形成了分布式的调度结构。分布式的调度方式一定程度上结合了我在上文中所提到两种方法的优势,即主调度器擅长全局地指派任务,而电梯擅长动态仿真分析接受或拒绝任务。

  2. 在此举一个任务可能流动的例子:

    1. 主调度器首次取出:根据一些全局策略(最好是基于先验知识的、依赖于静态数据的),把任务首先支配给电梯X。
    2. 电梯X分析任务:电梯X从“任务信箱“读取任务,结合自身动态运行情况(容量、速度、方向、楼层等),决定接受/拒绝任务(评估算法应该设计成“能拒绝的尽量拒绝”,因为拒绝了有可能再次收到)。
    3. 主调度器再次取出任务(任务被拒情况下):分析拒绝者身份和拒绝理由,对任务进行重新拆分、重新分配或者向电梯X委派不可拒绝的任务。
  3. 此外,在此例举两个本架构中的关键类:

    1 第一,是获取任务的请求池RequestPool,请求池是一个托盘类,生产者是输入线程和电梯线程,消费者是主调度器,由于请求池对调度器是透明的,因此主调度器的任务获取与“分配式”方式完全一致,有关请求池的守护者模式等实现请参考第二章的总结作业的心得与体会。

    public class RequestPool {
    
        // single item mode
    private RequestList() {};
    public static RequestList getInstance() {} // 由输入线程确定输入结束
    public synchronized void setNoMoreRequest() {}
    // 由输入线程投放新的乘客任务
    public synchronized void createNewPassenger(Passenger p) {}
    // 由电梯线程反馈/拒绝乘客任务:反馈的任务,乘客p的当前楼层已由电梯改变;
    // 拒绝的任务,乘客p的当前楼层不变,且附带更多拒绝信息。
    public synchronized void taskFinishFeedback(Passenger p) {}
    public synchronized void taskRejectFeedback(Passenger p) {} // 有主调度器读取在请求池中的任务
    public synchronized PersonRequest getPassenger(){}
    }

    2 第二,是乘客类Passenger,由于任务分拆中转点的设立和任务拒绝时记录详细拒绝信息的需求,课程组提供的PersonRequest功能已不足,为此需要新建乘客类满足功能需求:

    public class Passenger(){
    // 乘客最初的起点与目的地,实例化后不可更改
    final private int oriFloor;
    final private int desFloor;
    // 乘客当前所在楼层和子任务的目标楼层
    private int curFloor;
    private int tarFloor;
    // 此乘客是否可以被电梯所拒绝
    private boolean unRejectable;
    // 电梯拒绝理由的记录表
    private HashMap<Elevator,RejectRecord> rejectMap; public Passenger(PersonRequest p); // 改变和读取当前所在楼层和子任务目标楼层
    public void setCurrentFloor(int floor);
    public int getCurrentFloor();
    public void setTarFloor(int floor);
    public int getTarFloor(); // 判断乘客是否已经到达终点
    public boolean isArrive(); // 设置和读取乘客是否可被拒绝
    public void setUnRejectable(boolean flag);
    public boolean isUnRejectable(); // 新增或读取对应电梯的拒绝理由
    public void addRejectRecord(Elevator e, RejectRecord record);
    public RejectRecord getRejectRecord(Elevator e);
    }

    3 第三,是拒绝理由类RejectRecord,此类所保存的内容随主调度器算法差异而有很大差异,目前大致想到的有:拒绝搭载的路段、拒绝依据的主要理由、拒绝时的楼层方向容量数据(方便主调度器在电梯都拒绝时强制执行。)

优先队列和楼层类的电梯任务维护

对于每部电梯,其内部任务维护有着两种主流的方式——队列和楼层类:

1 队列实现:在队列中,电梯每通过一层即扫描队列中的任务,并根据电梯电梯已有任务和列表任务选择合适的方向运行。不过,如果每次都完整扫描一次队列固然性能开销很大,为此一种优化的方式是实现Comparator接口维护优先队列,按照既定逻辑(如Look)排列当前任务,电梯每到一层仅需要查看队头任务即可。

    //伪Look算法的实现
public static Comparator<Passenger> passComparator = new Comparator<Passenger>(){ @Override
public int compare(Passenger p1, Passenger p2) {
// 优先级1:在电梯运行方向的路上,内部按照与电梯距离排序。
// 优先级2:不在电梯运行方向的路上,内部按照与电梯距离排序
}
};

2 楼层类实现:在楼层而楼层方法则尽可能与现实乘电梯相似,电梯每到一层就向楼层类请求,查看存在的接送任务。

我采用的是此类方法,但在在实现时由于楼层类功能过于强大且实现结构不佳,甚至都没有显示地维护电梯内部的乘客id,因此在后期基于look算法优化和仿真评估的实现上遇到很大的阻碍,耦合过强。因此,建议在使用楼层类实现时:注意楼层类功能与电梯功能的明确划分、基于队列思想维护电梯内的“送达”乘客、动态仿真交给电梯实现楼层类只提供不复杂的参考信息。

说道最后,写OO是一个”逐渐后悔”的过程,不过勤思勤练下后悔之处会越来越少,以上的OO优化总结便结合了本章作业中我的后悔之处,其中有的想到的具体实现框架,有的地方依旧难以平衡解决。欢迎各位有思路的参与讨论,谢谢~

学会拒绝,是一种智慧——OO电梯章节优化框架的思考的更多相关文章

  1. SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 SignalR 简单示例 通过三个DEMO学会SignalR的三种实现方式 SignalR推送框架两个项目永久连接通讯使用 SignalR 集线器简单实例2 用SignalR创建实时永久长连接异步网络应用程序

    SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论   异常汇总:http://www ...

  2. OO电梯系列优化分享

    目录 前言 HW5 HW6 第二次作业uml协作图 HW7 第三次作业uml协作图 前言 本单元作业在优化方面确实有一些想法值得分享,故单开一篇博客分享一下三次作业的优化以及架构. 三次作业的共同之处 ...

  3. DBA思考系列——学会拒绝不合理的需求

    DBA思考系列--学会拒绝不合理的需求   一直以来,个性都比较随意,一般很少拒绝开发人员的一些需求(有点老好人的感觉). 这点一直被老大诟病,也一直在反省!最近又有一件事情,让我觉得:应该学会拒绝不 ...

  4. OO第三次电梯作业优化

    目录 第三次电梯作业个人优化 前言 优化思路 一.调度器 二.电梯 第三次电梯作业个人优化 前言 由于个人能力有限,第二次电梯作业只能完成正确性设计,没能进行优化,也因此损失了强测分数,于是第三次电梯 ...

  5. OO第二单元优化博客

    OO第二单元优化博客 第五次作业没有性能分,但是,我在这一单元的宗旨就是写一个日常生活中 最常见的那种电梯,所以第五次我没有写傻瓜电梯,而是直接写了个\(look\),和第六次基本相同. 总计一下lo ...

  6. OO第一单元优化博客

    OO第一单元优化博客 第一次作业: 合并同类项+提正系数项+优化系数指数0/1=满分 第二次作业: 初始想法 一开始是想以\(sin(x)​\)和\(cos(x)​\)的指数作为坐标,在图上画出来就可 ...

  7. 【转】10种简单的Java性能优化

    10种简单的Java性能优化 2015/06/23 | 分类: 基础技术 | 14 条评论 | 标签: 性能优化 分享到: 本文由 ImportNew - 一直在路上 翻译自 jaxenter.欢迎加 ...

  8. Spring框架是一种非侵入式的轻量级框架

    摘自<Spring框架技术> Spring框架是一种非侵入式的轻量级框架 1.非侵入式的技术体现 允许在应用系统中自由选择和组装Spring框架的各个功能模块,并且不强制要求应用系统的类必 ...

  9. 5种kafka消费端性能优化方法

    摘要:带你了解基于FusionInsight HD&MRS的5种kafka消费端性能优化方法. 本文分享自华为云社区<FusionInsight HD&MRSkafka消费端性能 ...

随机推荐

  1. 打开c++ 项目遇到的错误

    前言 后续持续更新: 无法打开源文件windows.h https://blog.csdn.net/Mr__George/article/details/87714252 找不到duilib.h ht ...

  2. wdcp升级php5.8到php7.1.12后安装redis

    一.安装redis a.下载redis: redis最新稳定版下载http://www.redis.io/download wget http://download.redis.io/releases ...

  3. yolov3测试自己的数据

    yolov3测试自己的数据 前言 上一篇我已经介绍了利用yolov3预训练权重文件(只包含卷积层)并训练 只需要进行如下编译: ./darknet detector train cfg/voc.dat ...

  4. Atcoder比赛副站

    https://agc039.contest.atcoder.jp/

  5. Maven的原理和使用

    一.Maven能做什么 1.假设我们有10个项目,都需要引入spring core模块,那么需要十份重复的Spring Core.jar和commons-logging.jar 使用Maven:mav ...

  6. Django(五)1 - 4章实战:从数据库读取图书列表并渲染出来、通过url传参urls.py path,re_path通过url传参设置、模板语法

    一.从数据库读取图书数据并渲染出来 1)app1/views.py函数books编写 [1]从模型下导入bookinfo信息 [2]从数据库获取图书对象列表 [3]把获取到的图书对象赋值给books键 ...

  7. vue2-dragula vue拖拽组件

    https://github.com/kristianmandrup/vue2-dragula git 地址 https://github.com/kristianmandrup/vue2-dragu ...

  8. C++ 检测物理内存以及磁盘空间

    BOOL CheckResource() { MEMORYSTATUSEX statex; statex.dwLength = sizeof (statex); GlobalMemoryStatusE ...

  9. C++ Winsock

    由于兼容的问题更新下winsock,有较好的移植性:客户端是非阻塞的,服务器是阻塞的! Win32控制台: 数据收发: 服务器向客户端发送一个txt文本内容和一个结构体数据: 服务器代码: #incl ...

  10. 112-PHP类变量之间的赋值标识为同一个对象(二)

    <?php class mao{ //定义猫类 public $age=0; //定义多个属性并初始化 public $weight=50; public $color='white'; } $ ...