Расширение Ruby (2.0) c возвращает массив массивов из функции c - PullRequest
0 голосов
/ 27 мая 2018

Я пишу расширение Ruby c для выполнения вычислений линейной алгебры с использованием фреймворка Accelerate macos.Чтобы использовать все доступные ядра, я также использую rb_thread_call_without_gvl для разблокировки глобальной блокировки виртуальной машины.

Я не эксперт по AC, поэтому, пожалуйста, потерпите меня.

Идея состоит в том, чтобы создатьметод ruby, который работает с двумя входными матрицами

VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)

. В этом методе я создаю struct, который затем передаю фактической функции

void* matmat_mul_nogvl(void* inputMatricesPtr)

Вы можете видеть из выводачто в коде c все работает как положено, но я пытаюсь понять, как вернуть окончательную матрицу (массив массивов) обратно из кода c.Окончательная матрица nil.Я думаю, что мне не хватает части, в которой я преобразую matC обратно в объект Ruby.

Пока это мой код (есть много отладок printf, чтобы проверить, что вычисления работают правильно)

#include <stdio.h>
#include <ruby.h>
#include <ruby/thread.h>
#include <time.h>
#include <Accelerate/Accelerate.h>
#include <math.h>

typedef struct {
    double *matrix;
    int nrows;
    int ncols;
}Matrix;

typedef struct {
    Matrix A;
    Matrix B;
    Matrix C;
}Transfer;


