Лучший способ обернуть указатель массива C в массив Chapel - PullRequest
0 голосов
/ 17 декабря 2018

При взаимодействии с C мне часто оказывается, что мне вручают указатель на массив.В настоящее время Chapel позволяет мне рассматривать этот указатель как 1D 0-индексированный массив.Однако есть случаи, когда я хотел бы рассматривать этот указатель как массив Чапеля (например, с многомерной областью).Какой самый идиоматичный способ добиться этого в Chapel?

Я мог бы попытаться сделать это, поместив указатель C в класс (с доменом) и определив this и these (serialи параллельные) методы, чтобы можно было индексировать и перебирать класс.Для реализации этого было бы полезно иметь функцию, которая отображает индекс в домене в местоположение с 0 индексами.Есть ли такая встроенная функция?

Ответы [ 3 ]

0 голосов
/ 18 декабря 2018

Мне было любопытно, смогу ли я обмануть массив Chapel, чтобы он ссылался на буфер, выделенный в C, без особых усилий.Я смог это сделать, но не горжусь результатом.В частности:

  • он использует функции, которые не предназначены для пользователя, поэтому может измениться или сломаться в любой момент в будущем
  • в настоящее время он работает только для прямоугольных массивов Часовни, которые начинают индексирование с 0в каждом измерении

Имея в виду эти предостережения, вот простой C-заголовок, который предоставляет несколько простых процедур, вызываемых из Chapel.Первый выделяет тривиальный массив из 9 элементов;вторая освобождает свой аргумент.

#include <stdlib.h>

double* getDataPtr() {
  double* dataPtr = (double*)malloc(9*sizeof(double));

  dataPtr[0] = 1.1;
  dataPtr[1] = 1.2;
  dataPtr[2] = 1.3;
  dataPtr[3] = 2.1;
  dataPtr[4] = 2.2;
  dataPtr[5] = 2.3;
  dataPtr[6] = 3.1;
  dataPtr[7] = 3.2;
  dataPtr[8] = 3.3;

  return dataPtr;
}

void freeDataPtr(double* ptr) {
  free(ptr);
}

, и вот код Чапеля, который вызывает его, затем принудительно указывает указатель C в существующий массив соответствующего размера и индексы на основе 0:

//                                                                        
// Declare a Chapel array.  Note that this program will only work as      
// written if it uses 0-based indexing.                                   
//                                                                        
var A: [0..2, 0..2] real;

//                                                                        
// testit.h is the C code above.  It defines a simple C stub that returns a pointer
// to floating point data.                                                            
//                                                                        
require "testit.h";

//                                                                        
// Here are the key routines that testit.h exposes back to Chapel to      
// get and free a pointer to floating point data.                         
//                                                                        
extern proc getDataPtr(): c_ptr(real);
extern proc freeDataPtr(ptr: c_ptr(real));

//                                                                        
// Grab the pointer from C                                                
//                                                                        
const myCPtr = getDataPtr();

//                                                                        
// Save two pointer values defined in A's descriptor.  Note that these    
// are not part of its public interface, so are not recommended for       
// typical users and could change / break at any future point.            
//                                                                        
const saveData = A._value.data;
const saveShiftedData = A._value.shiftedData;

//                                                                        
// Replace these pointers with the one we got from C.                     
//                                                                        
A._value.data = (myCPtr: _ddata(real));
A._value.shiftedData = (myCPtr: _ddata(real));

//                                                                        
// print out A, "proving" that we're referring to the data from C         
//                                                                        
writeln(A);

//                                                                        
// restore the original pointers to avoid having Chapel try to free       
// the C memory / leak the Chapel memory.                                 
//                                                                        
A._value.data = saveData;
A._value.shiftedData = saveShiftedData;

//                                                                        
// Free the C data                                                        
//                                                                        
freeDataPtr(myCPtr);

Вывод заявления writeln(A) Чапеля:

1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3

Я думаю, что было бы вполне разумно подать запрос на добавление функции на странице *1039* GitHub Чапеля , предлагающей лучшего пользователяинтерфейс для принятия указателя C, как этот, хотя и в более хорошем и официальном виде.

0 голосов
/ 18 декабря 2018

Немного другой подход может быть ниже.Недостатком здесь является то, что это не массив Chapel, но он позволяет индексировать / выполнять итерации так же, как это делает массив Chapel.Это не обрабатывает шаги или ненулевое выравнивание, и проверка границ не выполняется.

prototype module CPtrArray {

  use SysCTypes;

  record CPtrArray {
    type eltType;
    param rank : int;
    const first : rank*int;
    const blk : rank*int;

    var data : c_ptr(eltType);


    proc init(type t, D : domain, ptr : c_ptr(t)) {
      this.eltType = t;
      this.rank = D.rank;
      this.first = D.first;
      var blktmp : rank*int;
      blktmp(rank) = 1;
      if (rank > 1) {
        for param idim in (rank-1)..1 by -1 {
          blktmp(idim) = blktmp(idim+1)*D.shape(idim+1);
        } 
      }
      this.blk = blktmp;
      this.complete();
      data = ptr;
    }

    proc getDataOffset(ndx : rank*int) : int {
      var offset = ndx(rank)-first(rank);
      if (rank > 1) {
        for param idim in 1..(rank-1) do
          offset += (ndx(idim)-first(idim))*blk(idim);
      }
      return offset;
    }

    inline proc this(ndx : rank*int) ref {
      return data[getDataOffset(ndx)];
    }

    inline proc this(i:int ...rank) ref {
      return this(i);
    }

    // Should provide iterators as well.                 
  }

}

Простая тестовая программа, которая использует это:

use CPtrArray;

var A : [0.. #10] real;
forall ii in 0.. #10 do A[ii] = ii+1.0;

writeln(A);

var ptr = c_ptrTo(A[0]);

// Code for testing the array class goes here
const D = {1.. #5, 3.. #2};
var A1 = new CPtrArray(real, D, ptr);

for ndx in D {
  writeln(ndx," ",A1[ndx]);
}
0 голосов
/ 17 декабря 2018

К сожалению, не существует функции, которая работает для каждого домена.DefaultRectangularArr использует метод с именем getDataIndex под прикрытием, которое выполняет это вычисление, и похоже, что другие типы массивов полагаются на аналогичный метод, определенный во внутреннем классе хранения.Похоже, что они не доступны на самих доменах.Я подозреваю, что полагаться на что-либо из этого было бы нецелесообразно, так как в любом случае они могут быть изменены как часть корректировки реализации.

Мы надеемся, что в конечном итоге указатели, подобные описываемым вами, могут быть обернуты в массивы часовни, используя что-то вродеmakeArrayFromPtr функция, определенная для взаимодействия.К сожалению, сегодня эта функция поддерживает только индексированные массивы 1D 0, но в настоящее время ведется работа по расширению нашей поддержки совместимости массивов.Я ожидаю, что эта функция настроит свои аргументы или определит другую версию для многомерных массивов, и мы все еще это выясняем.

...