简体   繁体   English

System.Numerics.Vectors提供了有关大小和位顺序的保证?

[英]What guarantees does System.Numerics.Vectors provide about size and bit order?

I have implemented a vector-based c# approximation of Log. 我已经实现了Log的基于矢量的c#近似。 It includes unsafe code. 它包括不安全的代码。 It's been working fine in a number of environments, but on a recent deployment has fallen over. 它在许多环境中都运行良好,但最近的部署已经失败了。 The implementation uses SIMD through the System.Numerics.Vectors library. 该实现通过System.Numerics.Vectors库使用SIMD。

Unfortunately I cannot test on the system where the software isn't working. 不幸的是,我无法在软件无法运行的系统上进行测试。 However, I would like to know which assumptions I made about the library are invalid: 但是,我想知道我对库做出的假设是无效的:

  • Does Vector<float>.Count always return a power of 2 ? Vector <float> .Count总是返回2的幂吗?
  • Does Vector<UInt64>.Count == Vector.Count * 2 ? Vector <UInt64> .Count == Vector.Count * 2?
  • Can I take a pointer of a vector using Unsafe.AsPointer, then perform standard operations as though it were N packed numbers in memory ? 我可以使用Unsafe.AsPointer获取向量的指针,然后执行标准操作,就好像它是内存中的N个打包数字一样吗?
  • Are there any processors that dotNet 4 runs on that have different endian-ness or don't store floats in IEEE754 format? 是否有任何运行dotNet 4的处理器具有不同的字节序或不存储IEEE754格式的浮点数?

The code is as follows: 代码如下:

