2015-11-26 5 views
0

У меня есть двумерная матрица Олеварианта удвоений xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble);.Самый быстрый способ конвертировать двумерный массив двустворчатого двумерного динамического двумерного массива

Я хочу преобразовать (как можно быстрее) в простой 2D динамический массив DestArray : array[0..1] of array of Double с использованием move().

В этом процессе решения этого вопроса я использовал Count=5 с ожидаемыми 40 байтами на измерение. Но я обнаружил, что адрес diff между Pointer(DestArray[0]) и Pointer(DestArray[1]) составляет 56 байт.

Итак, каковы 16 байтов между ними? Я не знаю о первых 8 байтах, но последние 8 байтов - это информация о размере массива.

В результате move() не работает за один шаг Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove);.

Я нашел способ, используя два отдельных шага, но у меня все еще есть чувство, что это можно сделать более элегантным.

Вопросы:

  1. Есть ли на самом деле гораздо более простой и легкий быстрый способ?
  2. Есть ли какие-либо директивы компилятора или аналогичные, которые могут изменить макет памяти динамического массива, чтобы позволить move() работать за один шаг?
  3. Из любопытства, каковы первые 8 байтов в промежутке между Pointer(DestArray[0]) и Pointer(DestArray[1])?

Вот полный фрагмент кода:

procedure TForm1.FormCreate(Sender: TObject); 

procedure PrintEqualityVerdictLine(value1 : Double; value2 : Double); 
const 
    cEqualVerdict : array[Boolean] of String = ('!!!Not Equal!!!', 'Equal'); 
begin 
    Memo1.Lines.Add(FloatToStr(value1) + ' =? ' + FloatToStr(value2) + '  ' + cEqualVerdict[ SameValue(value1, value2, 0.001) ]); 
end; 

procedure VariantArrayOfDoubleToDynamicDoubleArray; 
var 
    xyInput : OleVariant; 
    Count: Integer; 
    n: Integer; 

    DestArray : packed array[0..1] of packed array of Double; 

    V_Ptr: PVarData; 
    VarArrayData: PVarData; 
    BytesToMove: Integer; 

    SourceBytePtr : PByte; 
    BytesToMovePerColumn: Integer; 
    DestBytePtr: PByte; 
begin 
    // create 2 column OleVariant array: 
    Count := 5; 
    xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble); 

    // fill test data: 
    for n := 0 to Count-1 do 
    begin 
    xyInput[n, 0] := 1.0 * n; 
    xyInput[n, 1] := 2.0 * Count + n; 
    end; 

    SetLength(DestArray[0], Count); 
    SetLength(DestArray[1], Count); 

    V_Ptr := PVarData(@xyInput); 
    if ((V_Ptr^.VType and $F000) = varArray) and 
    ((V_Ptr^.VType and varTypeMask) = varDouble) 
    then 
    begin 
    VarArrayData := PVarData(V_Ptr^.VArray^.Data); 
    BytesToMovePerColumn := Count * V_Ptr^.VArray^.ElementSize; 
    BytesToMove := BytesToMovePerColumn*V_Ptr^.VArray^.DimCount; 

    // print 16 discovered intermediate bytes of the DestArray: 
    DestBytePtr := Pointer(DestArray[0]); 
    Inc(DestBytePtr, BytesToMovePerColumn); 
    for n := 1 to 16 do 
    begin 
     Memo1.Lines.Add('byte['+IntToStr(n) + ']: ' + IntToStr(DestBytePtr^)); 
     Inc(DestBytePtr); 
    end; 

// This does NOT work: col 1 of arr gets offset due to 16 discovered intermediate bytes: 
//  Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove); 

// This works: 
    SourceBytePtr := PByte(VarArrayData); 
    Move(SourceBytePtr^, Pointer(DestArray[0])^, BytesToMovePerColumn); 
    Inc(SourceBytePtr, BytesToMovePerColumn); 
    Move(SourceBytePtr^, Pointer(DestArray[1])^, BytesToMovePerColumn); 
    end; 

    // print: 
    Memo1.Lines.Add('VariantArrayOfDoubleToDoubleArray:'); 

    Memo1.Lines.Add('col 0:'); 
    for n := 0 to Count - 1 do 
    PrintEqualityVerdictLine(xyInput[n, 0], DestArray[0, n]); 
    Memo1.Lines.Add(''); 

    Memo1.Lines.Add('col 1:'); 
    for n := 0 to Count - 1 do 
    PrintEqualityVerdictLine(xyInput[n, 1], DestArray[1, n]); 
end; 

begin 
    Memo1.Lines.Clear; 
    VariantArrayOfDoubleToDynamicDoubleArray; 
end; 

ответ

4

Но я обнаружил, что адрес различий между Pointer(DestArray[0]) и Pointer(DestArray[1]) составляет 56 байт.

Этого можно ожидать. Ну, чтобы быть более ясным, нет никаких оснований ожидать, что DestArray[0] и DestArray[1] укажут на соседние блоки памяти.

Ваш тип

array[0..1] of array of Double; 

Обратите внимание, что я удалил packed ключевое слово, которое игнорируется, когда применяется к массивам. Здесь у вас есть массив, содержащий два указателя. Эти два указателя являются независимыми. Посмотрите, как вы распределяете динамические массивы.

SetLength(DestArray[0], Count); 
SetLength(DestArray[1], Count); 

Каждый вызов SetLength результатов в отдельном выделении кучи. Никакой причины для смещения памяти. Прежде чем перейти к вопросу о том, что динамический массив имеет дополнительный блок метаданных, хранящихся непосредственно перед полезной нагрузкой массива, и каждый блок памяти имеет свои собственные метаданные, используемые диспетчером памяти. Таким образом, даже если диспетчер памяти случайно случайно обслуживал соседние блоки памяти, метаданные будут располагаться между двумя массивами. Кстати, эти метаданные диспетчера памяти являются ответом на ваш вопрос 3.

В технических терминах, что у вас здесь, в DestArray есть jagged array. С другой стороны, вы ищете многомерный массив. Delphi фактически не поддерживает динамические многомерные массивы. Все, что у вас есть, - это зубчатые массивы. Если вам нужен непрерывный блок памяти, вам нужно будет выделить одномерный блок памяти и самостоятельно выполнить вычисление индекса.

Итак, если вы продолжаете работу с зубчатыми массивами, вам нужно будет выполнить одну копию для каждого внутреннего массива. Если вы переключитесь на линейный массив, вы можете уйти с одной копией, но вам придется выполнять собственное индексирование. Конечно, индексирование очень легко сделать, и это может быть эффективным. Наконец, вполне вероятно, что вы могли бы выделить линейный массив в Delphi раньше времени и поместить указатель на этот массив в свой вариант и тем самым полностью исключить копию.

+0

Спасибо за ваши материалы и мое первое знакомство с зубчатым массивом. Было бы неплохо, если бы метаданные были помещены в конец динамического массива. Но я дам ему выстрел с помощью одного длинного массива. –

+0

Это не помогло бы. Тогда это будет в конце первого массива. Кроме того, мета-данные менеджера памяти. Плюс нет гарантии, что блоки смежны. –

 Смежные вопросы

  • Нет связанных вопросов^_^