Изменение типа массива приводит к падению после сборки мусор
От: Albeoris  
Дата: 22.08.15 14:27
Оценка: 9 (2)
Доброго времени суток!

Как известно, в CLR у каждого массива есть заголовок — два IntPtr, один из которых указывает на тип элемента массива, а другой описывает его размер.
Логично предположить, что если изменить тип элементов массива, скажем с byte на int, а размер массива, соответственно, уменьшить в 4 раза, при условии, что он кратен 4, упаковать его в (object) и распаковать, как (int[]), мы получим массив нового типа без какого-либо копирования элементов. Быстро и удобно.

В большинстве случаев это не требуется, и можно обойтись unsafe/fixed и кастом типа указателей.
Но далеко не все методы .NET умеют работать с указателями. Наглядный пример — методы Read/Write потоков.

Но есть проблема — работает вышеописанный способ лишь до первой сборки мусора. Как только GC пытается удалить осиротевший массив, претерпевший подобное изнасилование, приложение падает.
Вопрос — чего не хватает? Как научить GC работать со старыми массивами, у которых изменился тип?

  Текущая реализация грязного хака
int[] result = _input.DungerousReadStructs<Int32>(entry.Length / 4);



public static T[] DungerousReadStructs<T>(this Stream input, int count) where T : struct
{
    if (count < 1)
        return new T[0];

    Array result = new T[count];
    Int32 entrySize = UnsafeTypeCache<T>.UnsafeSize;
    using (UnsafeTypeCache<byte>.ChangeArrayType(result, entrySize))
        input.EnsureRead((byte[])result, 0, result.Length);

    return (T[])result;
}



public static class TypeCache<T>
{
    public static readonly Type Type = typeof(T);
}

public static class UnsafeTypeCache<T>
{
    public static readonly Int32 UnsafeSize = GetSize();
    public static readonly UIntPtr ArrayTypePointer = GetArrayTypePointer();

    private static Int32 GetSize()
    {
        DynamicMethod dynamicMethod = new DynamicMethod("SizeOf", typeof(Int32), Type.EmptyTypes);
        ILGenerator generator = dynamicMethod.GetILGenerator();

        generator.Emit(OpCodes.Sizeof, TypeCache<T>.Type);
        generator.Emit(OpCodes.Ret);

        return ((Func<int>)dynamicMethod.CreateDelegate(typeof(Func<Int32>)))();
    }

    private static unsafe UIntPtr GetArrayTypePointer()
    {
        T[] result = new T[1];
        using (SafeGCHandle handle = new SafeGCHandle(result, GCHandleType.Pinned))
            return *(((UIntPtr*)handle.AddrOfPinnedObject().ToPointer()) - 2);
    }

    public static IDisposable ChangeArrayType(Array array, Int32 oldElementSize)
    {
        if (array.Length < 1)
            throw new NotSupportedException();

        SafeGCHandle handle = new SafeGCHandle(array, GCHandleType.Pinned);
        try
        {
            unsafe
            {
                UIntPtr* arrayPointer = (UIntPtr*)handle.AddrOfPinnedObject().ToPointer();
                UIntPtr arrayLength = *(arrayPointer - 1);
                UIntPtr arrayType = *(arrayPointer - 2);
                UInt64 arraySize = ((UInt64)arrayLength * (UInt64)oldElementSize);

                if (arraySize % (UInt64)UnsafeSize != 0)
                    throw new InvalidCastException();

                try
                {
                    *(arrayPointer - 1) = new UIntPtr(arraySize / (UInt64)UnsafeSize);
                    *(arrayPointer - 2) = ArrayTypePointer;

                    return new DisposableAction(() =>
                    {
                        *(arrayPointer - 1) = arrayLength;
                        *(arrayPointer - 2) = arrayType;
                        handle.Dispose();
                    });
                }
                catch
                {
                    *(arrayPointer - 1) = arrayLength;
                    *(arrayPointer - 2) = arrayType;
                    throw;
                }
            }
        }
        catch
        {
            handle.SafeDispose();
            throw;
        }
    }
}


Работает. А вот если перед тем, как я дёрну "Free" не вернуть значения типа и длины к оригинальным, сборка мусора крашет приложение.
Вот и возник вопрос — отчего так? Помимо заголовка архива, тип объекта хранится где-то ещё? (н.п. в объявлении локальной переменной?). Можно ли и его изменить?

