可以将二维字节数组制成一个巨大的连续字节数组吗?

Posted

技术标签:

【中文标题】可以将二维字节数组制成一个巨大的连续字节数组吗?【英文标题】:Can as 2D byte array be made one huge continuous byte array? 【发布时间】:2011-04-08 18:24:23 【问题描述】:

我在内存中有一个非常大的二维字节数组,

byte MyBA = new byte[int.MaxValue][10];

有什么方法(可能是不安全的)可以让 C# 误以为这是一个巨大的连续字节数组?我想这样做,以便可以将其传递给 MemoryStream,然后传递给 BinaryReader

MyReader = new BinaryReader(MemoryStream(*MyBA)) //Syntax obviously made-up here

【问题讨论】:

我理解你的问题,但你为什么要分配这么大的数组??我毫不怀疑,即使您成功解决了这个技术问题,您的架构也是错误的,您尝试开发的程序最终也无法正常工作。 Gilad,我需要分配一个大数组才能在内存中处理它。速度对我来说是关键问题。另外,我有很多线程都同时处理相同的数据。我理解您的担忧,但是架构已经可以处理高达任意限制的数据,即字节数组的最大大小。如果我可以删除此限制(或愚弄 c#),那么我看不出它不应该继续工作的理由。 假设 int 是 32 位,每个 10 位,那你的数组不是 40 GB 吗? 【参考方案1】:

我不相信 .NET 提供此功能,但实现您自己的 System.IO.Stream 实现应该相当容易,它可以无缝切换支持数组。以下是(未经测试的)基础知识:

public class MultiArrayMemoryStream: System.IO.Stream

    byte[][] _arrays;
    long _position;
    int _arrayNumber;
    int _posInArray;

    public MultiArrayMemoryStream(byte[][] arrays)
        _arrays = arrays;
        _position = 0;
        _arrayNumber = 0;
        _posInArray = 0;
    

    public override int Read(byte[] buffer, int offset, int count)
        int read = 0;
        while(read<count)
            if(_arrayNumber>=_arrays.Length)
                return read;
            
            if(count-read <= _arrays[_arrayNumber].Length - _posInArray)
                Buffer.BlockCopy(_arrays[_arrayNumber], _posInArray, buffer, offset+read, count-read);
                _posInArray+=count-read;
                            _position+=count-read;
                read=count;
            else
                Buffer.BlockCopy(_arrays[_arrayNumber], _posInArray, buffer, offset+read, _arrays[_arrayNumber].Length - _posInArray);
                read+=_arrays[_arrayNumber].Length - _posInArray;
                            _position+=_arrays[_arrayNumber].Length - _posInArray;
                _arrayNumber++;
                _posInArray=0;
            
        
        return count;
    

    public override long Length
        get 
            long res = 0;
            for(int i=0;i<_arrays.Length;i++)
                res+=_arrays[i].Length;
            
            return res;
        
    

    public override long Position
        get  return _position; 
        set  throw new NotSupportedException(); 
    

    public override bool CanRead
        get  return true; 
    

    public override bool CanSeek
        get  return false; 
    

    public override bool CanWrite
        get  return false; 
    

    public override void Flush()
    

    public override void Seek(long offset, SeekOrigin origin)
        throw new NotSupportedException();
    

    public override void SetLength(long value)
        throw new NotSupportedException();
    

    public override void Write(byte[] buffer, int offset, int count)
        throw new NotSupportedException();
           

另一种解决 2^31 字节大小限制的方法是 UnmanagedMemoryStream,它在非托管内存缓冲区(可能与操作系统支持的一样大)之上实现 System.IO.Stream。像这样的东西可能会起作用(未经测试):

var fileStream = new FileStream("data", 
  FileMode.Open, 
  FileAccess.Read, 
  FileShare.Read, 
  16 * 1024, 
  FileOptions.SequentialScan);
long length = fileStream.Length;
IntPtr buffer = Marshal.AllocHGlobal(new IntPtr(length));
var memoryStream = new UnmanagedMemoryStream((byte*) buffer.ToPointer(), length, length, FileAccess.ReadWrite);
fileStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
// work with the UnmanagedMemoryStream
Marshal.FreeHGlobal(buffer);

【讨论】:

Rasmus - 我没听说过 - 我会查一下 - 谢谢 Rasmus - 看起来很有趣。 您能否指导我在示例中如何最好地将字节流从磁盘加载到 UnmanagedMemoryStream 中? 谢谢拉斯马斯 - 我正在处理你在这里给我的东西 Rasmus - 我做不到。几乎没有使用 SafeBuffer 的文档或示例,我得到“无法创建 ... SafeBuffer 的实例”。【参考方案2】:

同意。无论如何,你有数组大小本身的限制。

如果你真的需要在流中操作巨大的数组,编写你的自定义内存流类。

【讨论】:

是的 _ 我已经考虑过了——但是当我尝试跨越字节数组边界读取写入自定义流类时,这给了我一个单独的问题——我对此还有另一个问题。【参考方案3】:

我认为您可以使用以下方法使用线性结构而不是二维结构。

您可以使用 byte[int.MaxValue*10] 而不是 byte[int.MaxValue][10]。您可以将 [4,5] 处的项目寻址为 int.MaxValue*(4-1)+(5-1)。 (一般公式是(i-1)*列数+(j-1)。

当然你可以使用其他约定。

【讨论】:

他使用2D结构的原因是他超出了单字节数组的大小限制。【参考方案4】:

如果我正确理解了您的问题,那么您有一个庞大的文件,您想将其读入内存然后进行处理。但是你不能这样做,因为文件中的数据量超过了任何一维数组。

您提到速度很重要,并且您有多个线程并行运行以尽可能快地处理数据。如果您无论如何都必须为每个线程划分数据,为什么不将线程数基于覆盖所有内容所需的byte[int.MaxValue] 缓冲区数?

【讨论】:

对不起,应该说清楚。我的每个线程都运行在整个数据上。 我明白了。所以你正在做一些事情,比如对数据应用多个过滤器,而不是使用线程来更快地处理一组数据?只是想看看是否有另一种方法可以用来解决这个内存限制。【参考方案5】:

您可以创建一个memoryStream,然后使用Write方法逐行传递数组

编辑: MemoryStream 的限制当然是您的应用程序存在的内存量。也许有一个限制,但如果您需要更多内存,那么您应该考虑修改您的整体架构。例如。您可以分块处理数据,也可以对文件执行交换机制。

【讨论】:

是的 - 我这样做。但我相信 MemoryStream 仍然有一个最大限制,你可以写入多少 - 这与字节数组的最大大小相同......【参考方案6】:

如果您使用的是 Framework 4.0,您可以选择使用 MemoryMappedFile。内存映射文件可以由物理文件或 Windows 交换文件支持。内存映射文件就像内存中的流一样,在需要时透明地与后备存储交换数据。

如果您不使用 Framework 4.0,您仍然可以使用此选项,但您需要自己编写或找到现有的包装器。我希望The Code Project 上有很多。

【讨论】:

谢谢克里斯。我尝试了该选项,但 MemoryMappedFiles 非常慢。 @ManInMoon,这很不幸。性能下降可能是因为从用户空间到内核空间的转换,因为 MemoryMappedFiles 是内核对象。 @ManInMoon,数据的来源是什么?它是从文件中读入内存的吗?

以上是关于可以将二维字节数组制成一个巨大的连续字节数组吗?的主要内容,如果未能解决你的问题,请参考以下文章

多个线程可以对数组中的不同字节进行“原子”无锁写入吗?

如何在Java中将二维布尔数组转换为一维字节数组?

什么是一维数组

字符流和字节流的区别

字符流和字节流的区别

字符流和字节流的区别