ما هي أسرع طريقة لتحويل float[] إلى بايت[]؟

StackOverflow https://stackoverflow.com/questions/619041

  •  03-07-2019
  •  | 
  •  

سؤال

أود الحصول على byte[] من float[] في أسرع وقت ممكن، دون تكرار المصفوفة بأكملها (من خلال طاقم الممثلين، على الأرجح).الكود غير الآمن جيد.شكرًا!

أنا أبحث عن مصفوفة بايت أطول بأربع مرات من المصفوفة العائمة (سيكون بُعد مصفوفة البايت 4 أضعاف حجم المصفوفة العائمة، حيث أن كل عائم يتكون من 4 بايت).سأمرر هذا إلى BinaryWriter.

يحرر:إلى هؤلاء النقاد الذين يصرخون "التحسين المبكر":لقد قمت بقياس ذلك باستخدام ملف التعريف ANTS قبل أن أقوم بالتحسين.حدثت زيادة ملحوظة في السرعة لأن الملف يحتوي على ذاكرة تخزين مؤقت للكتابة، كما أن حجم المصفوفة العائمة يتناسب تمامًا مع حجم القطاع الموجود على القرص.يقوم الكاتب الثنائي بتغليف مؤشر الملف الذي تم إنشاؤه باستخدام pinvoke'd win32 API.يحدث التحسين لأن هذا يقلل من عدد استدعاءات الوظائف.

وفيما يتعلق بالذاكرة، يقوم هذا التطبيق بإنشاء مخابئ ضخمة تستهلك قدرًا كبيرًا من الذاكرة.يمكنني تخصيص المخزن المؤقت للبايت مرة واحدة وإعادة استخدامه عدة مرات - حيث يؤدي استخدام الذاكرة المزدوجة في هذه الحالة بالذات إلى حدوث خطأ مستدير في استهلاك الذاكرة الإجمالي للتطبيق.

لذا أعتقد أن الدرس هنا هو عدم وضع افتراضات سابقة لأوانها ;)

هل كانت مفيدة؟

المحلول

إذا كنت لا تريد أي تحويل ليحدث، أود أن أقترح Buffer.BlockCopy ().

public static void BlockCopy(
    Array src,
    int srcOffset,
    Array dst,
    int dstOffset,
    int count
)

وعلى سبيل المثال:

float[] floatArray = new float[1000];
byte[] byteArray = new byte[floatArray.Length * 4];

Buffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);

نصائح أخرى

وهناك سريع <م> (رمز لا غير آمنة) الطريقة القذرة للقيام بذلك:

[StructLayout(LayoutKind.Explicit)]
struct BytetoDoubleConverter
{
    [FieldOffset(0)]
    public Byte[] Bytes;

    [FieldOffset(0)]
    public Double[] Doubles;
}
//...
static Double Sum(byte[] data)
{
    BytetoDoubleConverter convert = new BytetoDoubleConverter { Bytes = data };
    Double result = 0;
    for (int i = 0; i < convert.Doubles.Length / sizeof(Double); i++)
    {
        result += convert.Doubles[i];
    }
    return result;
}

وهذا العمل، ولكن لست متأكدا من الدعم على مونو أو إصدارات أحدث من CLR . الشيء الغريب الوحيد هو أن array.Length هو طول بايت. ويمكن تفسير ذلك لأنه ينظر إلى طول مجموعة المخزن مع مجموعة، ولأن هذه المجموعة كانت مجموعة بايت طول ستظل في طول بايت. مفهرس لا نفكر في مزدوجة كونها ثمانية بايت كبيرة جدا ضروري لا حساب هناك.

