如何增加Ubuntu的虚拟内存和/或为Matlab设置交换空间?

情况:在Ubuntu的Matlab中修复“内存不足”错误 目标:在外部硬盘/固态硬盘上分配一些虚拟内存和/或交换空间;读写速度从20 GBps降低到0.1 GBps,这是可以接受的! 术语:交换空间和虚拟内存 here “不,交换空间和虚拟内存是完全不同的。例如,内存映射一个1GB的文件会使用额外的1GB虚拟内存,但对交换空间的使用没有任何改变。交换空间是一种后备存储。许多虚拟内存的用途与后备存储无关。(还有一些系统具有虚拟内存但没有交换空间,以及具有交换空间但没有虚拟内存的系统。)”
消费级硬件有限,所以我需要在我的外部硬盘上使用更多的虚拟内存和/或交换空间。Matlab提到了交换空间(TODO排除虚拟内存?)
“Linux系统 - 使用mkswap和swapon命令来更改交换空间。”
系统特点
你可以通过执行swapon -s命令来查看你有多少交换空间。
Matlab的配置如下:
% https://stackoverflow.com/a/35971040/54964 com.mathworks.services.Prefs.setIntegerPref('JavaMemHeapMax', 2048); % MB
% TODO 找不到如何让Matlab使用/dev/sda3
所以你可以看到我的Matlab没有使用它。在Matlab中,当处理大矩阵时,我会遇到内存不足的错误。我尝试将矩阵重塑为向量并编写并行代码,但都没有成功。
因此,我想使用虚拟内存,因为我需要完成工作,速度并不重要。
伪代码
1. 创建一个交换空间的Shell脚本,启动MATLAB,并在MATLAB退出时删除交换空间。(MichaelHooreman
2. 在外部硬盘上启用交换空间。如何在这里使用sudo swapon -a命令?
3. 启动Matlab。
4. 让Matlab使用交换空间。
5. 在Matlab退出时删除交换空间。

Micheal的脚本中的临时交换空间、运行客户端和关闭/删除交换空间的情况

情况: 无法控制设置环境(1)、运行Matlab(2)和关闭环境(3)中的错误
脚本
#!/usr/bin/env bash

# https://stackoverflow.com/a/69808/54964
set -e 
# TODO How to do swapoff if any error?

SWAP_FILE="/media/masi/SamiSwapVirtual/.swap_file_20.7.2016"
SIZE_MB=16000
TO_RUN="matlab"

dd if="/dev/zero" of=${SWAP_FILE} bs="1M" count=${SIZE_MB} status="progress"
mkswap ${SWAP_FILE}
chmod 0600 ${SWAP_FILE}
sudo chown 0.0 ${SWAP_FILE} # https://unix.stackexchange.com/a/297153/16920
sudo swapon -v ${SWAP_FILE}
echo "Swap enabled. Press enter to continue"; read
${TO_RUN}
echo "I will remove the swap. Press enter to continue"; read
sudo swapoff -v ${SWAP_FILE}
rm -vf ${SWAP_FILE}

迭代1使用Transcend 25M3 1 TB,之前很少使用过文件系统为ext4。
  1. 运行脚本后的日志

    sh start_matlab_with_swap.sh 
    16000+0 个记录已写入
    16000+0 个记录已读取
    16777216000 字节(17 GB,16 GiB)已复制,134.489 秒,125 MB/s
    设置交换空间版本 1,大小为 15.6 GiB(16777211904 字节)
    无标签,UUID=48c2835b-4499-4534-aa49-0648e15bd5d9
    masi 的密码: 
    swapon /media/masi/SamiWeek/tmp/swap_file_18.7.2016
    swapon: /media/masi/SamiWeek/tmp/swap_file_18.7.2016: 不安全的文件所有者 1000,建议为 0(root)。
    swapon: /media/masi/SamiWeek/tmp/swap_file_18.7.2016: 找到交换机标记:版本 1d,页面大小 4,相同的字节顺序
    swapon: /media/masi/SamiWeek/tmp/swap_file_18.7.2016: 页面大小=4096,交换大小=16777216000,设备大小=16777216000
    交换已启用。按回车键继续
    start_matlab_with_swap.sh: 11: read: arg count
    
  2. 运行客户端

    • 在交换内存中首次启动 Matlab 时,命令历史记录消失(工单 #02075943),并出现错误 There was a problem reading your command history - -。只需重新启动 Matlab,如果使用默认设置,则问题将解决。命令 prefdir 返回 /home/masi/.matlab/R2016a,这是默认位置(/home/{username}/.matlab/R2016a)。重新启动后,文件 /home/masi/.matlab/R2016a/matlab.prf 存在,点击此处

    • ... [其他错误] ...

  3. 关闭 Matlab 并在终端中再次输入密码

    [sudo] password for masi: 
    swapoff /media/masi/SamiWeek/tmp/swap_file_18.7.2016
    [ 这里出现错误! ]
    
打开:如何应用更好的错误处理?在源代码中查看我使用的脚本示例。线程如何进行错误捕获并在出现错误/警告时执行Swapoff操作?

永久交换 = 将交换设置与运行客户端分开

设置交换

# https://unix.stackexchange.com/q/297767/16920
masi@masi:~$ sudo fallocate -l 20G /mnt/.swapfile

masi@masi:~$ sudo mkswap /mnt/.swapfile 
Setting up swapspace version 1, size = 20 GiB (21474832384 bytes)
no label, UUID=45df9e48-1760-47e8-84d7-7a14f56bbd72

masi@masi:~$ sudo swapon /mnt/.swapfile
swapon: /mnt/.swapfile: insecure permissions 0644, 0600 suggested.

masi@masi:~$ sudo chmod 600 /mnt/.swapfile

masi@masi:~$ free -m
              total        used        free      shared  buff/cache   available
Mem:           7925        1494         175         196        6255        5892
Swap:         28610           0       28610

把以下内容放在/etc/fstab的末尾,以实现永久更改。
# https://unix.stackexchange.com/a/298212/16920
# https://unix.stackexchange.com/a/298543/16920

# If swap is on SSD, trim blocks each time at startup.
#/mnt/.swapfile  none    swap    defaults,discard      0        0

# If swap on External HDD, just use sw.
/media/masi/SamiWeek/.swapfile  none    swap    sw      0        0

系统:Linux Ubuntu 16.04 64位 Linux内核:4.6 Linux内核选项:wl Matlab:2016a 官方Matlab文档:解决“内存不足”错误 外置硬盘:Transcend 1 TB StoreJet 25M3 评测,Transcend 2 TB StoreJet 25M3 外置硬盘文件系统:ext4 外置硬盘缓存:8 MB 相关主题:如何在Ubuntu中增加MATLAB内存限制?(如何使用mkswap、swapon来为MATLAB提供内存?)如何减少Matlab中的物理内存增加?如何解决Matlab的内存不足错误?如何解决Matlab中10800x10800矩阵的内存不足错误?如何在Matlab r2012b中增加内存限制(连续和总体)?如何增加Matlab 2009b中的数组块并解决内存不足错误?如何解决Matlab中一个小变量的内存不足问题?Matlab中的“内存不足”。一种缓慢但永久的解决方案?

4不好意思,这并没有回答问题。但你确定要这样做吗?从交换文件进行计算需要非常长的时间。内存的读取速度至少为每秒20 GB,而你的外部硬盘的读取速度不到每秒0.1 GB。 - Anake
1@Anake 是的,我知道。这不是个问题。我有很大的矩阵需要使用32/64 GB 的计算,但我的当前8 GB 超薄本无法完成。我仍然需要在假期进行计算。 - Léo Léopold Hertz 준영
1非常抱歉,又是一条无用的评论。你可以在家里或学校将电脑保持开机状态,这样你就可以通过SSH远程连接,并在家中或学校运行它们了。 - Anake
@Anake 很抱歉,目前无法实现。此外,由于需要本地计算,所以需要使用Matlab。同时,目前也没有足够的密钥来完成这个任务。 - Léo Léopold Hertz 준영
在这个背景下,MATLAB有什么特别之处吗?这不只是一个关于如何增加交换空间的重复问题吗? - steeldriver
不行。看伪代码。 - Léo Léopold Hertz 준영
5个回答

你不能为软件专门分配交换空间。你可以创建一个shell脚本,在MATLAB启动时创建交换空间,并在MATLAB退出时删除交换空间。
以下是一个示例脚本,它在/tmp目录中创建了一个大小为10MB的交换空间,挂载该空间,启动R(我没有MATLAB),等待R退出,卸载交换文件并删除它。
请注意: - 由于系统会将该交换文件用于任何软件,可能不是由你运行的,所以你会收到一个警告。你需要自己解决这个问题。 - 如果你按下[Ctrl]-[C]终止脚本,或者注销登录等,交换空间将保持挂载状态。你也需要自己解决这个问题。
#!/usr/bin/env bash

SWAP_FILE=/tmp/my_swap_file
SIZE_MB=10
TO_RUN="R"

dd if=/dev/zero of=${SWAP_FILE} bs=1M count=${SIZE_MB}
mkswap ${SWAP_FILE}
chmod 0600 ${SWAP_FILE}
sudo swapon -v ${SWAP_FILE}
echo "Swap enabled. Press enter to continue"; read
${TO_RUN}
echo "I will remove the swap. Press enter to continue"; read
sudo swapoff -v ${SWAP_FILE}
rm -vf ${SWAP_FILE}

1嗯,它并不是专门为MATLAB设计的,而是适用于任何需要临时增加内存的情况。请注意,swapon/swapoff需要root权限(因此:sudo)。 - Michael Hooreman
1是的,你有。这就是如何成为根用户的方法。 - Michael Hooreman
好答案。我个人会这样处理,并为那些令人愉快的时刻保留我的自己的add-swap.sh脚本,当内存被占用殆尽时使用。 - Sergiy Kolodyazhnyy
我在这里颁发了悬赏,因为它让我正确地思考。然而,我无法接受这个答案,因为错误管理非常困难且不完整。最好将交换设置与运行Matlab分开,请参阅此处的答案:http://unix.stackexchange.com/a/298543/16920 - Léo Léopold Hertz 준영

如果你的硬盘/固态硬盘上有交换分区,请使用zswap。 然而,模块zram没有硬盘/固态硬盘上的交换分区,所以Hakala的回答不适用。 请参考zram vs zswap vs zcache Ultimate guide: when to use which one了解更多信息。 按照How to Activate Zswap Successfully for Matlab Computation in Ubuntu 16.04?中的说明设置zswap
  • /etc/default/grub文件中,将相应的行替换为以下行:

    # https://wiki.archlinux.org/index.php/Zswap
    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash zswap.enabled=1 zswap.max_pool_percent=25 zswap.compressor=lzo"
    
  • 运行sudo update-grub命令。


好的,你有一个相当长的列表。让我逐行回答。
1. 如何在这里应用更好的错误处理和错误捕获?请参考我在源代码中提供的示例脚本。查看主题“如何进行错误捕获并在出现错误/警告时执行Swapoff”。
我根本不喜欢这个脚本的概念。你试图将外部硬盘用作交换空间,这只是一个糟糕的想法。如果你真的打算经常这样做,那么请调整分区大小以放置一个合适的交换分区,添加一个交换文件,或者直接购买一个更大的内部硬盘。
2. 如果矩阵大小超过了交换空间的大小,如何发出警告?
只需进行一些计算。如果你在程序开始之前就知道矩阵的大小,那么将其转换为MiB并与可用的交换空间进行比较即可。
3. 如何在Matlab中计算巨大矩阵时显示进度条?
Matlab有一个API对吧?我不认为这是正确的论坛来提问这个问题。即使你有一个API,由于IO通过交换空间进行,所以进度条只会是不连续的,并不能真实反映进度。
如何在迭代(2)中终止繁忙进程和/或swapon -s/swapoff?
别这么做。只是因为你的计算完成了,不意味着操作系统也完成了你分配的资源。当它完成写入交换空间后,它会释放资源。你已经消耗了很多内存,导致许多应用程序无法获得所需的内存,因此它们也在使用交换空间。只需将其保留并让操作系统自行处理即可。在执行下一次运行之前,请清除缓存。
echo 3 > /proc/sys/vm/drop_caches 

这个问题可能比我想的要复杂,我不是一个Linux虚拟机专家。值得研究一下SLAB/SLUB分配器的工作原理以及如何调整它以满足你的大内存需求。你可能可以将Matlab锁定在内存中,这会强制操作系统为你保留内存,否则它就无法启动,完成后还需要解锁。我可以通过C API很好地实现这一点,但我不确定在无法重新编译的进程之外该如何做到这一点,这需要一些研究。

最后,EC2正是为这种情况而生的。看起来你需要16G内存,m4.4xlarge配置有64G内存,每小时费用为0.958美元。这比一杯咖啡还便宜。使用juju charm或类似工具脚本化你的Matlab安装,并将整个过程转化为计算服务。

16G是指16GB吗?

  • 是的,通常当我们省略后缀时,表示的是以字节为单位的基于2的幂次方的数字。如果你想更简洁,可以写成16GiB。

“我需要超过100GB的矩阵。我不知道你是否能在EC2上实现。”

你是否应该通过echo 3 > /proc/sys/vm/drop_caches清除缓存?

  • 是的,这样做不会有任何坏处。请参阅Linux内核中的Documentation/sysctl/vm.txt。

如何将Matlab锁定到内存中?

  • man mlock。虽然我在引用时搞错了。这个调用确保您可以分配所需的所有内存,并防止其被交换出去,它永远不会使用虚拟内存。这不是您想要的。

我认为您可以将C API绑定到Matlab。- - 如果进程发生故障,您有没有关闭交换空间的任何想法?

我要坦率地说,你提出的按照你的方式微观管理交换文件的概念是荒谬的。操作系统的任务是管理资源并以公平一致的方式分配它们。一旦你给它更多的资源,它会根据自己的判断使用它们。你不能告诉它何时完成并从它下面抽取资源,操作系统会告诉你何时完成。
当我向操作系统请求内存地址空间时,有时并不总是成功,这并不意味着我不能再试一次。Matlab无法想到调用malloc两次是Matlab的问题。
所以,要实现你想要的改变,如果那100G的空间真的非常宝贵,那么你需要找出如何告诉操作系统减少其内存占用(首先清除缓存),这样内存管理器就不会感觉需要使用额外的交换空间。只有在那之后,你才能要求内存管理器释放交换文件。
种植记忆和硬盘等东西很容易,但缩小它们却难得多。缩小需要重新平衡每个在该空间上分配资源的用户。如果我说“我有一台100TB的存储阵列,但现在只需要60TB,为什么当我移除40TB的磁盘时,阵列停止工作呢?”嗯,答案显而易见,对吧?
所以,这是我看到的你的选择。
1. 调查Matlab C API,看看是否能更好地控制为这些大规模工作集分配内存。 2. 重构你的计算,使用子矩阵或其他稀疏数据表示来计算你现在拥有的内容。 3. 使用众多的线性代数库编写自己的C/C++程序来执行计算,并使用malloc或mmap anonymous来分配你需要的地址空间。

是的,但可能不够,你可能需要等待并重试,以确保成功,如果有可能的话。 - ppetraki
1哈,你的评论剩下的部分在我的智能手机上没有显示出来。你不能像那样使用sudo echo命令,而是应该这样做:echo 3 | sudo tee /proc/sys/vm/drop_caches - ppetraki
我接受这个答案,因为它在回答中表达了正确的意思。在同一个脚本中运行客户端并设置环境是非常容易出错的。关于这个问题的更多讨论请参考这里:http://unix.stackexchange.com/a/298543/16920。如果你能稍微整理一下你的内容就太好了。 - Léo Léopold Hertz 준영

我至少会测试一下压缩RAM的性能(zram内核模块,自内核版本3.14起可用)。
按照archlinux wiki指南进行操作。
modprobe zram
echo lz4 > /sys/block/zram0/comp_algorithm
echo 4G > /sys/block/zram0/disksize
mkswap --label zram0 /dev/zram0
swapon --priority 100 /dev/zram0

我的猜测是,压缩的RAM应该比磁盘I/O更快。
为了在重启后保留更改,请将启动时的命令放置在/etc/rc.local中,并运行sudo systemctl enable rc-local.service。

我对http://unix.stackexchange.com/q/297752/16920的帖子进行了回答,讨论了zram对内存大小和分页速率的影响。我发现对内存大小没有任何影响,可能主要是对分页有影响。然而,使用runit后,systemd/...警告增加了。了解zram的错误率将会很有帮助。 - Léo Léopold Hertz 준영
在zram0的情况下,可以在*/sys/block/zram0的文件orig_data_sizecompr_data_size*中获得一些统计数据。我在那里没有太多的交换数据,但对于已有的数据来说,比例大约为0.35。根据这个比例,11 GB的数据可能适应4 GB的RAM。这可能意味着有7 GB的数据没有被交换到磁盘上。 - J.J. Hakala
1你可以将启动时的命令放在 /etc/rc.local 文件中,然后运行 sudo systemctl enable rc-local.service 命令。 - J.J. Hakala
我发现对于HDD/SSD来说zram不适用于交换空间,所以这里不适合使用zram,请参考 http://askubuntu.com/a/472227/25388 线程。 - Léo Léopold Hertz 준영

这是如何使用外部硬盘扩展您的SWAP内存的方法:
首先,运行以下命令来记录你实际的SWAP内存: ``` free -m ```
其次,准备好你的硬盘驱动器文件夹。它应该是类似于`/media/myhdd`这样的路径。
决定要添加的额外SWAP的大小。假设为X GB。
计算该数量对应的字节数。使用GB,公式为:Y=X*1024^3,其中Y是计算结果。
选择文件的块大小(以字节为单位)。在这里我们使用默认值:4096(关于此更多信息请参考here)。
计算文件将具有的块数:Z=Y/4096
在终端中使用以下命令创建一个大小为X GB的文件: ``` dd if=/dev/zero of=/media/myhdd/swapfile bs=4096 count=Z ```
使用以下命令在文件中创建SWAP: ``` sudo mkswap /media/myhdd/swapfile -f ```
最后,启用SWAP: ``` sudo swapon -p 1000 /media/myhdd/swapfile ```

现在您的 SWAP 已经增加。请再次使用 free -m 命令进行检查。


我们可以在一个非交互式脚本中进行设置(需要sudo权限):
#!/bin/bash

### Inputs ###

swap_GB=$(expr 1) # Enter here size of the swap memory to create, in GB.
swap_bs=$(expr 4096) # Enter here block size, in bytes (must be a multiple of 8).
HDD_folder="/media/myhdd/" # Enter absolute path of HDD inside the brackets.

### Swap creation ###

swap_size=$(expr $swap_GB \* 1024 \* 1024 \* 1024 / $swap_bs)
dd if=/dev/zero of=${HDD_folder}"/swapfile" bs=$swap_bs count=$swap_size
mkswap ${HDD_folder}"/swapfile" -f
swapon -p 1000 ${HDD_folder}"/swapfile"

### EOF ###

PS:如果可能的话,请进行优化/更正。就像我说的,这是我第一次写脚本 :)

嗯,交换分区已经很慢了,但是我无法想象将交换分区放在USB驱动器上。以我个人看来,它会变得像地狱一样慢。 - Michael Hooreman
这里提供一个脚本作为摘要会很好。 - Léo Léopold Hertz 준영
1我从来没有写过剧本,但会试一下。 - user308164