临界、多重读独占写多线程同步测试

多重读独占写同步对象适用于读多写少的特定场合,恢弘今天提供了一个第三方的源码(仿照Firebird数据库的源码中的编写的),我写了一个简单的测试程序对其性能试图做个对比。

首先,我们请出参与对比的四位选手:

1、本次的种子选手,FireBird拷贝版多重读独占写对象;

2、Windows操作系统提供的多重读独占写对象;

3、Sysutils单元提供的多重读独占写对象;

4、永久亮丽的临界对象;

现在,说明比赛规则:

1、以8:2的比例来执行相同的代码,看总时间来确定读写的效率。

2、读的过程中,会验证每一个字节内容是否正确,以确定锁本身是否可靠。

首先请出读函数:

procedure TForm5.ReadTest;
var
  I: Integer;
  C: Byte;
begin
AtomicIncrement(FReadTimes);
C := FBuffer[0];
for I := 1 to Length(FBuffer) - 1 do
  begin
  assert(FBuffer[I] = C, 'BufferError');
  end;
end;

然后请出写函数:

procedure TForm5.WriteTest;
var
  I: Integer;
  C: Byte;
begin
C := Random(255);
AtomicIncrement(FWriteTimes);
for I := 0 to Length(FBuffer) - 1 do
  FBuffer[I] := C;
end;

测试代码:

procedure TForm5.TestCS(ALoopMgr: TQForJobs; AJob: PQJob; AIndex: NativeInt);
begin
if (AIndex mod 10) < FReadRatio then
  begin
  FCS.Enter;
  try
    ReadTest;
  finally
    FCS.Leave;
  end;
  end
else
  begin
  FCS.Enter;
  try
    WriteTest;
  finally
    FCS.Leave;
  end;
  end;
end;

procedure TForm5.TestMRSW(ALoopMgr: TQForJobs; AJob: PQJob; AIndex: NativeInt);
begin
if (AIndex mod 10) < FReadRatio then
  begin
  FMRSW.AcquireReadLock;
  try
    ReadTest;
  finally
    FMRSW.ReleaseReadLock;
  end;
  end
else
  begin
  FMRSW.AcquireWriteLock;
  try
    WriteTest;
  finally
    FMRSW.ReleaseWriteLock;
  end;
  end;
end;

procedure TForm5.TestOS(ALoopMgr: TQForJobs; AJob: PQJob; AIndex: NativeInt);
begin
if (AIndex mod 10) < FReadRatio then
  begin
  AcquireSRWLockShared(FOS);
  try
    ReadTest;
  finally
    ReleaseSRWLockShared(FOS);
  end;
  end
else
  begin
  AcquireSRWLockExclusive(FOS);
  try
    WriteTest;
  finally
    ReleaseSRWLockExclusive(FOS);
  end;
  end;

end;

procedure TForm5.TestSysutils(ALoopMgr: TQForJobs; AJob: PQJob;
  AIndex: NativeInt);
begin
if (AIndex mod 10) < FReadRatio then
  begin
  FRWSync.BeginRead;
  try
    ReadTest;
  finally
    FRWSync.EndRead;
  end;
  end
else
  begin
  FRWSync.BeginWrite;
  try
    WriteTest;
  finally
    FRWSync.EndWrite;
  end;
  end;
end;

测试计时:

procedure TForm5.WinMRSWClick(Sender: TObject);
var
  T: Cardinal;
begin
SetLength(FBuffer, 2048);
FReadTimes := 0;
FWriteTimes := 0;
FReadRatio := SpinEdit1.Value;
T := GetTimeStamp;
TQForJobs.For(0, StrToInt(Edit1.Text) - 1, TestOS);
T := GetTimeStamp - T;
Label1.Caption := 'API: Read ' + IntToStr(FReadTimes) + ' times ,Write ' +
  IntToStr(FWriteTimes) + ' times,Used Time ' + FloatToStr(T / 10) + ' ms';
Memo1.Lines.Add(Label1.Caption);
end;

其它三个的测试代码只更改了For的回调函数和Label1.Caption的前缀。

现在呈现亮丽的对比结果:

mrsw

好了,我们以临界做为基准,分别计算下百分比:

本次种子选手:(3250-2554.3)*100/3250=21.4%

Windows API:(3250-3055.2)*100/3250=5.99%

Sysutils自带:(3250-2597.5)*100/3250=20.08%

看起来不错,总是有所提升。但是,俺在这里要提供各位一点,馅饼虽好,但要会吃。使用这种锁定你一定要保证:请求读的情况下,就是纯读取。你要确保你的代码及代码中调用的函数不会引起内部的写操作改变内容,因为读是可以并行的,如果一旦引起相关操作,后果是严重的,相当于没有同步写内容。如果你保证不了这一点,还是老实的使用临界吧,毕竟读写都保护了,无侧漏,更安全。

我们再看一下读写比为5:5的测试结果:

mrsw5_5

此时种子选手依然强势,但领先优势已经不大了和Window API几乎一致,而自带的Sysutils的同步对象,已经被远远落下了。

下面是反过来读写比2:8的测试结果,意外的是Windows API成了冠军,其它的都已经被临界超过了。

mrsw28

【友情提示】

本人只对本机器上本测试方案的测试结果负责,不对你和他机器上的测试用例的结果负责。结果仅供参考。

【个人推荐】

除非你能保证读写是完全正确的,否则临界要比其它的更安全一些。安全第一,不是吗?

【下载】

测试程序(需要Vista+) 测试源码(基于XE7)

 

分享到: