[Fundamental] From Online Softmax to Flash Attention V3
Flash Attention from Fundamental Series
Flash Attention from Fundamental Series
以Qwen0.5B为例解析mllm的基本实现,CPU Backend
How to impl high performance 6xKx16 micro kernel
数据并行、模型并行、流水并行、专家混合
Usage of Nsight System
xv6 中的 fork() 系统调用将父进程的所有用户空间内存复制到子进程中。如果父进程很大,复制可能需要很长的时间。更糟糕的是,这项工作通常是无用的:fork() 之后通常是子进程的 exec(),它放弃了复制的内存,没有使用复制过来的大部分内存。如果父代和子代都使用了复制的页面,并且其中一个或两个都写了它,那么这个复制才是真正需要的。 所以写时复制(copy on write, cow) 这个技术变得十分的重要。在这个 lab 中,我们需要实现一个写时复制的 fork()。我们需要修改原本的 fork() 程序中做内存申请的模块,同时我们还需要实现 usertrap 来在写时(在实际写的时候碰到 cow flag,抛出分页错误)的时候处理这个 trap。 因为一个程序很可能被 fork() 了很多的分支,所以我们需要一个计数器来确定这个 page 是要被释放还是保留。 一般来说,一个正常运行的程序在写时复制的时候会有下面几个流程: fork() 出一个子进程 child。 child 拥有父进程的页的引用,并且页的 flag 有 cow 标识。 并且要把 页 引用 ++ child 需要向自己的页中写入新的数据. 此时需要重新分配内存,页引用 –。 当 child 返回的时候,可能有内存需要由 kernel 转换到 user。 当父进程销毁的时候,如果计数器为 0,则销毁,反之,不变。 每一页都需要一个计数器来进行计数,所以我们需要在 kernel 中加入一个数组来记录,查阅 xv6 book,我们发现有如下的图: Fig 1. memlayoutXV6 手册 我们需要在 kernel data 之后的区域内申请一块内存来作为计数器存储的数组。我们可以在 kalloc.c 中实现。 // ./kernel/kalloc.c struct { struct spinlock lock; struct run *freelist; // lab 5 uint* ref_cnt; struct spinlock ref_cnt_lock; char * pa_start; } kmem; 在这个 kmem 结构体中加入了一个 ref_cnt_lock 来保证计数器的正确引用。一个 ref_cnt pointer 来指示 ref array 的起始位置,使用 pa_start 来表示实际的 free memory 的起始位置。我们还需要修改初始化程序来正确的申请计数器数组,并且对 free memory 填充上一个初始值。...
RISC-V assembly Which registers contain arguments to functions? For example, which register holds 13 in main’s call to printf? Register: a0, a1, a2…, a7 for integer arguments. Register fa0, fa1, fa2…, fa7 for float arguments. Register a2 holds 13 when we call printf(). Where is the call to function f in the assembly code for main? Where is the call to g? (Hint: the compiler may inline functions.) Compiler inlined f(8) and g() in printf() function....
MIT 6.S081 Lab3 website 做 Lab 3 需要提前阅读 XV6 book,了解 RISC-V SR39 的地址格式,并且实验中大量用到了页表的准换函数,需要查阅 XV6 手册。不过,熟记 RISC-V 的地址说实在的没有什么用处,通过这个实验理解页表的工作方式并且 hands on 才是真的。 Speed up system calls 目前有很多的操作系统(Linux)在用户区和内核区之间共享一块数据(Read-Only for user),这样用户在进行系统调用的时候就不需要陷入内核态后,由内核态拷贝数据进用户态,而是将数据写在这个共享的区块内。这样可以加快操作系统的运行速度(毕竟大部分系统调用需要的内存消耗是很小的,内存的消耗在当今已经不是问题)。 在本实验中我们需要使用 ugetpid() 来进行加速获得进程的 pid。 首先就是为每一个进程创建一个页表作为共享内存区块。我们发现在 kernel\memlayout.h 中已经为我们定义好了需要的数据结构: struct usyscall { int pid; // Process ID }; 那么我们只需要在 kernel/proc.c /proc.h 中加入代码,来实现进程创建时创建页表,销毁时销毁页表的动作就行了。 在 proc.h 中,我们需要在进程的 PCB 中加入新的数据结构: struct proc { ... struct usyscall *usyscall; // using read only shared data to accelerate. ... } 为了让进程的正常创建和释放,我们需要向进程创建和销毁函数中加入对应的页表操作代码。...