ARTS第一周

167 阅读4分钟

算法

Given two integers dividend and divisor, divide two integers without using multiplication, division and mod operator.

刚开始用循环减的方式实现。自测及runcode ok, submit提示超时。看了discuss,需要提升性能。

于是结合循环移位方式直接找到max/2的值对应的商, 并依次递减。 代码如下。 实际上还可以用动态规划的思想进一步提升,前面计算商的倍数应该可以被后面继续利用。

    public int divide(int idividend, int idivisor) {
        long dividend = idividend;
        long divisor = idivisor;
        long absDividend =  dividend > 0 ?  dividend: Math.abs(dividend);
        long absDivisor = divisor > 0 ?  divisor: Math.abs(divisor);

        int ret = 0;
        while (absDividend > 0) {
            long tempDivisor = absDivisor;
            long tempRet = 0;

            while (true) {
                if (absDividend >= tempDivisor) {
                    if (tempRet == 0) {
                        tempRet = 1;
                    } else {
                        tempRet <<= 1;
                    }
                    tempDivisor <<= 1;
                } else {
                    break;
                }
            }

            absDividend -= (tempDivisor >>= 1);
            ret += (tempRet);
        }

        if (((dividend > 0) && (divisor > 0))
            || ((dividend < 0) && (divisor < 0))) {
            return ret;
        } else {
            return -ret;
        }
    }

Review

描述了io在操作系统内部是如何工作的,涉及概念包括页映射,虚拟内存,文件系统,以及流式IO。

缓存处理 内核及用户空间

首先,进程通过进行read()系统调用来获取数据到缓冲区。内核收到调用请求后向磁盘控制器硬件发出命令以从磁盘获取数据。磁盘控制器通过DMA将数据直接写入内核内存缓冲区,无需主CPU的进一步介入。磁盘控制器完成填充缓冲区后,内核就会将数据从内核空间中的临时缓冲区复制到进程指定的缓冲区。

内核会尝试缓存及预取数据,因此进程请求的数据可能已经在内核空间中可用。如果是则复制该进程请求的数据。如果数据不可用,则在内核将数据带入内存时挂起进程。

虚拟内存

虚拟内存带来两个重要优势:

  1. 多个虚拟地址可以指代相同的物理内存位置。
  2. 虚拟内存空间可能大于可用的实际硬件内存。

通过将内核空间地址映射到与用户空间中的虚拟地址相同的物理地址,DMA硬件(只能访问物理内存地址)可以填充内核和用户空间进程同时可见的缓冲区。

通过这种方式可以消除内核和用户空间之间的副本,但需要内核和用户缓冲区对齐共享的页面。缓冲区也必须是磁盘控制器使用的块大小的倍数(通常是512字节磁盘扇区)。操作系统将其内存地址空间划分为页面,页面为固定大小。内存页大小为磁盘块大小的倍数,通常为2的幂(简化寻址)。典型的内存页面大小为1,024,2048和4,096字节。虚拟和物理内存页面大小相同。

文件/块定向I/O.

文件系统是更高级别的抽象

文件系统也有页面概念,其大小可以与基本内存页面或其倍数相同。典型的文件系统页面大小范围为2,048到8,192字节,并且是基本内存页面大小的倍数。

页文件系统执行I/O分为以下步骤:

  1. 确定请求跨越的文件系统页面(磁盘扇区)。磁盘上的文件内容/元数据可能分布在多个文件系统页面上,这些页面可以是非连续的。
  2. 在内核空间中分配足够的内存页以保存相应的文件系统页面。
  3. 建立这些内存页面和文件系统页面之间的映射。
  4. 为每个内存页面生成页面。
  5. 虚拟内存系统捕获页面错误,从磁盘读取内容。
  6. 文件系统解析原始数据以提取所请求的文件内容或属性信息。

在随后的I/O请求中,部分或全部文件内容可能无需从磁盘重新读取,而在前面存放的物理内存里。

Stream I/O

并非所有I/O都是面向块的。 还有必须按顺序访问字节的流式I/O,。 TTY(控制台)设备,打印机端口和网络连接是流式IO的常见示例。

Tip

读rocketmq源码经验:

  1. 找相关资料 包括功能说明,官方手册,电子书籍。
  2. 搭建调试环境。
  3. 自己能提出问题,带着问题去分析。
  4. 遇到障碍要坚持,多看两遍。
  5. 过程中用uml及时记录理解。

Share

rocketmq发送消息流程及总结:

  1. 生产者只会发送消息到master broker
  2. 客户端发送消息通过topic配置的queue个数,进行负载均衡。
  3. 客户端消息发送通过faultStrategy过滤不可用或者响应慢的broker。(顺序消息有限制)
  4. broker将所有topic的消息写到统一的commitlog。通过顺序写有效提升io性能。
  5. 性能优化方式还包括mappedfile以及组提交(每10ms做一次批量处理)
  6. 代码版本4.4.0
  7. 流程图使用plantuml绘制。

客户端发送消息流程

broker commitlog保存流程

broker consumequeue保存流程