Также интересует — как в такой случае правильно описывать структуры? Marshaling работать не будет — это понятно. Соответственно, никаких ссылочных типов.
Вопрос: Как прикрутить собственный маршалинг, н.п. для корректного распознавания строк в той или иной кодировке? Вложенных массивов (аналог ByValStr, ByValArray)
Вопрос: Нужно ли структурам задавать аттрбиут StructLayout и будет ли он использоваться (или это часть механизма маршалинга?)
Вопрос: К чему приведёт использование подобного хака в совокупности со stackalloc?
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Отредактировано 22.08.2015 16:37 Albeoris . Предыдущая версия . Еще …
Отредактировано 22.08.2015 16:33 Albeoris . Предыдущая версия .
c# cshapr native pointer hack gc internal array crash
Re: Изменение типа массива приводит к падению после сборки м
От: Sinix  
Дата: 22.08.15 14:40
Оценка: :)
Здравствуйте, Albeoris, Вы писали:

A>Вопрос — чего не хватает? Как научить GC работать со старыми массивами, у которых изменился тип?


Кэп: write in C
Автор: UgN
Дата: 20.12.02
.

Ну, или использовать какой-нибудь грязный хак
Автор: UgN
Дата: 20.12.02
, но это скучно.
Отредактировано 22.08.2015 14:46 Sinix . Предыдущая версия .
Re: Изменение типа массива приводит к падению после сборки мусора
От: GlebZ Россия  
Дата: 22.08.15 14:41
Оценка:
Здравствуйте, Albeoris, Вы писали:

Если я правильно понял:
GCHandle handle = GCHandle.Alloc(ob, GCHandleType.Pinned);
....//working


handle.Free(ob);
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[2]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 22.08.15 16:32
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Если я правильно понял:

GZ>
GZ>GCHandle handle = GCHandle.Alloc(ob, GCHandleType.Pinned);
GZ>....//working

GZ>handle.Free(ob);
GZ>


Да, в настоящий момент так и делаю:

int[] result = _input.DungerousReadStructs<Int32>(entry.Length / 4);



public static T[] DungerousReadStructs<T>(this Stream input, int count) where T : struct
{
    if (count < 1)
        return new T[0];

    Array result = new T[count];
    Int32 entrySize = UnsafeTypeCache<T>.UnsafeSize;
    using (UnsafeTypeCache<byte>.ChangeArrayType(result, entrySize))
        input.EnsureRead((byte[])result, 0, result.Length);

    return (T[])result;
}



public static class TypeCache<T>
{
    public static readonly Type Type = typeof(T);
}

public static class UnsafeTypeCache<T>
{
    public static readonly Int32 UnsafeSize = GetSize();
    public static readonly UIntPtr ArrayTypePointer = GetArrayTypePointer();

    private static Int32 GetSize()
    {
        DynamicMethod dynamicMethod = new DynamicMethod("SizeOf", typeof(Int32), Type.EmptyTypes);
        ILGenerator generator = dynamicMethod.GetILGenerator();

        generator.Emit(OpCodes.Sizeof, TypeCache<T>.Type);
        generator.Emit(OpCodes.Ret);

        return ((Func<int>)dynamicMethod.CreateDelegate(typeof(Func<Int32>)))();
    }

    private static unsafe UIntPtr GetArrayTypePointer()
    {
        T[] result = new T[1];
        using (SafeGCHandle handle = new SafeGCHandle(result, GCHandleType.Pinned))
            return *(((UIntPtr*)handle.AddrOfPinnedObject().ToPointer()) - 2);
    }

