我正在寻找生成一个充满随机字节的文件(750 MB).我在一个单独的线程中使用的代码如下所示:
我分配了一个大小的缓冲区,因为在磁盘上写入会消耗更多时间:
function Generate(buf:Pointer):DWORD;stdcall;
var
i:DWORD;
begin
for i := 0 to keysize -1 do
PByte(DWORD(buf) + i)^ := Random(256);
Result:=0;
end;
Run Code Online (Sandbox Code Playgroud)
问题是该过程完成需要很长时间.有什么想法更快的方法?如果没有其他选择,我会尝试在汇编中实现它.
gab*_*abr 23
这听起来像是一个很好的练习题,所以我继续实施并行解决方案.它使用稍微超过3秒来生成750 MB文件,并在其工作期间使用超过90%的CPU.(SSD磁盘也有帮助.在RAID0磁盘对上生成文件需要3.5秒,在较慢的512 GB磁盘上生成文件需要4秒.)
所有重用的代码都可以使用OpenBSD许可证(几乎可以" 随意使用"):DSiWin32,GpStuff,GpRandomGen,Otl*.
uses
DSiWin32,
GpStuff,
GpRandomGen,
OtlCommon,
OtlCollections,
OtlParallel;
{$R *.dfm}
procedure FillBuffer(buf: pointer; bufSize: integer; randomGen: TGpRandom);
var
buf64: PInt64;
buf8 : PByte;
i : integer;
rnd : int64;
begin
buf64 := buf;
for i := 1 to bufSize div SizeOf(int64) do begin
buf64^ := randomGen.Rnd64;
Inc(buf64);
end;
rnd := randomGen.Rnd64;
buf8 := PByte(buf64);
for i := 1 to bufSize mod SizeOf(int64) do begin
buf8^ := rnd AND $FF;
rnd := rnd SHR 8;
Inc(buf8);
end;
end; { FillBuffer }
procedure CreateRandomFile(fileSize: integer; output: TStream);
const
CBlockSize = 1 * 1024 * 1024 {1 MB};
var
buffer : TOmniValue;
lastBufferSize: integer;
memStr : TMemoryStream;
numBuffers : integer;
outQueue : IOmniBlockingCollection;
begin
outQueue := TOmniBlockingCollection.Create;
numBuffers := (fileSize - 1) div CBlockSize + 1;
lastBufferSize := (fileSize - 1) mod CBlockSize + 1;
Parallel.ForEach(1, numBuffers).NoWait
.NumTasks(Environment.Process.Affinity.Count)
.OnStop(
procedure
begin
outQueue.CompleteAdding;
end)
.Initialize(
procedure(var taskState: TOmniValue)
begin
taskState := TGpRandom.Create;
end)
.Finalize(
procedure(const taskState: TOmniValue)
begin
taskState.AsObject.Free;
end)
.Execute(
procedure(const value: integer; var taskState: TOmniValue)
var
buffer : TMemoryStream;
bytesToWrite: integer;
begin
if value = numBuffers then
bytesToWrite := lastBufferSize
else
bytesToWrite := CBlockSize;
buffer := TMemoryStream.Create;
buffer.Size := bytesToWrite;
FillBuffer(buffer.Memory, bytesToWrite, taskState.AsObject as TGpRandom);
outQueue.Add(buffer);
end);
for buffer in outQueue do begin
memStr := buffer.AsObject as TMemoryStream;
output.CopyFrom(memStr, 0);
FreeAndNil(memStr);
end;
end;
procedure TForm43.btnRandomClick(Sender: TObject);
var
fileStr: TFileStream;
time : int64;
begin
time := DSiTimeGetTime64;
try
fileStr := TFileStream.Create('e:\0\random.dat', fmCreate);
try
CreateRandomFile(750*1024*1024, fileStr);
finally FreeAndNil(fileStr); end;
finally Caption := Format('Completed in %d ms', [DSiElapsedTime64(time)]); end;
end;
Run Code Online (Sandbox Code Playgroud)
编辑:在这种情况下使用ForEach并不是一个非常优雅的解决方案,因此我使用Parallel.ParallelTask和更好的IOmniCounter增强了OmniThreadLibrary.使用SVN中的版本993(或更新版本),您可以解决此多生产者 - 单一消费者问题,如下所示.
procedure CreateRandomFile(fileSize: integer; output: TStream);
const
CBlockSize = 1 * 1024 * 1024 {1 MB};
var
buffer : TOmniValue;
memStr : TMemoryStream;
outQueue : IOmniBlockingCollection;
unwritten: IOmniCounter;
begin
outQueue := TOmniBlockingCollection.Create;
unwritten := CreateCounter(fileSize);
Parallel.ParallelTask.NoWait
.NumTasks(Environment.Process.Affinity.Count)
.OnStop(Parallel.CompleteQueue(outQueue))
.Execute(
procedure
var
buffer : TMemoryStream;
bytesToWrite: integer;
randomGen : TGpRandom;
begin
randomGen := TGpRandom.Create;
try
while unwritten.Take(CBlockSize, bytesToWrite) do begin
buffer := TMemoryStream.Create;
buffer.Size := bytesToWrite;
FillBuffer(buffer.Memory, bytesToWrite, randomGen);
outQueue.Add(buffer);
end;
finally FreeAndNil(randomGen); end;
end
);
for buffer in outQueue do begin
memStr := buffer.AsObject as TMemoryStream;
output.CopyFrom(memStr, 0);
FreeAndNil(memStr);
end;
end;
Run Code Online (Sandbox Code Playgroud)
EDIT2:关于这个问题的更长篇博文:2.1之后的生活:并行数据生成(Parallel.Task简介)
我不知道德尔福,但它可能会浪费时间在Random(256)
电话上.你为什么不手工编码伪随机的东西
n = (n * 1103515245 + 12345) & 0xff;
Run Code Online (Sandbox Code Playgroud)
让我们n
从某个地方开始并使用递归(例如这个)来生成下一个n
.这不是真的是随机的,但它应该用于创建随机文件执行.
编辑
一些值得思考的东西.如果您创建此文件希望它不易被压缩,那么上面概述的方法并不是那么好,因为该& 0xff
部分.这样做更好
n = (n * 1103515245 + 12345) & 0x7fffffff;
Run Code Online (Sandbox Code Playgroud)
作为0x7fffffff = 2147483647
素数.并存储确切的较大值n
,并进行n % 256
分配.我已经有了一些很好的运行选择常量,并且更喜欢它作为内置.NET替代品的熵源,因为它的速度快了很多倍,而且你很少需要真正随机或更好的伪随机数.
归档时间: |
|
查看次数: |
1375 次 |
最近记录: |