交换如何这么慢?


77

我以某种方式碰巧换出了14 GB的内存。杀死了罪魁祸首之后,我又有了大量的可用内存,因此我想我可以再次引入重要数据。因此,使用32 GB中的5 GB和14 GB的交换空间,我跑了swapoff -a.. 4小时后,大约一半的工作完成了。

这意味着不到1 MB / s,而我可以轻松复制200 MB / s。我的交换已加密,但是所有普通分区也都加密了,使用es-ni不会导致明显的CPU负载(填充交换空间仅花费了几分钟)。我看到没有特别的理由进行优化swapoff,但是我想知道它怎么会变慢呢?


只需添加更多数据:我的主内存为32 GB,并且4个硬盘中的每一个都有32 GB的交换空间(肯定是个大问题,但是谁在乎呢?)。整个交换空间可以在不到5分钟的时间内被(解密并)读取:

time -p sudo sh -c 'for i in /dev/mapper/cryptswap?; do md5sum $i & done; wait'
014a2b7ef300e11094134785e1d882af  /dev/mapper/cryptswap1
a6d8ef09203c1d8d459109ff93b6627c  /dev/mapper/cryptswap4
05aff81f8d276ddf07cf26619726a405  /dev/mapper/cryptswap3
e7f606449327b9a016e88d46049c0c9a  /dev/mapper/cryptswap2
real 264.27

读取分区的一部分不会比读取全部分区慢。然而,阅读其中的1/10大约需要100倍的时间。

我观察到,在swapoff这两个期间,CPU大部分都处于空闲状态(可能是一个内核的10%),磁盘也处于空闲状态(由LED进行“测量”)。我还看到交换空间被一个接一个地关闭。


1
我想知道,当系统将交换的页面单独加载回RAM时,是否会出现相同的现象?例如,如果我将一个系统挂在磁盘上然后启动,则所有内容都被换出,然后将其加载回RAM。对我来说似乎也很慢。
彼得·普德拉克(PetrPudlák)2012年

是否所有交换设备都具有相同的优先级?
尼尔斯,2012年

@PetrPudlák:挂到磁盘有点不同,它只是将RAM内容写入交换区域中的可用空间,而这个(和未挂起的)速度可能要快得多。我无法尝试,因为它不适用于加密交换。
maaartinus 2012年

@Nils:是的,优先级是相同的,磁盘及其分区也是如此。
maaartinus 2012年

这使它更加奇怪。在这种情况下,交换将在所有磁盘上进行条带化-这应该非常快。iostat -d 5在此期间swapoff,磁盘上的IO 是否显示为低?
尼尔斯2012年

Answers:


53

首先,让我们看看您对硬盘的期望。您的硬盘驱动器可以顺序执行200 MB / s的速度。当你寻求因素在时间,也可以是慢。举一个任意的例子,看看Seagate的现代3TB磁盘之一ST3000DM001的规格

  • 最大持续数据传输速率:210 MB / s

  • 寻求平均读取时间:<8.5毫秒

  • 每个扇区的字节数:4,096

如果您不需要搜索,并且交换位置位于磁盘边缘附近,则可以看到最高速率= 210 MB / s

但是,如果交换数据完全分散,在最坏的情况下,您需要四处寻找读取的每个扇区。这意味着您每8.5毫秒只能读取4 KB,即4 KB / 0.0085 = 470 KB / s

因此,马上行动起来,实际上您正在以硬盘速度为生,这并非不可想象


就是说,swapoff运行速度如此之慢并且不得不无序读取页面,尤其是如果它们被快速写入(这意味着顺序),这似乎很愚蠢。但这可能只是内核的工作方式。Ubuntu错误报告#486666讨论了相同的问题:

The swap is being removed at speed of 0.5 MB/s, while the
hard drive speed is 60 MB/s;
No other programs are using harddrive a lot, system is not under
high load etc.

Ubuntu 9.10 on quad core.

