几个月前,Linus Torvalds 在 Linux 5.9 版本中重写了 wait_on_page_bit_common() 相关逻辑,原因是为了解决页面锁竞争的公平性问题。
在这之前,页面锁持有者在执行 ”unlock_page()" 时仅会唤醒等待队列里的一个独占进程(FIFO,且该进程正在执行 “lock_page” ),但是该进程不一定正在运行,特别是在负载情况下,只有其获得 CPU 时间才能真正运行,与此同时,其它正在运行的没有进入等待队列的进程可以直接获得页面锁,这个过程虽然依然有效利用了页面,但会导致非常不公平的情况,被唤醒的等待进程运行后发现页面锁没有释放,于是重新进入等待队列,而且是最后一个,极端情况下,这个进程可能会重复该过程以至于等待数十秒。
因此 Linus Torvalds 修改为在唤醒等待进程时同时会把锁交给它,无论它是否正在运行。
然而,这并没有完全解决问题。自这之后,偶尔会发生 BUG_ON() ,因为上述 “wait-to-set” 的过程并非原子性的,所以被唤醒的进程仍有可能被“第三者插足”。如果要把 “wait-to-set” 这一过程修改为原子性操作,需要修改大约 50 个函数。因此,在上周,Linus Torvalds 把 wait_on_page_writeback () 函数中的 if 修改为 while 来解决这一问题。而这基本上又回到了最开始的情况,不过发生频率要低得多,因此仍然可控。
不过,据外国媒体报道,其在该版上的基准测试中, PostgreSQL 性能下降了 5%-10%,对此,Linus Torvalds 认为这种下降并非由该补丁引起,可能和其测试模型或机器性能有关。
Linux 5.11-rc3 现已发布,感兴趣的同学可以进行相关测试。
更多详细内容查看以下 commit:
精彩评论
有人提出,从代码逻辑上讲,if会使得trace_wait_on_page_writeback和wait_on_page_bit运行0次或者1次,而while会使得trace_wait_on_page_writeback和wait_on_page_bit运行0次或者N次,确实会增加这段代码运行的时间。
有人提出改成while会增加汇编代码中的JMP指令,可能有指令对齐的问题,但有人说这不会造成指令对齐问题。我更倾向于后者。
在绝大多数情况下,trace_wait_on_page_writeback和wait_on_page_bit只会运行一次。可能是PostgreSQL基准测试,恰好触发了那种运行N次的场景。
这个patch对PostgreSQL性能的影响,只出现在:有100~250个PostgreSQL客户端,同时PostgreSQL服务器是低端硬件,比如桌面级的Core i7/i9或者Ryzen这种机器,这样一种场景下。
对于更大型的服务器,比如有十几个CPU核的数据库服务器来说,这种性能问题并不会出现。
这块东西在3和4的内核同样会有吗
最新评论(17)
对于更大型的服务器,比如有十几个CPU核的数据库服务器来说,这种性能问题并不会出现。
Linus当然知道,这会在极其少见的情况下造成性能损失,但这个patch是在页面锁竞争的公平性、代码修改工作量、和性能之间进行权衡之后的选择。
目前来看影响不大,在绝大多数情况下,trace_wait_on_page_writeback和wait_on_page_bit只会运行0次或者1次,而不是循环N次。
有人提出,从代码逻辑上讲,if会使得trace_wait_on_page_writeback和wait_on_page_bit运行0次或者1次,而while会使得trace_wait_on_page_writeback和wait_on_page_bit运行0次或者N次,确实会增加这段代码运行的时间。
有人提出改成while会增加汇编代码中的JMP指令,可能有指令对齐的问题,但有人说这不会造成指令对齐问题。我更倾向于后者。
在绝大多数情况下,trace_wait_on_page_writeback和wait_on_page_bit只会运行一次。可能是PostgreSQL基准测试,恰好触发了那种运行N次的场景。
这个patch对PostgreSQL性能的影响,只出现在:有100~250个PostgreSQL客户端,同时PostgreSQL服务器是低端硬件,比如桌面级的Core i7/i9或者Ryzen这种机器,这样一种场景下。
对于更大型的服务器,比如有十几个CPU核的数据库服务器来说,这种性能问题并不会出现。