10.5 锁与性能

我们前面已经看过了两类锁带来的挑战,一个是死锁,另一个是破坏了程序的模块化。这一部分来看看第三个挑战,也就是锁与性能之间的权衡。我们前面已经提过几次锁对性能的影响,但是因为这部分太重要了,我们再来详细的看一下。

基本上来说,如果你想获得更高的性能,你需要拆分数据结构和锁。如果你只有一个big kernel lock,那么操作系统只能被一个CPU运行。如果你想要性能随着CPU的数量增加而增加,你需要将数据结构和锁进行拆分。

那怎么拆分呢?通常不会很简单,有的时候还有些困难。比如说,你是否应该为每个目录关联不同的锁?你是否应该为每个inode关联不同的锁?你是否应该为每个进程关联不同的锁?或者是否有更好的方式来拆分数据结构呢?如果你重新设计了加锁的规则,你需要确保不破坏内核一直尝试维护的数据不变性。

如果你拆分了锁,你可能需要重写代码。如果你为了获得更好的性能,重构了部分内核或者程序,将数据结构进行拆分并引入了更多的锁,这涉及到很多工作,你需要确保你能够继续维持数据的不变性,你需要重写代码。通常来说这里有很多的工作,并且并不容易。

所以这里就有矛盾点了。我们想要获得更好的性能,那么我们需要有更多的锁,但是这又引入了大量的工作。

通常来说,开发的流程是:

  • 先以coarse-grained lock(注,也就是大锁)开始。

  • 再对程序进行测试,来看一下程序是否能使用多核。

  • 如果可以的话,那么工作就结束了,你对于锁的设计足够好了;如果不可以的话,那意味着锁存在竞争,多个进程会尝试获取同一个锁,因此它们将会序列化的执行,性能也上不去,之后你就需要重构程序。

在这个流程中,测试的过程比较重要。有可能模块使用了coarse-grained lock,但是它并没有经常被并行的调用,那么其实就没有必要重构程序,因为重构程序设计到大量的工作,并且也会使得代码变得复杂。所以如果不是必要的话,还是不要进行重构。

Last updated