    public static IDisposable ChangeArrayType(Array array, Int32 oldElementSize)
    {
        if (array.Length < 1)
            throw new NotSupportedException();

        SafeGCHandle handle = new SafeGCHandle(array, GCHandleType.Pinned);
        try
        {
            unsafe
            {
                UIntPtr* arrayPointer = (UIntPtr*)handle.AddrOfPinnedObject().ToPointer();
                UIntPtr arrayLength = *(arrayPointer - 1);
                UIntPtr arrayType = *(arrayPointer - 2);
                UInt64 arraySize = ((UInt64)arrayLength * (UInt64)oldElementSize);

                if (arraySize % (UInt64)UnsafeSize != 0)
                    throw new InvalidCastException();

                GC.Collect(2, GCCollectionMode.Forced, true);
                GC.WaitForFullGCComplete();

                try
                {
                    *(arrayPointer - 1) = new UIntPtr(arraySize / (UInt64)UnsafeSize);
                    *(arrayPointer - 2) = ArrayTypePointer;

                    GC.Collect(2, GCCollectionMode.Forced, true);
                    GC.WaitForFullGCComplete();

                    return new DisposableAction(() =>
                    {
                        *(arrayPointer - 1) = arrayLength;
                        *(arrayPointer - 2) = arrayType;
                        handle.Dispose();

                        GC.Collect(2, GCCollectionMode.Forced, true);
                        GC.WaitForFullGCComplete();
                    });
                }
                catch
                {
                    *(arrayPointer - 1) = arrayLength;
                    *(arrayPointer - 2) = arrayType;
                    throw;
                }
            }
        }
        catch
        {
            handle.SafeDispose();
            throw;
        }
    }
}


Работает. А вот если перед тем, как я дёрну "Free" не вернуть значения типа и длины к оригинальным, сборка мусора крашет приложение.
Вот и возник вопрос — отчего так? Помимо заголовка архива, тип объекта хранится где-то ещё? (н.п. в объявлении локальной переменной?). Можно ли и его изменить?

Также интересует — как в такой случае правильно описывать структуры? Marshaling работать не будет — это понятно. Соответственно, никаких ссылочных типов.
Вопрос: Как прикрутить собственный маршалинг, н.п. для корректного распознавания строк в той или иной кодировке? Вложенных массивов (аналог ByValStr, ByValArray)
Вопрос: Нужно ли структурам задавать аттрбиут StructLayout и будет ли он использоваться (или это часть механизма маршалинга?)
Вопрос: К чему приведёт использование подобного хака в совокупности со stackalloc?
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[3]: Изменение типа массива приводит к падению после сборки мусора
От: GlebZ Россия  
Дата: 22.08.15 19:12
Оценка: 5 (1)
Здравствуйте, Albeoris, Вы писали:

A>Работает. А вот если перед тем, как я дёрну "Free" не вернуть значения типа и длины к оригинальным, сборка мусора крашет приложение.

A>Вот и возник вопрос — отчего так? Помимо заголовка архива, тип объекта хранится где-то ещё? (н.п. в объявлении локальной переменной?). Можно ли и его изменить?
Когда вызывается сборка мусора — начинается дефрагментация памяти. При этом ищутся все переменные и указатели как в стэке, так и в регистрах процессора. Найдя каждую из этих переменных, GC переносит область памяти и меняет значение. Но когда ты работаешь с указателем — GC не имеет возможности его отследить.

A>Также интересует — как в такой случае правильно описывать структуры? Marshaling работать не будет — это понятно. Соответственно, никаких ссылочных типов.

A>Вопрос: Как прикрутить собственный маршалинг, н.п. для корректного распознавания строк в той или иной кодировке? Вложенных массивов (аналог ByValStr, ByValArray)
A>Вопрос: Нужно ли структурам задавать аттрбиут StructLayout и будет ли он использоваться (или это часть механизма маршалинга?)
A>Вопрос: К чему приведёт использование подобного хака в совокупности со stackalloc?
Непонятно что ты хочешь добиться. Если это чистый нет — то binaryserializer и вперед. Если это текстовой файл, то при чтении есть енкодеры на любой вкус. Если это файл с сишной структурой — то можно сериализовать/десериализовать через Marshal.Copy/вместе с StructLayout. А лучше BitConverter(чаще всего). Но никогда не пользоваться указателями на managed память.
Мне никогда не приходилось работать с указателями на managed память. Нужно иметь очень веские причины для этого. А лучше написать часть кода на С++.
Re[4]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 22.08.15 21:16
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Непонятно что ты хочешь добиться. Если это чистый нет — то binaryserializer и вперед. Если это текстовой файл, то при чтении есть енкодеры на любой вкус. Если это файл с сишной структурой — то можно сериализовать/десериализовать через Marshal.Copy/вместе с StructLayout. А лучше BitConverter(чаще всего). Но никогда не пользоваться указателями на managed память.

