Ruby (2.0) c 扩展 return 来自 c 函数的数组数组
Ruby (2.0) c extension return array of arrays from c function
我正在编写一个 Ruby c 扩展来使用 Accelerate
macos
框架执行线性代数计算。为了使用所有可用的内核,我还使用 rb_thread_call_without_gvl
来解锁全局 VM 锁。
我不是 C 专家所以请耐心等待。
想法是创建一个 ruby 对两个输入矩阵进行操作的方法
VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
在这种方法中,我创建了一个 struct
,然后将其传递给实际函数
void* matmat_mul_nogvl(void* inputMatricesPtr)
您可以从输出中看到,在 c 代码中一切都按预期工作,但我很难理解如何 return 从 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)
并使用以下 ruby 代码进行测试
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"
你的 matmat_mul
函数调用的最终值 return Qnil
,这基本上是 ruby 中的 return nil
。
因此您需要为矩阵创建 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;
我正在编写一个 Ruby c 扩展来使用 Accelerate
macos
框架执行线性代数计算。为了使用所有可用的内核,我还使用 rb_thread_call_without_gvl
来解锁全局 VM 锁。
我不是 C 专家所以请耐心等待。
想法是创建一个 ruby 对两个输入矩阵进行操作的方法
VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
在这种方法中,我创建了一个 struct
,然后将其传递给实际函数
void* matmat_mul_nogvl(void* inputMatricesPtr)
您可以从输出中看到,在 c 代码中一切都按预期工作,但我很难理解如何 return 从 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)
并使用以下 ruby 代码进行测试
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"
你的 matmat_mul
函数调用的最终值 return Qnil
,这基本上是 ruby 中的 return nil
。
因此您需要为矩阵创建 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;