ولقد بحثت عن ذلك بعض أكثر، وانها وصفت الواقع على MSDN ، <م > كيفية: إنشاء C / C ++ الاتحاد باستخدام سمات (C # و Visual Basic) ، لذلك هناك احتمالات وسيتم دعم ذلك في الإصدارات المستقبلية. أنا لست متأكدا من مونو بالرغم من ذلك.

التحسين المبكر هو أصل كل الشرور!يعد اقتراح @ Vlad بالتكرار على كل تعويم إجابة أكثر منطقية من التبديل إلى بايت [].خذ الجدول التالي لأوقات التشغيل لزيادة عدد العناصر (متوسط ​​50 مرة):

Elements      BinaryWriter(float)      BinaryWriter(byte[])
-----------------------------------------------------------
10               8.72ms                    8.76ms
100              8.94ms                    8.82ms
1000            10.32ms                    9.06ms
10000           32.56ms                   10.34ms
100000         213.28ms                  739.90ms
1000000       1955.92ms                10668.56ms

لا يوجد فرق كبير بين الاثنين بالنسبة للأعداد الصغيرة من العناصر.بمجرد الوصول إلى عدد كبير من نطاق العناصر، فإن الوقت المستغرق في النسخ من float[] إلى byte[] يفوق بكثير الفوائد.

لذلك اتبع ما هو بسيط:

float[] data = new float[...];
foreach(float value in data)
{
    writer.Write(value);
}

وهناك الطريق الذي يتجنب نسخ الذاكرة والتكرار.

ويمكنك استخدام الإختراق القبيح حقا أن تغيير مؤقت مجموعة لنوع آخر باستخدام (آمنة) التلاعب الذاكرة.

واختبرت هذا الإختراق في كل OS 32 و 64 بت، لذلك ينبغي أن تكون محمولة.

ويتم الحفاظ على استخدام مصدر + عينة على https://gist.github.com/1050703 ، ولكن لراحتك أنا صقه هنا أيضا:

public static unsafe class FastArraySerializer
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)] public byte[] bytes;
        [FieldOffset(0)] public float[] floats;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    private static readonly UIntPtr BYTE_ARRAY_TYPE;
    private static readonly UIntPtr FLOAT_ARRAY_TYPE;

    static FastArraySerializer()
    {
        fixed (void* pBytes = new byte[1])
        fixed (void* pFloats = new float[1])
        {
            BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
            FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
        }
    }

    public static void AsByteArray(this float[] floats, Action<byte[]> action)
    {
        if (floats.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {floats = floats};
        union.floats.toByteArray();
        try
        {
            action(union.bytes);
        }
        finally
        {
            union.bytes.toFloatArray();
        }
    }

    public static void AsFloatArray(this byte[] bytes, Action<float[]> action)
    {
        if (bytes.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {bytes = bytes};
        union.bytes.toFloatArray();
        try
        {
            action(union.floats);
        }
        finally
        {
            union.floats.toByteArray();
        }
    }

    public static bool handleNullOrEmptyArray<TSrc,TDst>(this TSrc[] array, Action<TDst[]> action)
    {
        if (array == null)
        {
            action(null);
            return true;
        }

        if (array.Length == 0)
        {
            action(new TDst[0]);
            return true;
        }

        return false;
    }

    private static ArrayHeader* getHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }

    private static void toFloatArray(this byte[] bytes)
    {
        fixed (void* pArray = bytes)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = FLOAT_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
        }
    }

    private static void toByteArray(this float[] floats)
    {
        fixed(void* pArray = floats)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = BYTE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
        }
    }
}

والاستخدام هو:

var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
    foreach (var b in bytes)
    {
        Console.WriteLine(b);
    }
});

من الأفضل أن تترك BinaryWriter افعل هذا من أجلك.سيكون هناك تكرار لمجموعة البيانات بأكملها بغض النظر عن الطريقة التي تستخدمها، لذلك ليس هناك فائدة من اللعب بالبايتات.

على الرغم من أنه يمكنك الحصول على byte* المؤشر باستخدام unsafe و fixed, ، لا يمكنك تحويل byte* ل byte[] لكي يقبلها الكاتب كمعلمة دون إجراء نسخ البيانات.وهو ما لا تريد القيام به لأنه سيضاعف مساحة ذاكرتك و أضف تكرارًا إضافيًا على التكرار الحتمي الذي يجب تنفيذه لإخراج البيانات إلى القرص.

بدلا من ذلك، أنت لا تزال أفضل حالا التكرار على مجموعة من العوامات وكتابة كل منها float للكاتب بشكل فردي, ، باستخدام Write(double) طريقة.سيظل سريعًا بسبب التخزين المؤقت داخل الكاتب.يرى sixlettervariablesأرقام.

لدينا فئة تسمى LudicrousSpeedSerialization وأنه يحتوي على طريقة غير آمنة التالية:

    static public byte[] ConvertFloatsToBytes(float[] data)
    {
        int n = data.Length;
        byte[] ret = new byte[n * sizeof(float)];
        if (n == 0) return ret;

        unsafe
        {
            fixed (byte* pByteArray = &ret[0])
            {
                float* pFloatArray = (float*)pByteArray;
                for (int i = 0; i < n; i++)
                {
                    pFloatArray[i] = data[i];
                }
            }
        }

        return ret;
    }

وعلى الرغم من أنه أساسا لم تفعل لحلقة وراء الكواليس، فإنه لا قيام بهذه المهمة في سطر واحد

byte[] byteArray = floatArray.Select(
                    f=>System.BitConverter.GetBytes(f)).Aggregate(
                    (bytes, f) => {List<byte> temp = bytes.ToList(); temp.AddRange(f); return temp.ToArray(); });
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top