GZ>Мне никогда не приходилось работать с указателями на managed память. Нужно иметь очень веские причины для этого. А лучше написать часть кода на С++.

Всё очень просто: для того чтобы считать массив Int32, нужно считать массив байт и декодировать его в Int32 (как это, например, делает BinaryReader при помощи BitConverter'а).
Если нужно считать структуру, нужно считать массив байт, после чего отмарашалить его при помощи метода PtrToStructure.
В обоих случая создаются два массива. В обоих случаях происходит копирование данных между ними. Когда речь идёт о 100 объектов, это не играет роли, но когда их миллионы, подобные телодвижения негативным образом сказываются на производительности.

Вместо этого, я создаю массив структур (для простоты, возьмём Int32). Пришпиливаю его и меняю в памяти тип, заявляя, что массив на самом деле байтовый. При помощи упаковки подсовываю его в метод Stream.Read, который наполняет предоставленную область памяти байтами, после чего я меняю тип массива на первоначальный. Таким образом получая массив нужных мне структур без лишнего копирования и двойного выделения памяти.

Написать для этого CLI/C++ сборку можно, но мне точно также придётся передавать в методы указатели на управляемую память, а код будет делать ровно тоже самое. Зачем, если это можно сделать в C#?
Тогда уж правильнее было бы сразу писать на С++. Но пока авторы стандарта не откажутся от поддержки совместимости с Си, ноги моей в нём не будет.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[5]: Изменение типа массива приводит к падению после сборки мусора
От: GlebZ Россия  
Дата: 22.08.15 21:54
Оценка: +1
Здравствуйте, Albeoris, Вы писали:

A>Всё очень просто: для того чтобы считать массив Int32, нужно считать массив байт и декодировать его в Int32 (как это, например, делает BinaryReader при помощи BitConverter'а).

A>Если нужно считать структуру, нужно считать массив байт, после чего отмарашалить его при помощи метода PtrToStructure.
A>В обоих случая создаются два массива. В обоих случаях происходит копирование данных между ними. Когда речь идёт о 100 объектов, это не играет роли, но когда их миллионы, подобные телодвижения негативным образом сказываются на производительности.
Обычно такие негативы совершенно незаметны на фоне операций ввода/вывода на диск или сеть.

A>Вместо этого, я создаю массив структур (для простоты, возьмём Int32). Пришпиливаю его и меняю в памяти тип, заявляя, что массив на самом деле байтовый. При помощи упаковки подсовываю его в метод Stream.Read, который наполняет предоставленную область памяти байтами, после чего я меняю тип массива на первоначальный. Таким образом получая массив нужных мне структур без лишнего копирования и двойного выделения памяти.

Либо надежность и простота, либо быстрота и колдовство. Для первого случае и существует NetFramework.

Обычно сначала пишется программа а затем налаживается быстродействие с помощью профайлера, а не наоборот. Ошибки ты получишь точно, а вот профит вряд ли. Исключение только, алгоритмическая оптимизация (когда заранее ясна какая-то NP сложность)
Re[6]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 22.08.15 22:27
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Обычно такие негативы совершенно незаметны на фоне операций ввода/вывода на диск или сеть.

Безусловно. В моём случае, это не так. Нужно уложиться в 300 мсек. Оптимизирую всё, что можно.

GZ>Либо надежность и простота, либо быстрота и колдовство. Для первого случае и существует NetFramework.

С этим не соглашусь, так как недавно MS сами говорили, что одной из проблем .NET с которыми они будут бороться, является постоянное копирование памяти, что не позволяет C# тягаться в производительности с тем Си.
Безусловно, такие шутки не стоит делать в продакшен-коде, но почему бы не собрать подводные камни в домашнем проекте?

GZ>Обычно сначала пишется программа а затем налаживается быстродействие с помощью профайлера, а не наоборот. Ошибки ты получишь точно, а вот профит вряд ли. Исключение только, алгоритмическая оптимизация (когда заранее ясна какая-то NP сложность)

Профит получил — выиграл 450 мсек. Это очень много, когда речь идёт о пользовательском интерфейсе, который должен максимально быстро и без видимых задержек реагировать на действия пользователя.
На это все давным давно плюнули и по любому поводу втыкают "крутилки" ожидания. Опять же — в домашнем проекте. Ни в коем случае не призываю использовать данный подход в продакшен.

...да и вообще ни к чему не призываю: просто интересуюсь, чем это может грозить, и как ещё поизощрённее выстрелить себе в ногу, чтобы испытать чувство полного удовлетворения от создания того, что удалось реализовать ещё один грязный хак.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[5]: Изменение типа массива приводит к падению после сборки мусора
От: Sinix  
Дата: 22.08.15 23:09
Оценка: +1
Здравствуйте, Albeoris, Вы писали:

A>Всё очень просто: для того чтобы считать массив Int32, нужно считать массив байт и декодировать его в Int32 (как это, например, делает BinaryReader при помощи BitConverter'а).

A>Если нужно считать структуру, нужно считать массив байт, после чего отмарашалить его при помощи метода PtrToStructure.

Вообще-то для этого принято использовать memmapped files + ReadArray<T>. Либо чтение в буфер с последующим Marshal.Copy() как тут. Вам всё равно нужно два массива: один обрабатываем, в другой идёт асинхронное чтение, так что разницы никакой.

Ну и в крайнем случае можно использовать хак в моём сообщении выше, но вам оно не подойдёт — потеряете на синхронном чтении больше, чем сэкономите на копейках.
Re[7]: Изменение типа массива приводит к падению после сборки мусора
От: Sinix  
Дата: 22.08.15 23:14
Оценка: 1 (1) +2
Здравствуйте, Albeoris, Вы писали:

A>Профит получил — выиграл 450 мсек. Это очень много, когда речь идёт о пользовательском интерфейсе, который должен максимально быстро и без видимых задержек реагировать на действия пользователя.

Вот тут ошибка. Такие вещи надо решать асинхронными чтением-обработкой. Иначе первый же не в меру любопытный антивирус, полусдохший бэдблок или "заснувший" винт и приплыли. Ну и разумеется, "крутилку" показывать с запозданием в полсекунды (если операция не смогла), а не сразу.
Re[6]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 23.08.15 00:17
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Вообще-то для этого принято использовать memmapped files + ReadArray<T>. Либо чтение в буфер с последующим Marshal.Copy() как тут. Вам всё равно нужно два массива: один обрабатываем, в другой идёт асинхронное чтение, так что разницы никакой.


Для чего "для этого"? MemoryMappedFile, естественно, используется. Что такое ReadArray<T>? Ещё раз: зачем читать в буфер, если можно читать напрямую в целевой массив?
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[8]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 23.08.15 00:21
Оценка:
A>>Профит получил — выиграл 450 мсек. Это очень много, когда речь идёт о пользовательском интерфейсе, который должен максимально быстро и без видимых задержек реагировать на действия пользователя.
S>Вот тут ошибка. Такие вещи надо решать асинхронными чтением-обработкой. Иначе первый же не в меру любопытный антивирус, полусдохший бэдблок или "заснувший" винт и приплыли. Ну и разумеется, "крутилку" показывать с запозданием в полсекунды (если операция не смогла), а не сразу.

Безусловно. Нужно. Будет сделана. Но это не решение проблемы, а костыль, который помогает пользователю понять, что мы занимаемся чем-то полезным, а не просто висим. Но я не вижу смысла "делать правильно и безопасно", если можно сделать "небезопасно, но быстро", сократив время ожидания с 1.5 секунд до 300 мсек.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[7]: Изменение типа массива приводит к падению после сборки мусора
От: Sinix  
Дата: 23.08.15 10:17
Оценка: 106 (2)
Здравствуйте, Albeoris, Вы писали:

A> Что такое ReadArray<T>?

memMappedFile.CreateViewAccessor().ReadArray<SomeStruct>()


A>Ещё раз: зачем читать в буфер, если можно читать напрямую в целевой массив?

Потому что вам важна производительность и потому что чтение и обработку предыдущего буфера можно выполнять параллельно.


A>Безусловно. Нужно. Будет сделана. Но это не решение проблемы, а костыль, который помогает пользователю понять, что мы занимаемся чем-то полезным, а не просто висим.

Это не костыль, а подстраховка на случай "никогда такого не было, и вот опять". Как показывает практика, обернуть код в самописный хелпер аля using (BeginUiOperation()) { ... } куда проще, чем краснеть на презентации из-за внезапно™ засбоившего %подставить%.
Re[8]: Изменение типа массива приводит к падению после сборки мусора
От: Albeoris  
Дата: 23.08.15 12:30
Оценка: +1
S>
memMappedFile.CreateViewAccessor().ReadArray<SomeStruct>()

О, спасибо, возьму на заметку! Жаль, что его нет у ViewStream. Поменял вызовы в нескольких местах. К сожалению, не годится, когда файл нужно вначале распаковать.
"Хаос всегда побеждает порядок, поскольку лучше организован." (с) Терри Пратчетт
Re[5]: Изменение типа массива приводит к падению после сборки мусора
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.08.15 18:54
Оценка: 73 (2)
Здравствуйте, Albeoris, Вы писали:

A>Здравствуйте, GlebZ, Вы писали:


A>Всё очень просто: для того чтобы считать массив Int32, нужно считать массив байт и декодировать его в Int32 (как это, например, делает BinaryReader при помощи BitConverter'а).

A>Если нужно считать структуру, нужно считать массив байт, после чего отмарашалить его при помощи метода PtrToStructure.
A>В обоих случая создаются два массива. В обоих случаях происходит копирование данных между ними. Когда речь идёт о 100 объектов, это не играет роли, но когда их миллионы, подобные телодвижения негативным образом сказываются на производительности.
Ты пытаешься писать на C# как на C. На C — массив это указатель, и указатели можно приводить одни к другому без копирования данных. На C# массив это не указатель. Если тебе нужен указатель, то используй указатели. То есть ты читаешь массив байт, а потом его превращаешь в указатель и работаешь с указателем.
То что ты хочешь получить на выходе — не массив структур, а указатель на структуру. Тебе ничего не мешает это сделать.


A>Вместо этого, я создаю массив структур (для простоты, возьмём Int32). Пришпиливаю его и меняю в памяти тип, заявляя, что массив на самом деле байтовый. При помощи упаковки подсовываю его в метод Stream.Read, который наполняет предоставленную область памяти байтами, после чего я меняю тип массива на первоначальный. Таким образом получая массив нужных мне структур без лишнего копирования и двойного выделения памяти.

И зачем тебе это? Чтобы ты смог написать Array.Sort к полученному массиву? Не смеши.
Для быстродействия ты все равно будешь:
1) Не использовать linq, поэтому тебе не нужна реализация IEnumerable
2) Напишешь обычный for-loop, поэтому внутри for не будет разницы у тебя массив или указатель
Re[9]: Изменение типа массива приводит к падению после сборки мусора
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.08.15 19:02
Оценка: +1
Здравствуйте, Albeoris, Вы писали:

S>>
memMappedFile.CreateViewAccessor().ReadArray<SomeStruct>()

A>О, спасибо, возьму на заметку! Жаль, что его нет у ViewStream. Поменял вызовы в нескольких местах. К сожалению, не годится, когда файл нужно вначале распаковать.


Когда файл сначала надо распаковать, то используй UnmanagedMemoryStream, передавай ему указатель на массив структур, сконвертированный в Byte* и пиши в него при распаковке.
Re[7]: Изменение типа массива приводит к падению после сборки мусора
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.08.15 22:42
Оценка:
Здравствуйте, Albeoris, Вы писали:


A>Профит получил — выиграл 450 мсек. Это очень много, когда речь идёт о пользовательском интерфейсе, который должен максимально быстро и без видимых задержек реагировать на действия пользователя.

Когда речь идет о пользовательском интерфейсе нужны асинхронные операции, слава богу во всех современных UI других нет. Лочить UI чтением даже на 100мс смерти подобно.

Самый лучший вариант — потоковая обработка и feedback и в UI. Даже если у тебя многогигабайтный файл, то тебе совершенно не требуется его обрабатывать весь. Ты можешь обработать кусок и сразу его отдать в UI.
Re: Изменение типа массива приводит к падению после сборки мусор
От: desco США http://v2matveev.blogspot.com
Дата: 24.08.15 04:58
Оценка: 66 (4)
Здравствуйте, Albeoris, Вы писали:

Slice.net такое вроде умеет. Disclaimer: сам не пробовал
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.