A2B Game Solutions

A2B is a "zach-like" programming game, which let you to use a very simple "programming language" to solve different problems for strings.

Personally, I highly recommand this game along with "Shenzhen IO" and "Factorio" as an beginner tutorial for anyone who wants to be a software engineer.

Spoiler Alert

The …

more ...

浅淡TCP BBR

背景

在一对跨地域的机器(美国<->香港),使用TCP(Cubic拥塞控制算法)通信throughput最高2MB/s,丢包率0.02%。使用UDP通信throughput最高能达到140MB/s。

这是一个非常典型的长肥管道(LFN),并且丢包率比较高。尝试使用BBR算法后,throughput可达50MB/s+(Windows系统,通信协议使用用户态MsQuic)。

Loss-based Congestion Control Algorithm

Reno和Cubic是比较经典的,用于TCP的拥塞控制算法。这一类算法使用的是基于丢包反馈的思想,即一旦产生了丢包,就认为链路上产生了拥塞。先将拥塞窗口减半,再进入快速恢复模式。

在快速恢复完成后中,就又会重新进入拥塞避免阶段。

TCP-Reno-Cubic

Reno当收到一个ACK包时,会将拥塞窗口增大一个MSS,窗口大小线性增长。而Cubic使用的是一个基于上次拥塞事件产生时间的三次函数,所以拥塞窗口能更快速的恢复到拥塞事件发生之前的大小。

但无论是Reno还是Cubic,在遭遇高丢包率的时候,其拥塞控制窗口的大小会一直处于一个非常小的状态。

在RTT较大时,拥塞窗口的大小增长速度更加缓慢,使得带宽利用率长时间维持在一个较低的状态。