Swap partition is encrypted.
Top (atop) shows near 100% hard drive usage
  DSK | sdc | busy 88% | read 56 | write 0 | avio 9 ms |
but the device transfer is low (kdesysguard)
  0.4 MiB/s on /dev/sdc reads, and 0 on writes

答复之一是:

It takes a long time to sort out because it has to rearrange and flush the
memory, as well as go through multiple decrypt cycles, etc. This is quite
normal

错误报告已关闭,但尚未解决。

梅尔·戈尔曼(Mel Gorman)的书《了解Linux虚拟内存管理器》虽然有点过时,但是同意这是一个缓慢的操作:

可以预见,负责停用区域的函数称为sys_swapoff()。此功能主要与更新swap_info_struct。在每个分页出页分页的主要任务是负责try_to_unuse()这是 极其昂贵的。

从2007年开始,关于linux-kernel邮件列表的讨论更多,主题为“ 加速交换 ”-尽管他们讨论的速度比您看到的要高。


这是一个有趣的问题,由于swapoff很少使用,因此可能会被普遍忽略。我认为,如果你真的想追查,第一步将试图更加仔细地看你的磁盘使用模式(可能与atopiostat或甚至更强大的工具,如perfsystemtap)。要寻找的东西可能是过多的查找,小的I / O操作,不断的重写和数据移动等。


5
很好的解释。应当指出的是,有可能规避大部分的碎片,并通过核心倾销交换内存的大段快速释放大部分互换:unix.stackexchange.com/questions/254202/...
布兰登杜普里

这不仅是碎片/寻求时间。我的交换是在SSD上进行的,并且随机读取速度非常快,但是swapoff命令比它应该的慢得多,并且我的SSD负载利用率约为1%。我怀疑内核或交换区(使用约90-100%的CPU)中涉及某个列表遍历。当然,如果所有工作都按顺序完成,并且磁盘搜索也很慢,那么它可能会明显增加。
Thomas Guyot-Sionnest

33

我的SSD笔记本电脑也遇到了同样的问题,因此寻找时间应该不是问题。

我找到了另一种解释。这是节选

swapoff现在的工作方式是查看交换分区中每个交换出的内存页面,并尝试查找使用该页面的所有程序。如果无法立即找到它们,它将查看正在运行的每个程序的页表以查找它们。在最坏的情况下,它将检查所有页表中分区中每个换出的页。没错-相同的页表被一遍又一遍地检查。

因此,这是一个内核问题,而不是其他任何问题。


不,这不是内核问题恕我直言。它是如何swapoff实现的。当换出的进程退出时,不需要花费很长时间。
Marki555

15
这是内核中实现swapoff的问题-因此是内核问题!您可以看到是否strace swapoff几乎所有功能都可以调用swapoff系统调用。
尼克·克雷格·伍德

1
我的服务器确实具有48 GB RAM(32核),曾使用6 GB的免费错误交换(0.7 GB)。swappiness = 10,尝试将其设置为0,还尝试了swapoff以查看会发生什么。swapoff花费了很长时间(可能需要30分钟),释放了极慢的速度。我确实在几乎没有负载的情况下具有SSD,并且CPU相似,期望交换过程需要100%的CPU。
索林

1
问题是如何实现交换(在内核中)。几年前,关于内核开发中更好的方法进行了讨论,但是他们说这是一个极端的案例,不想改变它。
Marki555

6
在具有1 TB RAM(是,TB)和2 GB交换(满足SAP要求)的服务器上,交换需要12个小时才能释放出2 GB的5%(1个CPU核心为100%)。
Marki555

22

swapoff是的,该机制效率极低。解决方法很简单:遍历进程,而不是遍历交换的页面。使用以下python脚本(我不隶属于):

git clone https://github.com/wiedemannc/deswappify-auto

