IVY的问题,每次page fault都需要传一个完整的page,4KB,性能太差。

优化的策略:

  1. 每次传一个delta,没有必要传整个Page。

  2. 如果是写的address不同,但是在同一个page上,会频繁导致owner的转移。因为大量的write pagefault。如下面的例子。

1
2
3
4
5
6
7
8
9
10
11
12
void thread0(){
for{
x++;
}
}
void thread1(){
for{
y++;
}
}

// x, y 在同一个page上

解决方案:
使用lock/unlock。

1
2
3
4
5
lock(l_x)
write(x)
write(x)
...
unlock(l_x) // synchronization

Release Consistency:
如果对共享变量的操作,都有lock/unlock的操作,一定能保证结果和Sequential consistency一样。

ps:wiki上的定义可能更好理解一点:
所有的内存读写操作都有两个额外的同步操作:acquire和release,acquire和release构成一个临界域。(相当于lock和unlock)如果说,一个写操作,能被在它后面acquire的读操作看到,就说明这个系统满足release consistency。

改进:
还是上面那个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void thread0(){
for{
lock(x)
x++;
unlock(x)
}
}
void thread1(){
for{
lock(y)
y++;
unlock(y)
}
}

每次都要做一次同步。
所以一种改进方式就是用lazy的模式;等其他人试图去loc的时候做一个同步,unlock的时候没有必要同步。

这种方式称为lazy release consistency;
之前那种方式eager release consistency;

一个综合的例子:

1
2
3
4
5
6
7
x = y = 0

p0: acquire(x), x=1, release(x)

p1: acquire(x), acquire(y), y = x, release(y), release(x)

p2: acquire(x), print x, y, release(x)

假设p0,p1,p2依次执行。

IVY: 每次都有pagefault,所以数据总是最新的。
ERC: 结果为1,1
LRC: 结果为0,1

这样的LRC会有个问题,理论上y依赖于x,而在p2看到的是x=0,y=1,这样不可理解。因果关系没有得到维护。
所以在LRC完整定义上会维护一个因果关系图。即使没有显示地调用lock,也应该做同步。

vector clock

三个线程,每个vector维度为3,代表自己看到的每个线程的最大的时间值。

Treed Masks