我正在用C++构建一个游戏引擎库.不久前,我使用Qt构建了一个应用程序,并且对使用Implicit Sharing非常着迷.我想知道是否有人可以更详细地解释这种技术,或者可以提供一个简单的例子.
我有什么方法可以避免修改副本的原位修改R?
我试图将较小的矩阵复制到一个较大的矩阵切片,如下所示.
library(data.table)
y <- matrix(c(11,21,31,12,22,32),nrow=3,ncol=2)
address(y)
[1] "08429190"
y[2:3,] <- matrix(c(1,1,8,12),nrow=2)
address(y)
[1] "0E033D28"
Run Code Online (Sandbox Code Playgroud) 我正在尝试在Python中的父进程和子进程之间共享对象.为了解决这个想法,我创建了一个简单的Python脚本:
from multiprocessing import Process
from os import getpid
import psutil
shared = list(range(20000000))
def shared_printer():
mem = psutil.Process(getpid()).memory_info().rss / (1024 ** 2)
print(getpid(), len(shared), '{}MB'.format(mem))
if __name__ == '__main__':
p = Process(target=shared_printer)
p.start()
shared_printer()
p.join()
Run Code Online (Sandbox Code Playgroud)
代码片段使用优秀的psutil库来打印RSS(驻留集大小).当我在OSX上用Python 2.7.15运行它时,我得到以下输出:
(33101, 20000000, '1MB')
(33100, 20000000, '626MB')
Run Code Online (Sandbox Code Playgroud)
当我在Ubuntu上运行完全相同的代码片段(Linux 4.15.0-1029-aws#30-Ubuntu SMP x86_64 GNU/Linux)时,我得到以下输出:
(4077, 20000000, '632MB')
(4078, 20000000, '629MB')
Run Code Online (Sandbox Code Playgroud)
请注意,子进程'RSS在OSX上是基本的0MB,与Linux中的父进程'RSS大小相同.我曾假设写入时复制行为在Linux中的工作方式相同,并允许子进程为大多数页面引用父进程的内存(可能除了存储对象头部的内存之外).
所以我猜测2系统中的写时复制行为存在一些差异.我的问题是:在Linux中我能做些什么来获得类似OSX的写时复制行为?
python linux copy-on-write shared-memory python-multiprocessing
我有以下功能:
func checkFiles(path string, excludedPatterns []string) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
我想知道,因为excludedPatterns永远不会改变,我应该通过使var全局(而不是每次都将它传递给函数)来优化它,或者Golang是否已经通过将它们作为copy-on-write传递来处理它?
编辑:我想我可以将切片作为指针传递,但我仍然想知道写时复制行为(如果它存在)以及一般来说我是否应该担心通过值或指针传递.
有没有办法从对象(数据框,矩阵,向量)中选择一个子集而无需复制所选数据?
我使用相当大的数据集,但从不更改它们.然而,为方便起见,我选择要操作的数据子集.每次制作一个大的子集的副本是非常低效的内存,但是正常的索引和subset(以及因此xapply()的函数族)都创建了所选数据的副本.所以我正在寻找可以解决这个问题的功能或数据结构.
一些可能符合我需求的方法,并希望在一些R包中实现:
xapply() 不创建子集的类似物.当我分叉我的进程时,如何防止GC激发写时复制?我最近一直在分析垃圾收集器在Ruby中的行为,因为我在程序中遇到了一些内存问题(即使对于相当小的任务,我的60核0.5Tb机器上的内存耗尽).对我来说,这确实限制了ruby在多核服务器上运行程序的实用性.我想在这里介绍我的实验和结果.
垃圾收集器在分叉期间运行时会出现问题.我调查了三个案例来说明这个问题.
情况1:我们使用数组在内存中分配了大量对象(字符串不超过20个字节).使用随机数和字符串格式创建字符串.当进程分叉并强制GC在子进程中运行时,所有共享内存都是私有的,导致初始内存重复.
情况2:我们使用数组在内存中分配了很多对象(字符串),但是使用rand.to_s函数创建了字符串,因此我们删除了与前一种情况相比的数据格式.我们最终使用的内存较少,可能是因为垃圾较少.当进程分叉并强制GC在子进程中运行时,只有部分内存变为私有.我们有重复的初始内存,但程度较小.
情况3:与之前相比,我们分配的对象更少,但对象更大,因此分配的内存量与之前的情况相同.当进程分叉并且我们强制GC在子进程中运行时,所有内存保持共享,即没有内存重复.
在这里,我粘贴用于这些实验的Ruby代码.要在不同情况之间切换,只需更改memory_object函数中的"option"值即可.在Ubuntu 14.04计算机上使用Ruby 2.2.2,2.2.1,2.1.3,2.1.5和1.9.3测试了代码.
案例1的示例输出:
ruby version 2.2.2
proces pid log priv_dirty shared_dirty
Parent 3897 post alloc 38 0
Parent 3897 4 fork 0 37
Child 3937 4 initial 0 37
Child 3937 8 empty GC 35 5
Run Code Online (Sandbox Code Playgroud)
完全相同的代码是用Python编写的,在所有情况下,CoW都可以正常工作.
案例1的示例输出:
python version 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2]
proces pid log priv_dirty shared_dirty
Parent 4308 post alloc 35 0
Parent 4308 4 fork 0 35
Child 4309 4 initial 0 35
Child 4309 10 …Run Code Online (Sandbox Code Playgroud) 我正在使用PHP编写Web API客户端,将CSV数据解析为关联数组,并且我希望在使用这些数组时保护我的用户免受数据复制.
我的用户永远不会写这些数组(理论上他们可以,但在实践中没有意义).
现在我的问题是......如果我的用户将这些数组作为方法的参数传递,那么PHP的写时复制机制是否会阻止数据复制,或者任何未明确接受对数组的引用的方法都会收到完整的副本阵列?
根据维基百科(可能是错的)
发出fork()系统调用时,会创建与父进程对应的所有页面的副本,由OS进行子进程加载到单独的内存位置.但在某些情况下不需要这样做.考虑一个子进行"exec"系统调用(用于执行C程序中的任何可执行文件)或fork()之后很快退出的情况.当需要子进程来执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换调用它的进程的地址空间.
在这种情况下,使用称为写时复制(COW)的技术.使用此技术,当发生fork时,不会为子进程复制父进程的页面.相反,页面在子进程和父进程之间共享.每当进程(父进程或子进程)修改页面时,就会对执行修改的进程(父进程或子进程)单独创建该特定页面的单独副本.然后,此过程将使用新复制的页面,而不是将来所有引用中的共享页面.另一个进程(未修改共享页面的进程)继续使用页面的原始副本(现在不再共享).这种技术称为写时复制,因为当某个进程写入页面时会复制该页面.
似乎当任一进程尝试写入页面时.将分配新页面副本并将其分配给生成页面错误的进程.之后,原始页面被标记为可写.
我的问题是:如果在任何进程尝试写入共享页面之前多次调用fork,会发生什么?
我刚刚阅读了一篇关于写时复制的维基百科文章(如果有任何支持它的文件系统很好奇),并对以下段落感到惊讶:
COW也在内核之外,库,应用程序和系统代码中使用.例如,C++标准库提供的字符串类是专门为允许写时复制实现而设计的:
std::string x("Hello");
std::string y = x; // x and y use the same buffer
y += ", World!"; // now y uses a different buffer
// x still uses the same old buffer
Run Code Online (Sandbox Code Playgroud)
我不知道STL中是否支持copy-on-write.真的吗?它是否适用于其他STL类,例如std::vector或std::array?哪些编译器支持该优化(特别是,我想知道G ++,英特尔C++编译器和Microsoft C++编译器)?
所以我有一个简单的cow_ptr.它看起来像这样:
template<class T, class Base=std::shared_ptr<T const>>
struct cow_ptr:private Base{
using Base::operator*;
using Base::operator->;
using Base::operator bool;
// etc
cow_ptr(std::shared_ptr<T> ptr):Base(ptr){}
// defaulted special member functions
template<class F>
decltype(auto) write(F&& f){
if (!unique()) self_clone();
Assert(unique());
return std::forward<F>(f)(const_cast<T&>(**this));
}
private:
void self_clone(){
if (!*this) return;
*this = std::make_shared<T>(**this);
Assert(unique());
}
};
Run Code Online (Sandbox Code Playgroud)
这可以保证它拥有一个非常量T并确保它在unique何时出现.write([&](T&){}).
在C++ 17的折旧.unique()似乎表明这种设计是有缺陷的.
我猜测如果我们从线程A中的cow_ptr<int> ptrwith 开始1,将它传递给线程B,使其唯一,修改它2,传ptr回它并在线程中读取它A我们已经生成了竞争条件.
我该如何解决?我可以简单地添加内存屏障write吗?哪一个?或者问题更根本? …
copy-on-write ×10
c++ ×3
fork ×2
linux ×2
r ×2
apply ×1
arrays ×1
c++17 ×1
data.table ×1
duplicates ×1
go ×1
immutability ×1
matrix ×1
memory ×1
php ×1
python ×1
qt ×1
ruby ×1
stl ×1
subset ×1