请注意,守护程序操作模式仅适用于经常休眠的台式机/笔记本电脑。我不会在服务器系统上将其作为守护程序运行-只能在前台运行它,等到它报告它已经处理了一些进程,然后停止并尝试:

swapoff /dev/x

由于现在大多数页面都存在于交换和内存中,因此swapoff几乎没有什么要做,并且现在应该非常快(我看到了数百MB / s)。

前面的历史部分

前面提到的python脚本基于此答案的其余部分,而这又是我对jlong编写的较早答案改进。由于脚本要安全得多,因此我建议仅尝试将其余答案作为最后一道防线

perl -we 'for(`ps -e -o pid,args`) { if(m/^ *(\d+) *(.{0,40})/) { $pid=$1; $desc=$2; if(open F, "/proc/$pid/smaps") { while(<F>) { if(m/^([0-9a-f]+)-([0-9a-f]+) /si){ $start_adr=$1; $end_adr=$2; }  elsif(m/^Swap:\s*(\d\d+) *kB/s){ print "SSIZE=$1_kB\t gdb --batch --pid $pid -ex \"dump memory /dev/null 0x$start_adr 0x$end_adr\"\t2>&1 >/dev/null |grep -v debug\t### $desc \n" }}}}}' | sort -Vr | head

这可能需要2秒钟,实际上并不会执行任何操作,仅列出前10个内存段(实际上它会打印更多的单行代码;是的,我确实喜欢单行代码;只需检查命令,接受风险,复制并粘贴到您的外壳;这些实际上将从swap读取)。

...Paste the generated one-liners...
swapoff /your/swap    # much faster now

主要的一线安全(对我而言)是安全的,除了它读取了大量的/ proc。

为您的手动检查准备的子命令不安全。在从交换读取内存段的过程中,每个命令都将挂起一个进程。因此,对于不能容忍任何暂停的流程而言,这是不安全的。我看到的传输速度约为每分钟1 GB。(上述python脚本消除了该缺陷)。

另一个危险是对系统施加过多的内存压力,因此请与常规 free -m

它有什么作用?

for(`ps -e -o pid,args`) {

  if(m/^ *(\d+) *(.{0,40})/) { 
    $pid=$1; 
    $desc=$2; 

    if(open F, "/proc/$pid/smaps") { 

      while(<F>) { 

        if(m/^([0-9a-f]+)-([0-9a-f]+) /si){ 
          $start_adr=$1; 
          $end_adr=$2; 
        } elsif( m/^Swap:\s*(\d\d+) *kB/s ){
          print "SSIZE=$1_kB\t gdb --batch --pid $pid -ex \"dump memory /dev/null 0x$start_adr 0x$end_adr\"\t2>&1 >/dev/null |grep -v debug\t### $desc \n" 
        }
      }
    }
  }
}

这个perl脚本的输出是一系列gdb命令dump memory (range),这些命令将交换的页面重新调用到内存中。

输出从大小开始,因此很容易通过它的槽,| sort -Vr | head以按大小(SSIZE)获得前10个最大的细分。该-V代表版本号,适当的排序,但它适合我的目的。我不知道如何使数字排序工作。


你会在这里使用的数值与排序sort -t = -k 2n
斯特凡Chazelas

9
似乎没有必要使用gdb来查看进程内存(至少在最近的内核上)。一个人可以直接打开/proc/$pid/mem,查找和阅读。以下是基于您的摘录的PoC:gist.github.com/WGH-/91260f6d65db88be2c847053c49be5ae这种方式不会停止,AFAIK不会因此而造成任何危险。
WGH

10

在交换期间,如果检测到使用中的交换插槽,则内核首先在页面中交换。然后,函数unuse_process()尝试查找与刚交换的页面相对应的所有页面表条目,并对页面表进行必要的更新。搜索是详尽且耗时的:它访问(整个系统的)每个内存描述符,并逐一检查其页表条目。

请参阅“了解Linux内核第3版”的第724页。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.