const float invLn2 = 1.44269504089f; // 1 / ln(2)
        const float pow2_126 = 8.5070592e+37f; //2^126

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector<float> QuickLog2(Vector<float> vecOrig)
        {
            //32 bit Float specification:
            //Leftmost bit is sign bit.
            //Next 8 bits are exponent
            //Next 23 bits are mantissa
            unsafe
            {
                var ints = Vector.AsVectorUInt32(vecOrig);

                var exponents = Vector.BitwiseAnd(ints, new Vector<uint>(0x7F800000));
                BitshiftVector23(Unsafe.AsPointer(ref exponents));

                var unsignedExponents = exponents - new Vector<uint>(127);
                var signedExponents = Vector.AsVectorInt32(unsignedExponents);
                var localMantissBitmask = Vector.AsVectorSingle(new Vector<UInt32>(0x807FFFFF));
                var maskedMantissas = Vector.BitwiseAnd(vecOrig, localMantissBitmask);
                var mantissas = maskedMantissas * new Vector<float>(pow2_126);

                var mantissasLogged = LogPolynomialFunction2(mantissas) * new Vector<float>(invLn2);

                Vector<float> floatExponents;
#if false
                floatExponents = Vector.ConvertToSingle(signedExponents);               
#else
                ConvertIntToFloatInPace(Unsafe.AsPointer(ref signedExponents));
                floatExponents = Vector.AsVectorSingle(signedExponents);
#endif

                return mantissasLogged + floatExponents;
            }
        }

        const float log10_2 = 0.30102999566398119521373889472449f;
        /// <summary>
        /// A vectorized implementation of Log10(N). Uses bitshift, bitmasks, and unsafe code.
        /// Does not have the same safety as Math.Log10: Behaviour for infities, zero, negative numbers are undefined.
        /// </summary>
        /// <param name="vec">The vector to take the log of</param>
        /// <returns>The log, to the base 10, of the vector</returns>
        /// <remarks>
        /// Accurate to about 10^-7, which is the limit of a 32 bit float anyway.
        /// In my (BS) tests, takes about twice as long to run on as Math.Log10(...), but operates on 8 numbers,
        /// so 4x faster.
        /// Reverts to Math.Log10(...) if vectors are not hardware accelerated. 
        /// Given the extra memory copies required, that will be much slower than using scalar code.
        /// It'll be nice once intrinsics make it into dotNet and we can replace this with a single instruction...
        /// </remarks>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Vector<float> QuickLog10(Vector<float> vec)
        {
            if (Vector.IsHardwareAccelerated)
                return QuickLog2(vec) * new Vector<float>(log10_2);
            else
            {
                float[] tmp = new float[Vector<float>.Count];
                vec.CopyTo(tmp);
                for (int i = 0; i < Vector<float>.Count; i++)
                    tmp[i] = (float)Math.Log10(tmp[i]);
                return new Vector<float>(tmp);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe void BitshiftVector23(void* vector)
        {
            UInt64* asUlong = (UInt64*)vector;
            if (Vector<UInt64>.Count == 4)
            {
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
            }
            else if (Vector<UInt64>.Count == 8)
            {
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
                asUlong++;
                *asUlong = *asUlong >> 23;
            }
            else
                for (int i = 0; i < Vector<UInt64>.Count; i++)
                    asUlong[i] = asUlong[i] >> 23;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe void ConvertIntToFloatInPace(void* vector)
        {
            int* asInt = (int*)vector;
            if (Vector<int>.Count == 8)
            {
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
                *(float*)asInt = *asInt;
                asInt++;
            }
            else if (Vector<UInt64>.Count == 16)
            {
                for (int i = 0; i < 2; i++)
                {
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                    *(float*)asInt = *asInt;
                    asInt++;
                }
            }
            else
                for (int i = 0; i < Vector<UInt64>.Count; i++)
                {
                    *(float*)asInt = *asInt;
                    asInt++;
                }
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static Vector<float> LogPolynomialFunction2(Vector<float> mantissas)
        {
            var zm1 = mantissas;
            var zp1 = mantissas + new Vector<float>(2f);
            var zm1Divzp1 = Vector.Divide(zm1, zp1);
            var squared = zm1Divzp1 * zm1Divzp1;
            var cur = zm1Divzp1;

            //Manual loop unwinding:
#if false
                var mantissasLogged = Vector<float>.Zero;
                for (float i = 0; i < 4; i++)
                {
                    var fac = 2f / (2f * i + 1f);
                    mantissasLogged += cur * new Vector<float>(fac);
                    cur *= squared;
                }
#else
            //i = 0;
            const float fac0 = 2f / (2 * 0 + 1);
            var mantissasLogged = cur * new Vector<float>(fac0);
            cur *= squared;

            //i = 1;
            const float fac1 = 2f / (2 * 1 + 1);
            mantissasLogged += cur * new Vector<float>(fac1);
            cur *= squared;

            //i = 2;
            const float fac2 = 2f / (2 * 2 + 1);
            mantissasLogged += cur * new Vector<float>(fac2);
            cur *= squared;

            //i = 3;
            const float fac3 = 2f / (2 * 3 + 1);
            mantissasLogged += cur * new Vector<float>(fac3);
            cur *= squared;

            //i = 4;
            const float fac4 = 2f / (2 * 4 + 1);
            mantissasLogged += cur * new Vector<float>(fac4);
#endif
            return mantissasLogged;
        }

EDIT: I put some simple tests into the program on startup. 编辑:我在启动时对程序进行了一些简单的测试。 Vector.IsHardwareAccelerated == true; Vector.IsHardwareAccelerated == true; Vector.Count == 4; Vector.Count == 4; This vectorised Log gives the correct answer for the first two inputs, but incorrect for the second two. 此向量化日志为前两个输入提供正确答案,但对于后两个输入则不正确。 Perhaps the assumption that Unsafe.AsPointer(Vector) gives me a pointer to the vector elements as four consecutive floats is incorrect. 也许假设Unsafe.AsPointer(Vector)给我一个指向矢量元素的指针作为四个连续的浮点数是不正确的。

Log outputs: 日志输出:

DEBUG Vector.IsHardwareAccelerated: True 
DEBUG Vector<float>.Count: 4 
DEBUG Vector<Uint64>.Count: 2 
DEBUG MathUtils test input data: 5.967E+009,1.072E+006,9.521E+017,4.726E+000 
DEBUG MathUtils required output: 9.776,6.030,17.979,0.674 
DEBUG MathUtils actual output: 9.776,6.030,0.218,0.072 

(Yet to have a chance to check the bit patterns...) (还有机会检查位模式......)

IEEE 754 floating-point standard does not specify endianness, it definitely could be a problem here (depending on what you are running on) IEEE 754浮点标准没有指定字节顺序,这肯定是一个问题(取决于你运行的是什么)

You can use BitConverter.IsLittleEndian and vary accordingly 您可以使用BitConverter.IsLittleEndian并相应地改变

Indicates the byte order ("endianness") in which data is stored in this computer architecture. 指示数据存储在此计算机体系结构中的字节顺序(“endianness”)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 System.Numerics.Vectors和Windows 7-不起作用 - System.Numerics.Vectors and Windows 7 - does not work 通过NuGet安装System.Numerics.Vectors - Installing System.Numerics.Vectors via NuGet System.Numerics.Vectors IsHardwareAccelerated返回false - System.Numerics.Vectors IsHardwareAccelerated returns false 为什么 System.Memory 依赖于 System.Numerics.Vectors in.Net 4.6.1 而不是 in.Net 4.6? - Why does System.Memory have a dependency on System.Numerics.Vectors in .Net 4.6.1 but not in .Net 4.6? 使用System.Numerics.Vectors旋转2D点 - Rotate 2D points using System.Numerics.Vectors 无法加载文件或程序集“System.Numerics.Vectors - Could not load file or assembly 'System.Numerics.Vectors VS Code Omnisharp.MsBuild.Projectmanager 无法加载程序集 System.Numerics.Vectors 4.1.3.0 - VS Code Omnisharp.MsBuild.Projectmanager can not load assembly System.Numerics.Vectors 4.1.3.0 C# 6、VS2015-3、.Net 4.6、System.Numerics.Vectors 没有 Vector<t></t> - C# 6, VS2015-3, .Net 4.6, System.Numerics.Vectors has no Vector<T> 在 Godot C# android 构建中使用 System.Text.Json 时无法加载文件或程序集“System.Numerics.Vectors” - Could not load file or assembly 'System.Numerics.Vectors' when using System.Text.Json in Godot C# android build SignalR - 使用 AspNetCore.SignalR.Client nuget 包时找不到 System.Numerics.Vectors v4.1.4 - SignalR - System.Numerics.Vectors v4.1.4 not found when using AspNetCore.SignalR.Client nuget package
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM