Yup, the swapoff
mechanism is horrendously inefficient. The workaround is easy: iterate over processes, instead iterating over the swapped pages. Use this python script (I am not affiliated):
git clone https://github.com/wiedemannc/deswappify-auto
cd ./deswappify-auto
sudo python3 deswappify_auto.py -d -v info
Note that the daemon mode of operation is only for desktops/laptops that are often hibernated. I wouldn't run it as a daemon on a server system - just run it for a while, wait until it reports it took care of some processes then stop it and try:
swapoff /dev/x
Since most of the pages are now present both in swap and in memory, the swapoff
has very little to do and should be now blazingly fast (I saw hundreds of MB/s).
History section ahead
The aforementioned python script is based on the rest of this answer, which in turn was my improvement of this older answer authored by jlong. As the script is much much safer I recommend to only try the rest of my answer as the last line of defense:
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
This runs maybe 2 seconds and won't actually do anything, just list the top 10 memory segments (actually it prints more one-liners; yes I do love one-liners; just examine the commands, accept the risk, copy and paste into your shell; these will actually read from swap).
...Paste the generated one-liners...
swapoff /your/swap # much faster now
The main one-liner is safe (for me), except it reads a lot of /proc.
The sub-commands prepared for your manual examination are not safe. Each command will hang one process for the duration of reading a memory segment from swap. So it's unsafe with processes that don't tolerate any pauses. The transfer speeds I saw were on the order of 1 gigabyte per minute. (The aforementioned python script removed that deficiency).
Another danger is putting too much memory pressure on the system, so check with the usual free -m
What does it do?
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"
}
}
}
}
}
The output of this perl script is a series of gdb
commands dump memory (range)
which recall swapped pages to memory.
The output starts with the size, so it's easy enough to pass it trough | sort -Vr | head
to get top 10 largest segments by size (SSIZE). The -V
stands for version-number-suitable sorting, but it works for my purpose. I couldn't figure how to make numerical sort work.
iostat -d 5
show low IO on the disks duringswapoff
, too?