delphi 快速读取内存字符串

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了delphi 快速读取内存字符串相关的知识,希望对你有一定的参考价值。

delphi 搜索内存中字符串、比如:http://www.baidu.com 类似这样的网站地址、在0x10000000 到 0x50000000 这块地址中搜索、但发现循环读取的时候、速度惊人(慢)

var
I : Integer;
ScanStarAdd,ScanEndAdd ,ss: Dword;
Temp : array[0..15] of char;
for I :=ScanStarAdd to ScanEndAdd - 1 do
begin
ReadProcessMemory( ProcessHwnd, pointer(ScanStarAdd ),@Temp,16,SS) ;
if checkUrl( Temp ) then
begin
/////保存当前网站地址
end;
ScanStarAdd := ScanStarAdd + $1;
application..ProcessMessages;
end;

貌似这样读取非常的不行、
看到CE那样的工具挺快的、就是多线程 学的有点迷糊

参考技术A Temp : array[0..15] of char;
你这缓冲太小了,使用GetMem获取几M的内存然后去搜索会快一些
checkUrl的算法要优化
ScanStarAdd := ScanStarAdd + $1;不能这样向后推移,必须一次一次推移缓冲区大小-URL长度的字节数
参考技术B 可以一次多读一些, 然后再在内存中找, 应该快很多! 缺点是比较占内存

如何从 .NET 中的内存映射文件中快速读取字节?

【中文标题】如何从 .NET 中的内存映射文件中快速读取字节?【英文标题】:How can I quickly read bytes from a memory mapped file in .NET? 【发布时间】:2011-10-31 15:55:14 【问题描述】:

在某些情况下,MemoryMappedViewAccessor 类并不能有效地读取字节;我们得到的最好的是通用的ReadArray<byte>,它是所有结构的路由,当您只需要字节时涉及几个不必要的步骤。

可以使用MemoryMappedViewStream,但是因为它是基于Stream,所以你需要先寻找到正确的位置,然后读取操作本身有很多不必要的步骤。

是否有一种快速、高性能的方法可以从 .NET 中的内存映射文件中读取字节数组,前提是它应该只是要读取的地址空间的特定区域?

【问题讨论】:

【参考方案1】:

这个解决方案需要不安全的代码(使用/unsafe开关编译),但是直接抓取了一个指向内存的指针;然后可以使用Marshal.Copy。这比 .NET 框架提供的方法快得多。

    // assumes part of a class where _view is a MemoryMappedViewAccessor object

    public unsafe byte[] ReadBytes(int offset, int num)
    
        byte[] arr = new byte[num];
        byte *ptr = (byte*)0;
        this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
        Marshal.Copy(IntPtr.Add(new IntPtr(ptr), offset), arr, 0, num);
        this._view.SafeMemoryMappedViewHandle.ReleasePointer();
        return arr;
    

    public unsafe void WriteBytes(int offset, byte[] data)
    
        byte* ptr = (byte*)0;
        this._view.SafeMemoryMappedViewHandle.AcquirePointer(ref ptr);
        Marshal.Copy(data, 0, IntPtr.Add(new IntPtr(ptr), offset), data.Length);
        this._view.SafeMemoryMappedViewHandle.ReleasePointer();
    

【讨论】:

您应该使用关键执行块和 try-finally 以确保即使 Marshal.Copy 抛出异常也能运行 ReleasePointer。 好答案 =) 事实上,分析显示托管包装器比使用不安全指针访问映射内存慢 30 倍。 @MattHowells 我同意。我读到 CER 可能会影响性能,但它似乎可以忽略不计(至少在受控测试中)。无论性能影响如何,它都是此处“备注”中所述的正确使用模式; msdn.microsoft.com/en-us/library/…【参考方案2】:

查看此错误报告:No way to determine internal offset used by MemoryMappedViewAccessor - Makes SafeMemoryMappedViewHandle property unusable.

来自报告:

MemoryMappedViewAccessor 有一个 SafeMemoryMappedViewHandle 属性,该属性返回 MemoryMappedView 内部使用的 ViewHandle,但没有任何属性返回 MemoryMappedView 使用的偏移量。

