Я пишу расширение 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"