void* matmat_mul_nogvl(void* inputMatricesPtr)
{
    printf("The input matrix struct is at the address %p\n", inputMatricesPtr);
    int i,j;
    int cblas_order = 101;
    int cblas_transpose = 111;
    Transfer inputMatrices;
    inputMatrices = *(Transfer *)inputMatricesPtr;
    double *matA = inputMatrices.A.matrix;
    int rowsA = inputMatrices.A.nrows;
    int colsA = inputMatrices.A.ncols;
    double *matB = inputMatrices.B.matrix;
    int rowsB = inputMatrices.B.nrows;
    int colsB = inputMatrices.B.ncols;
    double *matC = inputMatrices.C.matrix;
    int rowsC = inputMatrices.C.nrows;
    int colsC = inputMatrices.C.ncols;

    printf("\nIn cblas_dgem\n");
    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    printf("%d-%02d-%d %02d:%02d:%02d - In cblas_dgem\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
    printf("Matrix A shape: (%d,%d)\n", rowsA, colsA);
    printf("Matrix B shape: (%d,%d)\n", rowsB, colsB);
    printf("Matrix C shape: (%d,%d)\n\n", rowsC, colsC);



    int lda = colsA;
    int ldb = colsB;
    int ldc = colsC;
    cblas_dgemm(cblas_order, cblas_transpose, cblas_transpose, rowsA, colsB, colsA, 1.0, matA, lda, matB, ldb, 1.0, matC, ldc);

    for (i=0; i<rowsA; i++)
    {
        for (j=0; j<colsA; j++)
        {
            printf("Matrix A Element(%d,%d)=%f\n", i, j, matA[i * colsA + j]);
        }
    }
    for (i=0; i<rowsB; i++)
    {
        for (j=0; j<colsB; j++)
        {
            printf("Matrix B Element(%d,%d)=%f\n", i, j, matB[i * colsB + j]);
        }
    }

    for (i=0; i<rowsC; i++)
    {
        for (j=0; j<colsC; j++)
        {
            printf("Matrix C Element(%d,%d)=%f\n", i, j, matC[i * colsC + j]);
        }
    }




    return NULL;
}


VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
{
    printf("\nIn matmul\n");
    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    printf("%d-%02d-%d %02d:%02d:%02d In matmul\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);


    int rowsA = RARRAY_LEN(matrixA);
    VALUE firstElement = rb_ary_entry(matrixA, 0);
    int colsA = RARRAY_LEN(firstElement);
    printf("Matrix A shape: (%d,%d)\n", rowsA, colsA);
    int rowsB = RARRAY_LEN(matrixB);
    firstElement = rb_ary_entry(matrixB, 0);
    int colsB = RARRAY_LEN(firstElement);
    printf("Matrix B shape: (%d,%d)\n", rowsB, colsB);

    int i,j;
    double *matA = (double *)malloc(rowsA * colsA * sizeof(double));
    double *matB = (double *)malloc(rowsB * colsB * sizeof(double));
    int rowsC = rowsA;
    int colsC = colsB;
    printf("Matrix C shape: (%d,%d)\n\n", rowsC, colsC);
    double *matC = (double *)malloc(rowsC * colsC * sizeof(double));



    VALUE rowA;
    for (i=0; i<rowsA; i++)
    {
        rowA = rb_ary_entry(matrixA, i);
        for (j=0; j<colsA; j++)
        {
            matA[i * colsA + j] = NUM2DBL(rb_ary_entry( rowA, j));
            printf("Matrix A Element(%d,%d)=%f\n", i, j, matA[i * colsA + j]);
        }
    }
    printf("\n");

    VALUE rowB;
    for (i=0; i<rowsB; i++)
    {
        rowB = rb_ary_entry(matrixB, i);
        for (j=0; j<colsB; j++)
        {
            matB[i * colsB + j] = NUM2DBL(rb_ary_entry( rowB, j));
            printf("Matrix B Element(%d,%d)=%f\n", i, j, matB[i * colsB + j]);
        }
    }
    printf("\nBefore MatMul Matrix C is:\n");
    for (i=0; i<rowsC; i++)
    {
        for (j=0; j<colsC; j++)
        {
            matC[i * colsC + j] = 0.0;
            printf("Matrix C Element(%d,%d)=%f\n", i, j, matC[i * colsC + j]);
        }
    }


    printf("\n");


    Matrix inputMatrixA = {matA, rowsA, colsA};
    Matrix inputMatrixB = {matB, rowsB, colsB};
    Matrix inputMatrixC = {matC, rowsC, colsC};

    Transfer inputMatrices = {inputMatrixA, inputMatrixB, inputMatrixC};

    rb_thread_call_without_gvl(matmat_mul_nogvl, &inputMatrices, NULL, NULL);

    printf("\nBack in MatMul Matrix C is:\n");
    for (i=0; i<rowsC; i++)
    {
        for (j=0; j<colsC; j++)
        {
            printf("Matrix C Element(%d,%d)=%f\n", i, j, matC[i * colsC + j]);
        }
    }

    free(matA);
    free(matB);
    return Qnil;
}

void Init_blasnogvl()
{
    VALUE rg = rb_define_module("RG");
    VALUE linalg = rb_define_module_under(rg, "LinearAlgebra");
    VALUE operation = rb_define_class_under(linalg, "Operation", rb_cObject);
    rb_define_method(operation, "matmat_mul", matmat_mul, 2);
}

Вы можете скомпилировать его с помощью следующего extconf.rb

require 'mkmf'
extension_name = 'blasnogvl'
have_framework('Accelerate')
create_makefile(extension_name)

и проверить его с помощью следующего кода рубина

require './blasnogvl'

puts "#{Time.now} - Started"

rows = 4
cols = 3
mat = Array.new(rows){Array.new(cols){rand}}
puts "#{Time.now} - Matrix generated"
mat[0] = [0.0, 1.0, 2.0]
mat[1] = [3.0, 4.0, 5.0]
mat[2] = [6.0, 7.0, 8.0]
mat[3] = [6.0, 7.0, 8.0]
puts mat.to_s

matA = mat
matB = mat.transpose
operation = RG::LinearAlgebra::Operation.new
matC = operation.matmat_mul(matA, matB)

puts "After calculation matA is"
puts matA.to_s

puts "After calculation matB is"
puts matB.to_s

puts "matC in ruby is"
puts matC.to_s
puts "#{Time.now} - Matrix calculated"

1 Ответ

0 голосов
/ 27 мая 2018

Конечное значение ваших matmat_mul вызовов функций return Qnil, это в основном return nil в ruby.

Так что вам нужно создать массив ruby ​​для матрицы, создать массив для хранения каждой строки данных, заполните эти строки и вставьте их в массив результатов.

Сначала можно достичь с помощью rb_ary_new, но, поскольку размеры уже известны, мы можем дать подсказку ruby, какой ее размер будет использовать rb_ary_new_capa,и затем заполните значения rb_ary_push, который является реализацией для метода Array#<<.

Следующий код должен сделать:

// Multiplication code above ...
VALUE matrixC = rb_ary_new_capa(rowsC);

for (i = 0; i < rowsC; i++) {
    VALUE rowC = rb_ary_new_capa(colsC);

    for (j = 0; j < colsC; j++)
        rb_ary_push(rowC, DBL2NUM(matC[i * colsC + j]);

    rb_ary_push(matrixC, rowC);
}

return matrixC;
...