301
社区成员
发帖
与我相关
我的任务
分享这一单元作业的架构思路最初来源于性能分的计算公式:

首先,从图中的公式可以看出,一个中庸的实现(假设自己的 x 正好是 base 的 max 与 min 的平均数)就可以得到90%的性能分。此外,性能分有三个评价指标,而且评价指标间大体上相互矛盾,在当时的我看来几乎不可能有一种靠谱的算法可以保证性能的最优(尤其是在强测数据分布未知的情况下)。因此,我在 hw5 初步设计架构时,着重考虑了以下两点:
基于上述思想,我选择了比较基本的 ALS 策略(毕竟出现在作业指导书中的算法很可能成为主流)、放弃了调度器(比较好的随机竞争的调度策略可能更接近平均)、在一些细节上采用了更保守的处理(以性能换可靠性)。
这两条原则也用到了 hw6 作业中(于是我的hw6设计中没有中央调度器)。但在 hw6 后,我发现情况与预想的不太一样。许多同学用了影子电梯、量子电梯与look策略,于是朴素的ALS策略相比之下性能会比较差。而且强测的数据点数量很有限,并且应该是特殊选取的。最终在 hw6 的强测中有若干数据点性能分接近满分,有数量几乎相同的数据点性能分接近没有,另有若干数据点介于两者之间,最后性能分只有一半,以另一种方式实现了中庸。
回看,其实朴素的ALS策略随机性比较差,在某些数据下表现会很糟糕,但过分地引入随机又可能导致性能的普遍降低,因此还是需要一个调度器来在普遍性能与随机性之间做平衡。但毕竟在hw6中没有实现调度器,在 hw7 中加入调度器工作量会很大,并且带来的性能提升不会太多(一个简单的调度器没有任何理由打败影子电梯,而即使是影子电梯也不都能做到“中庸的实现”),于是在 hw7 中我沿用了自由竞争的策略,最后性能分也只有一半 (果然)。
虽然性能分无了,但这样的设计策略还是给代码编写与bug排查带来了很大的便利。最大的优势就是在线程的安全性上。由于不存在调度器,程序全程只有电梯线程与 Input 线程轮换着读写数据。只需要设置一个全局的锁,得到该锁的线程可以访问任意公共区的数据,仅仅这样就可以实现线程安全。由于不追求局部的极致性能,因此电梯 wait 可以全部设计为定时唤醒(如果电梯空闲就以一定的频率轮询),这样整个程序中就可以不用 notify ,也就不必记录时间戳,大大简化了程序的逻辑。


从图表中可以看出,我在 hw6 与 hw7 的迭代中在原有设计上增加了内容,没有进行大规模的重构,并且线程设计相对简单。
在三次作业中,稳定的内容主要是 Passenger 类、 MainWaitingQueue 类,而变化较大的类主要是 Elevator 类(由于对 reset 进行了特殊处理,在 hw6 迭代中加入的 reset 会改变电梯的运作模式)、Input 类(需要对新的请求类型进行解析)。
三次作业中我采取的都是一种类似于自由竞争的策略。因此,我在设计时没有加入中央调度器,只采用了电梯的局部调度器,由电梯自主选取乘客。
调度策略:
电梯如果要运行,首先需要选定主乘客。如果电梯内没有乘客,则选取中央等待队列中的第一个乘客作为主乘客。
电梯选定主乘客后,输出 Receive,前往主乘客处,中途允许搭载。
电梯接受主乘客后,前往主乘客的目的地,中途允许搭载。
如果电梯开始 reset ,则立刻(或者尽快)将所有乘客抛出,并开始 reset 。
指标平衡:
按照乘客到来时间决定乘客优先级,而非电梯与乘客的距离。这样可以让早到的乘客尽可能先得到服务,并且如果每次都选取最近/最远的乘客,可能导致总运行时间/电梯电量性能过差。相比之下,按时间决定优先级是一个折中的、比较随机的策略。
在向中央等待队列中加入乘客时我引入了一些随机性,即在新用户加入主请求队列时,将其插入队首。虽然可能会让原来的乘客等待更长时间,但新加入的乘客可能是换乘乘客,这样做可以一定程度上避免他们等待过长时间,也可以避免构造数据导致调度效率低下。
由于设计的特殊性,得到全局锁的电梯可以访问全部公共区信息。双轿厢电梯只需要创建一个两个子电梯的信息共享区,并存储换乘楼层的占用情况。当一个子电梯需要进入/离开换乘楼层时,修改公共区内容即可。如果一个电梯需要进入换乘楼层,并且另一个子电梯正在换乘楼层,那么该电梯先等待,并向公共区域写入请求,等换乘楼层空闲时再进入。
架构设计要三思而后行。这次没有中央调度器的离奇设计虽然还算顺利地通过了 hw7,但从性能与可扩展性的角度上将都不如有中央调度器的设计。在对新的需求进行设计架构时要对之前的设计进行审视,让程序能适应新的潜在需求。还要注意程序中的某些细节。这次的作业中我写许多性能细节都没有处理(比如电梯等待的轮询),这也为性能分爆炸埋下伏笔。
这一单元的课程中,我尝试了新的测试方式。由于上一单元被依赖评测机的习惯坑过,在这一单元的作业中,我没有使用评测机,而是尝试用走查的方式解决程序中的 bug。hw6 的代码走查不仔细,结果出现了 bug 。在 hw7 的中测 debug 中,我采用只看评测结果,不看解释信息的方式,去盲找程序中可能出现的问题。这样的走查可能发现一些比较明显的、测试无法覆盖的问题,不失为对中测的一个补充。
在这次作业中,我对于线程安全的实现比较简单粗暴,没有遇见死锁等多线程的 bug。在以后的作业中我应该尽早研究与死锁相关的问题,避免以后线程变复杂时出现死锁。