C# .NET等效于_byteswap_ulong/OSSwapInt32/或bswap32

C# .NET等效于_byteswap_ulong/OSSwapInt32/或bswap32,c#,C#,有人知道是否有一个.NET Framework等价物用于交换uint中的字节顺序吗 我正在尝试将一些自定义的c哈希代码移植到c#,该代码使用MSFTs(相当于*Nix world中的Apple OSSwapInt32或Bswap32)。我可以手动编写此函数,但我怀疑它是否会利用任何编译器优化(例如,c/c++编译器提供的内部特性很难超越,我希望运行时对内置功能也能做到这一点)。如果有关系的话,我不在乎如何保持持久性 我尝试过一种基于泛型的解决方案,但我不相信这是最佳的 BitConverter.

有人知道是否有一个.NET Framework等价物用于交换uint中的字节顺序吗

我正在尝试将一些自定义的c哈希代码移植到c#,该代码使用MSFTs(相当于*Nix world中的Apple OSSwapInt32或Bswap32)。我可以手动编写此函数,但我怀疑它是否会利用任何编译器优化(例如,c/c++编译器提供的内部特性很难超越,我希望运行时对内置功能也能做到这一点)。如果有关系的话,我不在乎如何保持持久性

我尝试过一种基于泛型的解决方案,但我不相信这是最佳的