下图为Cubic在有丢包(0.002%, 0.02%, 0 …

more ...

论文阅读-WiscKey:SSD友好的KV分离存储引擎

背景

基于LSM-Tree的存储引擎

Log-Structed Merge-Tree (a.k.a. LSM-Tree)是当下常用的一种基于磁盘的存储引擎。与Hash索引和B-Tree同为数据库核心的数据结构。

LSM-Tree的优势在于:

  1. 无需将所有的Key索引在内存中。可以通过分级查找的方式,查询到特定KV在磁盘中的偏移量
  2. 数据写入与合并使用顺序追加写,最大程度的利用磁盘的顺序写性能
  3. 对于数据写入,会使用batch方式写入磁盘
  4. 支持范围查询

LSM-Tree的劣势在于:

  1. 读放大与写放大
  2. 无法对单条数据加锁(事务支持)

现在常用的LevelDB和RocksDB,都是基于LSM-Tree的存储引擎。

LSM-Tree-Architecture

WiscKey主要解决的问题

LSM-Tree在性能上面的瓶颈主要在于读写的放大。简单说来,假设你的磁盘最大带宽为4GB/s,读写放大倍数为10,那么在应用层的有效吞吐量(Goodput)最多只能达到400MB/s。并且,对于一些大数据集,读写放大的值有可能会非常大(大于100)。

那么是什么原因导致的读写放大呢?我们下面分开讨论。

读放大

我们重温一下LSM-Tree的查询机制:

  1. 首先在mutable memtable中进行查找
  2. 然后在immutable memtable中进行查找
  3. 最后在不同的LSM-Tree层级中的SST file中进行查找

对于1和2 …

more ...

利用Arduino制作脚踏板Fn层开关

背景

之前焊了两个客制化键盘,一个是ErgoDone,一个是仿minila配列的60键盘。这两个键盘的共同特点是双手大拇指各有一个额外的开关,我将其做为Fn层的开关,用来开启两个fn层:Vim和NumPad(是啥并不重要,可以理解为功能键的重映射)。

这两把键盘都是Cherry红轴。最近想把之前的酷冷扰民青轴87键用起来,但是因为它是标准配列,所以没有Fn键。重新焊一把新键盘或者换轴又太贵了。所以想用软硬结合的方法,额外做两个脚踏板按键,用来控制相应的Fn层。

所需要的零件

  • 一把正(yang)常(jian)配列的键盘
  • Arduino Pro Micro (TB,25包邮,大概买贵了)
  • 两个脚踏板(TB,23两个包邮)
  • 一根USB 2.0公母线(PDD,3块钱。用来分离脚踏板开关和面包板)
  • 小号面包板一个(TB,5块)
  • 1个1K欧的电阻(成本不计,我用了两个)
  • 一些跳线和导线(成本不计)

电路设计

circuit

画图网站上只有Arduino Uno …

more ...

我到底从UW-CSE341学到了什么

TL;DR 什么也没学到

课程首页

Coursera-课程-A, B, C

介绍当中说,本课程主要内容是FP,并且顺道讨论一下程序语言的设计。

UW官方上面的课程只有Slides和assignments,video不提供。Coursera有video,但是版本比较老,是2013年的。下面的讨论都是基于au20的新版本。

简单来说,前半学期(hw1-hw4)是关于OCaml,一种静态类型的FP。后半学期(hw5-hw7)是Racket,一种动态语言FP。课程里面80%+(的难度)都在于新的FP编程语言,只包含少数PL的内容。例如HW4中,需要实践一个简单粗暴的tokenizer/parser;HW6实现了一个虚假的编程语言MUPL,这个实现并不需要你解析代码,而是手写解析好的语法树,在上面实现求值、闭包、递归等。

调试与debug也是一个非常蛋疼的问题,尤其是Racket写闭包的时候。。。

学习建议:

  1. OCaml的部分比较简单,7天一个小长假就可以搞定。做为平时的娱乐调剂也可以,周期不会超过一个月。
  2. 如果不想使用OCaml,可以尝试F#。因为都是一个流派的,语法接近 …
more ...

Introduction to Ceph

什么是Ceph

Ceph是一个可扩展的,高性能的分布式存储系统。提供了三种不同类型的接口以适应不同的应用场景:

  • block-based: 块存储,可以用做VM的虚拟磁盘
  • object-based: 对象存储,与Amazon S3等常用对象存储兼容
  • file system: POSIX兼容的分布式文件系统,可以被本地系统挂载,并且能被多个客户端共享

Ceph的特性

由于采用了CRUSH算法,Ceph有着优异的可扩展性(宣称可以无限扩展)。并且借助可扩展性,进而实现高性能、高可靠性和高可用性。

Ceph是一个去中心化的存储系统,无需中心节点进行资源的管理与调度,全部的管理功能由存储节点自治完成。使得整个系统可以自我管理与自我恢复,减少运维成本与管理成本。

RADOS - Ceph的存储引擎

RADOS=Reliable Autonomic Distributed Object Store。RADOS是Ceph底层的存储引擎,所有的接口都建立在RADOS的功能之上。

RADOS中的存储结构

  • 存储池(pool):逻辑层,每一个pool里都包含一些放置组
  • 放置组(placement-group, PG):逻辑层,一份数据会在PG当中进行灾备复制。每一个PG都对应着一系列的存储节点
  • 存储节点 …
more ...

2020 计蒜之道 线上决赛 - C. 攀登山峰

本文使用了一种概率算法,不是正解,只求骗分。

题意

原题

给一个长度为n的数组A[1...n]。

现在有q个查询请求(q <= 1e5),每个请求给定一个长度为m的子数组A[i...j]和一个整数t。问在子数组中出现次数超过m/t的数字中(t <= 20),最大的数是多少。

题意中的坑

阈值m / t是一个实数。(题意不明先死个🐴好吗?)

解法

首先我们将每种数字出现的位置简历一个索引,此时对于任意一个数,我们可以用二分法在O(logn)的时间内求出其在某个区间内出现的次数。

因为t最大是20,也就意味着如果我们随机取数,那么取到一个符合出现次数限制的(超过m/t次)数字的概率是1/t。如果我们进行多次尝试,并使用上面的方法对其进行验证,几乎可以保证我们一定可以取到正确的那一个。

那么“多次尝试”是多少次呢?我们枚举所有的t,然后计算其在q=1e5的情况下的成功率。因为这是一个随机算法,我们先使用下面的代码分别计算一下成功率为10%和50 …

more ...

6.824 Lab 2: Raft协议实现指南 (无剧透版)

背景

MIT6.824是一个用来学习分布式系统的非常好的资源。其中第二个课程作业就是关于Raft算法

由于在工作中涉及到分布一致性算法的调研,接触了paxos/raft算法。然后被@neutronest安利了一发,于是开始着手实现这个作业。

本文是我对这个项目的实现总结。希望能在划出重点的同时,不涉及实现细节,避免破坏大家的写代码体验。

本文唯一参考资料:In Search of an Understandable Consensus Algorithm

任务分解

在官方文档里,整个项目被分成了2A、2B、2C三个部分:

  • 2A - 投票与选举
  • 2B - 一致性
  • 2C - 可持久化

实际上,2C的工作量非常少,我们可以把2B和2C合成一个。然后单独提出几个比较重要的测试用例,划分成子项目。任务分解如下:

  • 2A - 投票与选举
  • 2B/2C - 一致性和可持久化
  • 三个难度比较高的Case
    • Test (2B): leader backs …
more ...

白话一致性协议 - Paxos、Raft和PacificA[2]

PacificA是一个框架

虽然在题目中我们把Paxos、Raft和PacificA并列,但是Paxos和Raft在论文中称自己为一种“算法”(algorithm);而PacificA对自己的定位是一种通用的,强一致的数据同步框架

在论文中,作者提到现有的可证明的数据同步协议往往过分简化了性能问题,导致看起来很美的理论设计无法转变有实际的系统(注:本篇论文应该早于Raft的论文)。所以作者的目标是将算法理论与实际相结合,进行相应的系统设计。

PacificA的实现

与Paxos和Raft不同的是,PacificA采用的算法非常简单。所以在这里我不进行太多铺垫,直接进入正题。

主从同步

PacificA使用了主从同步模型,用户的读写请求都会发往主节点,以保证强一致性。

主节点会对发来的写请求进行编号,使得所有写请求编号唯一且单调递增。这个编号也会同步到从节点。

无论是主节点还是从节点,所有的写请求都写入一个只追加的日志。主从节点都需要维护committed指针,代表在这个指针之前的数据都已经被所有节点所认可,处理“已确认”状态。从节点需要额外维护一个prepared指针,代表在这个指针之前所有的数据都处于“待确认”状态,而在prepared指针之后的数据,都是“待同步”状态。

主从之间的数据同步使用两步提交模型 …

more ...

C++类型擦除与`std::function`性能探索

什么是类型擦除

对于Python这种动态类型语言来说,是不存在“类型擦除”这个概念的。Python对象的行为并不由接口定义,而是由“当前方法和属性的集合”决定。

所以,以下的代码是完全合法的:

class Foo(object):
    def print(self):
        print 'foo'

class Bar(object):
    def print(self):
        print 'bar'

def do_print(obj):
    print obj.print()

do_print(Foo()) // print 'foo'
do_print(Bar()) // print 'bar'

但是对于C++这种静态类型语言来讲,我们就不能使用这种语法:

class Foo {
public:
    void print() { cout …
more ...