由于 MemoryMappedView 正在页面对齐 MemoryMappedFile.CreateViewAccessor(offset,size) 中请求的偏移量,因此在不知道偏移量的情况下不可能将 SafeMemoryMappedViewHandle 用于任何有用的事情。

请注意,我们真正想要做的是使用 AcquirePointer(ref byte* pointer) 方法来允许一些基于快速指针的(可能是非托管的)代码运行。我们可以将指针进行页面对齐,但必须可以找出与最初请求地址的偏移量是多少。

【讨论】:

看起来很傻。如果您控制视图,则不需要 .NET 告诉您偏移量,因为您指定了它。 (这就是我所做的:_view 是偏移量 0 处的访问器) Fwiw,这段代码也已经在多台机器上进行了压力测试 [数十亿次调用,数千种不同的 MMF] 现在有数千亿的调用,以及数十万的 MMF。我的代码不会发生这个错误;) 现在似乎已修复 - 您可以访问 MemoryMappedViewAccessor 的 PointerOffset 属性来计算出与请求的偏移量相对应的正确指针地址。只需将 PointerOffset 添加到 SafeMemoryMappedViewHandle.AcquirePointer() 返回的地址【参考方案3】:

我知道这是一个已回答的老问题,但我想加两分钱。

我使用接受的答案(使用不安全代码)和 MemoryMappedViewStream 方法进行了测试,以读取 200MB 字节数组。

MemoryMappedViewStream

        const int MMF_MAX_SIZE = 209_715_200;
        var buffer = new byte[ MMF_VIEW_SIZE ];

        using( var mmf = MemoryMappedFile.OpenExisting( "mmf1" ) )
        using( var view = mmf.CreateViewStream( 0, buffer.Length, MemoryMappedFileAccess.ReadWrite ) )  
        
            if( view.CanRead )
            
                Console.WriteLine( "Begin read" );
                sw.Start( );
                view.Read( buffer, 0, MMF_MAX_SIZE );
                sw.Stop( );
                Console.WriteLine( $"Read done - sw.ElapsedMillisecondsms" );
            
        

我对每种方法进行了 3 次测试,并收到了以下次数。

MemoryMappedViewStream:

    483ms 501 毫秒 490 毫秒

不安全的方法

    531ms 517ms 523ms

从少量测试来看,MemoryMappedViewStream 似乎具有非常轻微的优势。考虑到这一点,我会选择MemoryMappedViewStream

【讨论】:

这是有道理的,文档说使用视图流进行顺序访问,它已针对它进行了优化,而视图访问器旨在用于随机访问。【参考方案4】:

此解决方案的安全版本是:

var file = MemoryMappedFile.CreateFromFile(...);
var accessor = file.CreateViewAccessor();
var bytes = new byte[yourLength];

// assuming the string is at the start of the file
// aka position: 0
// https://msdn.microsoft.com/en-us/library/dd267761(v=vs.110).aspx
accessor.ReadArray<byte>(
    position: 0,      // The number of bytes in the accessor at which to begin reading
    array: bytes,     // The array to contain the structures read from the accessor
    offset: 0,        // The index in `array` in which to place the first copied structure
    count: yourLength // The number of structures of type T to read from the accessor.
);

var myString = Encoding.UTF8.GetString(bytes);

我已经测试过了,它确实有效。我无法评论它的性能,或者它是否是最好的整体解决方案,只是它有效。

【讨论】:

酷,是的,绝对避免使用指针 :) ReadArray&lt;byte&gt; 慢得多,但是

以上是关于delphi 快速读取内存字符串的主要内容,如果未能解决你的问题,请参考以下文章

在delphi中如何获取字符串的内存地址

delphi读取html格式的字符串,显示在delphi窗体中。

delphi 如何把字符串转化为xml,最好只提供读取就好了,不用保存

Delphi:如何从内存中完全删除字符串[重复]

是否有一个Boyer-Moore字符串搜索和快速搜索和替换功能以及Delphi 2010 String(UnicodeString)的快速字符串计数?

Delphi 用文件流读取文本文件字符串的方法