BitConverter.ToUInt32(BitConverter.GetBytes(g).Reverse().ToArray<byte>(),0);
BitConverter.ToUInt32(BitConverter.GetBytes(g.Reverse().ToArray(),0);
编辑:

所以我算出了这个特定函数平均在十分钟内被调用的次数(对于我们的散列消费者之一)。此函数被调用1000000000次。因此,我设置了一些微评测,以查看c代码相对于下面提供的解决方案(以及上面提出的解决方案)的性能

在我信任的笔记本电脑上,C代码在大约1500毫秒的时间内运行了这么多操作(使用内部程序)。 我上面介绍的c#代码运行时间几乎为2689581毫秒。这是一个巨大的差异。 马修·沃森(Matthew Watson)提出的c#代码运行时间几乎为36000毫秒。 Caramiriel提供的第一个解决方案运行时间约为115014 ms,第二个解决方案提供的运行时间约为36000 ms

虽然这些解决方案都没有接近内在调用的速度,但它们比我的原始解决方案要好得多(对于如此多的计算,从44分钟到36秒)。这完全可以接受我的申请。尽管如果.NET编译器提供一些与本机编译器相同的内在功能会更好

为了完整起见,这里是我的微基准标记C代码:

#include "stdafx.h"
#include "windows.h"

unsigned long Swap(unsigned int value)
{
    return _byteswap_uint64(value);
}

int _tmain(int argc, _TCHAR* argv[])
{
    unsigned int value = 0x01020304;
    unsigned long NUMITER = 10000000000;
    unsigned long a=0;
    unsigned long z=0;
    int throwAwayLoopCount = 5;

    for (int k = 0; k < throwAwayLoopCount; ++k)
    {
        a = GetTickCount();
        for (unsigned long  i = 0; i < NUMITER; ++i)
        {
            value = Swap(value);
        }
        z = GetTickCount();
        printf("Baseline, Cached: time is %4lld milliseconds: value%4lld\n", z-a,value);
    }

    printf("Baseline, Cached: time is %4lld milliseconds\n", z-a);

    return 0;
}
#包括“stdafx.h”
#包括“windows.h”
无符号长交换(无符号整数值)
{
返回字节WAP uint64(值);
}
int _tmain(int argc,_TCHAR*argv[]
{
无符号整数值=0x01020304;
无符号长NUMITER=1000000000;
无符号长a=0;
无符号长z=0;
int THROWAYLOOPCOUNT=5;
对于(int k=0;k
以下是对提供的解决方案进行基准测试的c#代码:

namespace ByteSwapProfiler
{
    using System.Runtime.InteropServices;
    using System.Diagnostics;

    [StructLayout(LayoutKind.Explicit)]
    internal struct UInt32Union
    {
        [FieldOffset(0)]
        public UInt32 Value;
        [FieldOffset(0)]
        public byte Byte1;
        [FieldOffset(1)]
        public byte Byte2;
        [FieldOffset(2)]
        public byte Byte3;
        [FieldOffset(3)]
        public byte Byte4;
    }


    class Program
    {

        static uint ByteSwapNaive(uint g)
        {
            return BitConverter.ToUInt32(BitConverter.GetBytes(g).Reverse().ToArray<byte>(), 0);
        }

        static uint ByteSwapCaramiriel1(uint value)
        {
            unchecked
            {
                return ((value & 0xff000000) >> 24) |
                        ((value & 0x00ff0000) >> 8) |
                        ((value & 0x0000ff00) << 8) |
                        ((value & 0x000000ff) << 24);
            }
        }

        static uint ByteSwapCaramiriel2(UInt32Union src)
        {
            UInt32Union dest = new UInt32Union
                {
                    Byte1 = src.Byte4,
                    Byte2 = src.Byte3,
                    Byte3 = src.Byte2,
                    Byte4 = src.Byte1
                };

            return dest.Value;
        }

        static uint ByteSwapMatthewWatson(uint word)
        {
            return ((word >> 24) & 0x000000FF) | ((word >> 8) & 0x0000FF00) | ((word << 8) & 0x00FF0000) | ((word << 24) & 0xFF000000);            
        }

        static void Main(string[] args)
        {
            uint value= 0x01020304;
            UInt32Union src = new UInt32Union();
            src.Value = value;

            ulong NUMITER = 10000000000;
            uint throwAwayLoopCount = 5;
            var sw = new Stopwatch();
            string name = "Naive";
            //for (int k = 0; k < throwAwayLoopCount; ++k)
            {
                sw = Stopwatch.StartNew();
                for (ulong i = 0; i < NUMITER; ++i)
                {
                    value = ByteSwapNaive(value);
                }
                sw.Stop();
                Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.  Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"),value);
            }

            Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0"));


            name = "MatthewWatson";
            for (int k = 0; k < throwAwayLoopCount; ++k)
            {
                sw = Stopwatch.StartNew();
                for (ulong i = 0; i < NUMITER; ++i)
                {
                    value = ByteSwapMatthewWatson(value);
                }
                sw.Stop();
                Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.  Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value);
            }
            Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0"));

            name = "Caramiriel2";
            for (int k = 0; k < throwAwayLoopCount; ++k)
            {
                sw = Stopwatch.StartNew();
                for (ulong i = 0; i < NUMITER; ++i)
                {
                    value = ByteSwapCaramiriel2(src);
                }
                sw.Stop();
                Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.  Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value);
            }
            
            Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0"));

            name = "Caramiriel1";
            for (int k = 0; k < throwAwayLoopCount; ++k)
            {
                sw = Stopwatch.StartNew();
                for (ulong i = 0; i < NUMITER; ++i)
                {
                    value = ByteSwapCaramiriel1(value);
                }
                sw.Stop();
                Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.  Value:{2} \n", name, (sw.ElapsedMilliseconds).ToString("0"), value);
            }
            
            Console.Write("{0,-13}, Cached: time is {1,7} milliseconds.\n", name, (sw.ElapsedMilliseconds).ToString("0"));
        }
    }
}
名称空间ByteSwapProfiler
{
使用System.Runtime.InteropServices;
使用系统诊断;
[StructLayout(LayoutKind.Explicit)]
内部结构UINT32接头
{
[字段偏移量(0)]
公共UInt32值;
[字段偏移量(0)]
公共字节字节1;
[现场偏移(1)]
公共字节字节2;
[现场偏移(2)]
公共字节字节3;
[现场偏移(3)]
公共字节字节4;
}
班级计划
{
静态单位字节数(单位g)
{
返回BitConverter.ToUInt32(BitConverter.GetBytes(g.Reverse().ToArray(),0);
}
静态uint字节数WAPCarameriel1(uint值)
{
未经检查
{
返回((值&0xff000000)>>24)|
((值&0x00ff0000)>>8)|

((value&0x0000ff00)24)和0x000000FF)|((word>>8)和0x0000ff00)|((word我认为你不会像byteswap\u ulong那么快得到任何东西,但是如果你使用这个:

public static uint SwapBytes(uint word)
{
    return ((word>>24)&0x000000FF) | ((word>>8)&0x0000FF00) | ((word<<8)&0x00FF0000) | ((word<<24)&0xFF000000);            
}
公共静态uint交换字节(uint字)
{

return((word>>24)&0x000000FF)|((word>>8)&0x0000FF00)|((word一种方法是直接处理(U)Int32类型。这使您的开销最小,例如Linq中使用的状态机和涉及的方法调用

unchecked {     
    return 
            ((value & 0xff000000) >> 24) |
            ((value & 0x00ff0000) >> 8) |
            ((value & 0x0000ff00) << 8) |
            ((value & 0x000000ff) << 24);
}

从.NET Core 2.1开始,BinaryPrimitives.ReverseEndianness为该功能提供了一个优化的软件实现。从.NET Core 3.0开始,它使用JIT内在实现,该内在实现被JIT转换为使用bswap指令的非常高效的机器代码。

您可以使用该方法,并且它将最有效地实现对于int16 int32 int64,实际上使用的是单CPU指令,这在大多数情况下都是内联的

Big-endian系统上的Bot这种方法不会运行,但我不知道有哪个系统运行当前的dotnet


或者你可以使用@scott's answer和BinaryPrimitives.ReverseEndianness,但这只适用于net core。

出于好奇,这些本质是什么?@Jon\u byteswap\u uint64是msfts固有的。你可以在这里找到它们的列表,你可以问这个问题,因为你不知道C\35;替代方案是否足够快。我们不知道呃,我们无法分析您的代码。您必须自己分析。@HansPassant感谢您的建设性反馈。正如我所怀疑的,上面代码的C#版本确实比内在版本慢了3个数量级。@NickStrupat Yep,这正是我所做的。它没有实现为原始的In运行时可以在此处确认这一点
[StructLayout(LayoutKind.Explicit)]
internal struct UInt32Union
{
    [FieldOffset(0)] public UInt32 Value;
    [FieldOffset(0)] public byte Byte1;
    [FieldOffset(1)] public byte Byte2;
    [FieldOffset(2)] public byte Byte3;
    [FieldOffset(3)] public byte Byte4;
}

static UInt32 Swap( UInt32 value )
{
    UInt32Union src = new UInt32Union
    src.Value = value;

    UInt32Union dest = new UInt32Union
        {
            Byte1 = src.Byte4,
            Byte2 = src.Byte3,
            Byte3 = src.Byte2,
            Byte4 = src.Byte1
        };

    return dest.Value;
}