【问题标题】:C# Stream write a Uint arrayC# Stream 写一个 Uint 数组
【发布时间】:2018-07-27 06:18:31
【问题描述】:

我有一些包含锯齿状 uint 数组的数据。
数据必须快速记录,所以我正在考虑使用 stream.Write

using (var stream = File.Open(Folder + @"\"+Filename, FileMode.OpenOrCreate))
 {                 
   // uint[][] RawData = new Uint [9000][]
  foreach(uint[] LogRow in RawData)
   {
     stream.Write(LogRow, 0, 300); 
   }           
  }
}

但是 Stream.write 只接受字节[]
可能有可能,简单地获取 LogRow
的指针偏移量 然后因为 a uint 是 32 位,所以使用 300*4 的计数
并将 uInt[ ] Logrow 作为字节缓冲区写入?

也许宽度不安全转换?

 int * ByteOffset = &LogRow;
//... ?? its a start 
// but it doesnt give the LogRow as Byte[] 
// not sure the best way to continou  
//

---更新说明---
此代码需要使用视频数据缓冲区,因此它必须尽可能快,并且没有转换开销、额外线程、安全检查、边界检查。在过去,人们常常更多地考虑速度,因为我们编码车间的糖果更少。在当今的编码需求中还有一些领域,其中速度仍然是“主要”要求。这是这样一个问题。不要假设我通过改进我的代码的其他区域来获得更快的速度,因为你还没有看到其他区域,这也不是对这个主题的建设性讨论。

基本上,我想要的是 uint LogRow[] ,可以作为 byte[] 使用而无需转换。将其视为我不想更改的计算机内存,只需将 uint[] 视为 byte[],应该可以计算偏移量和长度并将其分配给 byte[] 数组。 . 在 c++ 或汇编程序中很容易的事情,更困难的是它的 uint[] 的锯齿状数组。提醒一下,宽度 VB 的主要区别在于 C# 确实有不安全的选项,尽管使用它们是一门艺术。

我在下面写的一个衬里是强制转换(C# 编译器将对强制转换进行边界检查),但我把它写下来只是表明我知道什么是转换,而且我不是在寻找转换答案,谁添加了额外的说明。

byte[] objectCast = (byte[])(object)LogRow;

怎么做?

【问题讨论】:

  • “数据必须快速记录”关于您在这里谈论的哪个维度?不知是否有必要深入unsafe代码。
  • 我正在潜入毫秒;)(必须快速保存原始视频缓冲区,然后才能保存下一个缓冲区)
  • 转换需要复制时间,基本上我只是得到了可以直接转储的所有内存空间。 (c++ 不会介意,但这需要在 C# 中完成)。由于缓冲区确实很快到达,因此有这个速度要求。
  • 写入需要几毫秒,即使在 SSD 上也是如此。转换需要纳秒。与尝试消除这一微小步骤相比,您将从一个好的异步实现中受益更多。
  • 我建议将自助式写入磁盘卸载到另一个专用线程。将您的缓冲区设置为阻塞收集并让其他线程监视它。

标签: c# type-conversion


【解决方案1】:

如果您想使用文件流,请尝试使用 BinaryWriter,它会自动将每种类型写入字节。

【讨论】:

  • 每个 primitive 类型。
【解决方案2】:

好吧,我相信这无需转换就可以解决问题,它本质上使用结构体通过字段偏移量告诉编译器两个变量都指向相同的偏移量,因此 Ushort[ ] 成为标准 byte[ ] 变量。

虽然我认为这是解决方案,但我等待将其标记为答案,也许我将其发布在代码审查上。由于我很好奇其他人对此的看法,如果有更好更快的解决方案,我仍然全神贯注。 但由于我没有丢帧,所以我认为就是这样。

using System.Runtime.InteropServices   //must be on top of cs file

[StructLayout(LayoutKind.Explicit)]
 struct Converter
        {
            [FieldOffset(0)]
            public byte[] Bytes;     //warning bytes are 8 bit ! dont use
            [FieldOffset(0)]
            public ushort[] Ushorts;  //16bit
            [FieldOffset(0)]
            public char[] Chars;      //16bit
        }
 static byte[] ArrayUshort2Byte(ushort[] input)
        {

            Converter translate = new Converter();
            translate.Ushorts = input;
            return translate.Bytes;
        }

 static char[] ArrayUshort2Char(ushort[] input)
        {
            //without any buffer copying i convert types 
            //not even convert.ushort overhead no overhead here..

            Converter translate = new Converter();
            translate.Ushorts = input;
            return translate.Chars;
        }

小记, 在我想从 ushort 到字节表示的原始问题中,我在考虑“原始字节”作为内存字节。然而,Ushort 是 2 个字节而不是一个,处理宽度流写入器/流读取器,因此最好将这种转换为 char(这也是 16 位,如 ushort)

这很有趣,因为它很容易在 ushort /short / char 之间转换,只需将它们添加到结构宽度相同的偏移量。像我做宽度字节一样做一个类似的转换。

【讨论】:

    【解决方案3】:

    我不确定这是否是您想要的。这将返回与uint[] 对齐的byte[],但在这里您可以访问每个单独的字节:

    public unsafe static void Main()
    {
        var data = new uint[1][];
        data[0] = new uint[100000];
        data[0][0] = 1;
        data[0][1] = 2;
        data[0][2] = 3;
    
        Stopwatch sw = new Stopwatch();
        sw.Start();
    
        foreach (uint[] LogRow in data)
        {
            fixed (uint* start = &LogRow[0])
            {
                var dataLength = LogRow.Length * sizeof(uint);
                byte[] result = new byte[dataLength];
    
                Marshal.Copy((IntPtr)start, result, 0, dataLength);
                // This will return 100020003000...00000......
                // Do something with result
            }
        }
    
        sw.Stop();
    
        Console.WriteLine(sw.ElapsedTicks);
    
        Console.ReadKey();
    }
    

    在我的机器上完成 100 000 个元素大约需要 700 个 滴答声

    更新:

    从 C# 7.2 开始,您可以使用Span<T> 来避免不安全的代码:

    var jaggedData = new uint[1][];
    jaggedData[0] = new uint[100000];
    for (var index = 0; index < jaggedData[0].Length; index++)
    {
        jaggedData[0][index] = (uint)index;
    }
    
    foreach (uint[] logRow in jaggedData)
    {
        Span<byte> data = Span<uint>.DangerousCreate(logRow, ref logRow[0], logRow.Length).AsBytes();
        // Do stuff...
    }
    

    注意Span&lt;T&gt;是在栈上分配的,所以如果你有很多数据,那么你可能会得到StackOverflowException

    【讨论】:

    • 嗯,它需要一个副本,我的答案没有复制(见下文,我还没有将其标记为答案)
    • @user3800527 你可以比较一下速度,如果有什么不同请告诉我:) byte* 可以优化
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多