[обновление 2011-07-19]
доступна новая версия: http://www.mediafire.com/file/74fxj7u1n0ppcq9/MemStorageDemo-6639584-2011_07_19-12_47_00.zip
Я все еще пытаюсь отладить некоторые из повторных операций, что раздражает, но из свежей сессии xUnit я могу запустить тест, который создает 10 миллионов объектов (мне кажется, у меня сейчас сокращен размер строки для тестирования, но у меня было 10 миллионов строк с переменной длиной от 3 до 15 байт, у меня еще не было возможности попробовать что-то большее. В моей системе я шел от нагрузки примерно 1,95 г до 2,35 г с 10 миллионами объектов, и я до сих пор ничего не сделал со строками, кроме очень простого вспомогательного класса, который использует фактические управляемые строки.
в любом случае, так что я думаю, что это довольно хорошо работает, хотя определенно еще предстоит оптимизировать резервное хранилище, и я также думаю, что с итераторами можно проделать некоторую работу, если необходимо, в зависимости от того, сколько данных вы процесс сразу. к сожалению, просто не смогу взглянуть на это снова до завтрашнего или завтрашнего дня.
В любом случае, вот основные идеи:
MemoryArray
класс: использует неуправляемую память, выделенную Marhsal.AllocHGlobal()
для хранения структур. я тестировал с одним большим MemoryArray
, но для каждого члена есть только несколько полей, и я думаю, что пока вы сохраняете размер массива достаточно большим, для его разделения не будет большой разницы в потреблении памяти. MemoryArray
реализует IEnumerable<MemoryArrayItem>
, что я и использовал для заполнения массивов при тестировании.
MemoryArray
предназначен для хранения фрагментов данных регулярного размера из любого объекта, для которого выполняется резервное копирование. Есть некоторые вещи, которые вы можете сделать с помощью перечислителей с помощью математики указателей, которые я еще не реализовал. В настоящее время я каждый раз возвращаю новые объекты, так что я считаю, что это большой кусок. в классе прототипов, на котором я основывал этот прототип, я мог использовать очень регулярную указательную математику для выполнения обходов, однако я думаю, что способ, которым я это делал, был бы в основном полезен для очень быстрых обходов и, вероятно, не для взаимодействия.
MemoryArray
имеет только стандартный индексатор, который захватывает запрошенный элемент с помощью математического указателя на Int
Ptr, который представляет заголовок неуправляемых данных, которые выделяются при построении. Я также реализовал условный двумерный индексатор, где вы можете передать массив целых чисел, представляющих измерения, в массив, а затем выполнить над ним A [x, y]. это всего лишь небольшой пример того, как это может работать.
одна вещь, которую я еще не реализовал, - это какое-то подразделение, но я думаю, что подраздел подходит для проекта, поэтому я, вероятно, осуществлю это, когда у меня будет шанс.
MemoryArrayEnumerator
класс: я решил реализовать фактические перечислители вместо функций перечислителя. класс перечислителя в основном просто берет MemoryArray
, а затем предоставляет функции-счетчики для перечисления фактических MemoryArrayItem
объектов.
MemoryArrayItem
class: этот класс не делает ничего другого, кроме как содержит информацию о подходящем указателе, основанную на начале и позиции в массиве. это конкретные классы, которые реализуются поверх (в стороне от?) этого объекта, которые фактически выполняют указатель для вывода данных.
затем есть еще несколько классов поддержки, MemoryStringArray
- это резервный блок памяти переменного размера, который в настоящее время создает только строки, а также есть класс автоматического удаления (AutoDisposer
) и универсальный класс для обработки. прикрепляет и отсоединяет. (AutoReference<>
).
Теперь на вершине этого фундамента находятся специальные классы. каждый из трех типов (массив / перечислитель / элемент) реализован специально для объектов, которые вы просматриваете. в более масштабной версии этого проекта, от которой я отказался и для которой это стало шипом, у меня есть более общая обработка смещений и тому подобное, так что вы не так привязаны к конкретным классам, но даже если они есть, они довольно полезно; моей первоначальной реализацией были все конкретные классы без реальной базы.
В настоящее время у меня все они реализованы как отдельные классы, на которые вы передаете ссылки; Итак, класс TestArray
передается классу MemoryArray
в своем конструкторе. то же самое с перечислителем и товаром. я знаю, что там будет сохранена некоторая вычислительная мощность, и я думаю, что есть хорошая возможность и для экономии места, если я найду хороший способ реализовать их как потомков, а не просто иметь копию базовых классов. хотя сначала я хотел получить базовое представление об этом, и это, казалось, было самым простым способом. проблема в том, что это еще один слой косвенности.
TestArray
и TestArrayEnumerator
оказались не слишком многим, кроме как просто пройти через функции MemoryArray
и MemoryArrayEnumerator
. главная проблема в этих классах - просто вырезать указатели и распределить их по элементам, которые его используют.
но, таким образом, TestArrayItem - это место, где указатели фактически переключаются на реальные данные; вот файл я отрубил большой раздел комментариев, в котором рассматриваются некоторые варианты, чтобы лучше обращаться с резервным хранилищем переменной длины (все еще в фактическом файле по приведенной выше ссылке), и, пожалуйста, извините за множество комментариев, где я оставляю свои заметки о о чем я думаю, работая над этим:)
TestArrayItem.cs
// ----------------------------------------------
// rights lavished upon all with love
// see 'license/unlicense.txt'
// ♥ 2011, shelley butterfly - public domain
// ----------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MemStorageDemo
{
public unsafe class TestArrayItem
{
public static MemoryStringArray s_MemoryArrayStore = new MemoryStringArray();
public static int TestArrayItemSize_bytes =
sizeof(double) * 2
+ sizeof(int)
+ sizeof(int);
// hard-coding here; this is another place that things could be a little more generic if you wanted and if
// performance permitted; for instance, creating a dictionary of offsets based on index. also, perhaps
// a dictionary with key strings to allow indexing using field names of the object.
private enum EFieldOffset
{
DoubleTheFirstOffset = 0,
DoubleTheSecondOffset = 8,
IntTheFirstOffset = 16,
StringTheFirstHandleOffset = 20
}
private MemoryArrayItem myMemoryArrayItem;
private MemoryStringArray myStringStore;
// constructor that uses the static string array store
public TestArrayItem(MemoryArrayItem parMemoryArrayItem) :
this(parMemoryArrayItem, s_MemoryArrayStore)
{
}
// constructor for getting the item at its memory block without any initialization (e.g. existing item)
public TestArrayItem(MemoryArrayItem parMemoryArrayItem, MemoryStringArray parStringStore)
{
myMemoryArrayItem = parMemoryArrayItem;
myStringStore = parStringStore;
}
// constructor for geting the item at its memory block and initializing it (e.g. adding new items)
public TestArrayItem(MemoryArrayItem parMemoryArrayItem, double parDoubleTheFirst, double parDoubleTheSecond, int parIntTheFirst, string parStringTheFirst)
{
myMemoryArrayItem = parMemoryArrayItem;
DoubleTheFirst = parDoubleTheFirst;
DoubleTheSecond = parDoubleTheSecond;
IntTheFirst = parIntTheFirst;
StringTheFirst = parStringTheFirst;
}
// if you end up in a situation where the compiler isn't giving you equivalent performance to just doing
// the array math directly in the properties, you could always just do the math directly in the properties.
//
// it reads much cleaner the way i have it set up, and there's a lot less code duplication, so without
// actually determining empirically that i needed to do so, i would stick with the function calls.
private IntPtr GetPointerAtOffset(EFieldOffset parFieldOffset)
{ return myMemoryArrayItem.ObjectPointer + (int)parFieldOffset; }
private double* DoubleTheFirstPtr
{ get { return (double*)GetPointerAtOffset(EFieldOffset.DoubleTheFirstOffset); } }
public double DoubleTheFirst
{
get
{
return *DoubleTheFirstPtr;
}
set
{
*DoubleTheFirstPtr = value;
}
}
private double* DoubleTheSecondPtr
{ get { return (double*)GetPointerAtOffset(EFieldOffset.DoubleTheSecondOffset); } }
public double DoubleTheSecond
{
get
{
return *DoubleTheSecondPtr;
}
set
{
*DoubleTheSecondPtr = value;
}
}
// ahh wishing for a preprocessor about now
private int* IntTheFirstPtr
{ get { return (int*)GetPointerAtOffset(EFieldOffset.IntTheFirstOffset); } }
public int IntTheFirst
{
get
{
return *IntTheFirstPtr;
}
set
{
*IntTheFirstPtr = value;
}
}
// okay since we're using the StringArray backing store in the example, we just need to get the
// pointer stored in our blocks, and then copy the data from that address
private int* StringTheFirstHandlePtr
{ get { return (int*)GetPointerAtOffset(EFieldOffset.StringTheFirstHandleOffset); } }
public string StringTheFirst
{
get
{
return myStringStore.GetString(*StringTheFirstHandlePtr);
}
set
{
myStringStore.ModifyString(*StringTheFirstHandlePtr, value);
}
}
public void CreateStringTheFirst(string WithValue)
{
*StringTheFirstHandlePtr = myStringStore.AddString(WithValue);
}
public override string ToString()
{
return string.Format("{0:X8}: {{ {1:0.000}, {2:0.000}, {3}, {4} }} {5:X8}", (int)DoubleTheFirstPtr, DoubleTheFirst, DoubleTheSecond, IntTheFirst, StringTheFirst, (int)myMemoryArrayItem.ObjectPointer);
}
}
}
Итак, это настоящая магия; просто в основном реализуя функции, которые вычисляют правильные указатели на основе информации о полях. в сущности, я думаю, что это неплохой кандидат на генерацию кода, если предположить, что все работает правильно. я скрывал необходимость ручного управления памятью с помощью указателей автоматического ввода, и я думаю, что в конечном итоге отладка будет стоить ...
в любом случае, вот и все, надеюсь, я вернусь к Интернету этим вечером или завтра и сделаю все возможное, чтобы зарегистрироваться. К сожалению, собираюсь вернуть кабельный модем обратно в кабельную компанию, поэтому не буду быть в бизнесе, если мы не пойдем в Макдональдс или что-то еще :) надеюсь, что это была какая-то помощь; я буду отлаживать проблемы с ним, чтобы была хотя бы функциональная база; я знаю, что это не первый раз, когда я думаю о написании такой библиотеки, и я думаю, что другие тоже.
предыдущий контент
Я использовал нечто подобное для использования библиотеки COM, которую я создал, для взаимодействия с предварительно скомпилированной Win32 / 64 FFTW dll. для этого вопроса нам действительно нужно что-то более общее, чем то, что у меня было, поэтому я начал работать над чем-то на прошлой неделе, что было бы достаточно в качестве приличной универсальной библиотеки для этих типов использования, с расширяемым управлением памятью, многомерностью, нарезкой, и т. д.
ну, я наконец-то вчера осознал, что (а) пройдет еще несколько дней, прежде чем он будет готов, и (б) мне нужно было решение с шипами, чтобы выяснить некоторые из последних битов в любом случае. Итак, я решил просто сделать первый разрез на более низком уровне абстракции, чтобы попытаться удовлетворить потребности, затронутые в вашем вопросе. к сожалению, мы собираемся переехать, и мне нужно кое-что собрать, но я думаю, что этого достаточно, чтобы ответить на ваш вопрос.
Я собираюсь продолжить работу над примером на этой неделе, и я обновлю здесь новый код, и я постараюсь извлечь из него достаточно информации, чтобы сделать пост, объясняющий это, но если вам все равно интересно Последнее, что я, вероятно, смогу опубликовать до позже на этой неделе:
http://www.mediafire.com/file/a7yq53ls18q7bvf/EfficientStorage-6639584.zip
это просто решение VS2010 с базовыми потребностями для выполнения того, что, по моему мнению, отвечает вашим потребностям.есть много комментариев, но не стесняйтесь задавать вопросы, и я проверю, как только у меня будет Интернет ... в любом случае, это был забавный проект.
после завершения примера, который я собираюсь закончитьотключить полную библиотеку первой итерации и выпустить ее куда-нибудь;я обновлю здесь ссылку, когда она у меня будет.
обязательное предупреждение: оно компилируется, но не было проверено;Я уверен, что есть проблемы, это довольно большая тема.