BobQQ has asked for the wisdom of the Perl Monks concerning the following question:
Hi everybody,
I have a script that does rather a lot of number crunching, so I thought I might be able to speed it up by using the Inline::C module. Unfortunately, I can't figure out how pass a couple of arrays to my C subroutine, or how to get a new one out (all the arrays are of known size). Can anyone suggest a simple way to do this?
Thanks in advance
Re: Arrays and Inline C
by zentara (Archbishop) on Aug 01, 2008 at 16:42 UTC
|
I'm not an Inline expert, but here are some examples I have from previous experimenting. The thing you need to consider is the type of data in the arrays, Perl takes care of it for you automatically, but in C, you need to be exact.
| [reply] [d/l] [select] |
Re: Arrays and Inline C (pack)
by tye (Sage) on Aug 02, 2008 at 02:01 UTC
|
For some reason, people tend to think of accessing Perl arrays from C code. I think you'll run into a lot fewer bugs if you instead deal with C arrays from Perl code, since the code is so trivial to write:
use Inline ( 'C', <<END_C );
void _munge( char *outlist, int size, char *inlist1, char *inlist2 )
{
double *output = (double *) outlist;
double *list1 = (double *) inlist1;
double *list2 = (double *) inlist2;
/* use the "arrays" just like normal for C code: */
int i;
for( i= 0; i < size; ++i ) {
output[i] = combine( list1[i], list2[i] );
}
}
END_C
sub munge {
my( $l1, $l2 ) = @_;
my $size = 0+@$l1;
my $out = "\0" x ( $size * length pack "d", 0.0 );
my $pack1 = pack "d$size", @$l1;
my $pack2 = pack "d$size", @$l2;
_munge( $out, $size, $pack1, $pack2 );
return unpack "d$size", $out;
}
| [reply] [d/l] |
|
Thanks for the example - but somehow the trick doesn't seem to work:
use Inline 'C', <<'END_C'
void _munge( char *outlist, int size, char *inlist1, char *inlist2 )
{
double *output = (double *) outlist;
double *list1 = (double *) inlist1;
double *list2 = (double *) inlist2;
int i;
for( i= 0; i < size; ++i ) {
printf("%f %f\n", inlist1[i], inlist2[i]);
}
}
END_C
;
my $size = 3;
my $out = "\0" x ( $size * length pack "d", 0.0 );
my $pack1 = pack "d$size", 1, 2, 3;
my $pack2 = pack "d$size", 4, 5, 6;
_munge( $out, $size, $pack1, $pack2 );
This prints
0.000000 0.000000
0.000000 0.000000
0.000000 0.000000
I expected it to print the numbers passed in.
What happened? | [reply] [d/l] [select] |
|
#!/usr/bin/perl -w
use strict;
use Inline 'C', <<'END_C';
void _munge(
char *outbuf,
int size,
char *inbuf1,
char *inbuf2 )
{
double *output = (double *) outbuf;
double *list1 = (double *) inbuf1;
double *list2 = (double *) inbuf2;
int i;
for( i= 0; i < size; ++i ) {
printf( "%f %f\n", list1[i], list2[i] );
output[i]= list1[i] * list2[i];
}
}
END_C
my $size = 3;
my $out = "\0" x ( $size * length pack "d", 0.0 );
my $pack1= pack "d$size", 1, 2, 3;
my $pack2= pack "d$size", 4, 5, 6;
_munge( $out, $size, $pack1, $pack2 );
print $_, $/ for unpack "d$size", $out;
$ perl double.pl
1.000000 4.000000
2.000000 5.000000
3.000000 6.000000
4
10
18
| [reply] [d/l] [select] |
Re: Arrays and Inline C
by spivey49 (Monk) on Aug 01, 2008 at 16:39 UTC
|
Have a look at the Cookbook
Basically you pass and return the variable much like you would call any other function
use Inline C;
$var = "something";
somefunc($var);
__END__
__C__
somefunc(char* str){
.
.
return something;
}
| [reply] [d/l] |
Re: Arrays and Inline C
by tod222 (Pilgrim) on Aug 01, 2008 at 22:14 UTC
|
| [reply] |
Re: Arrays and Inline C
by syphilis (Archbishop) on Aug 02, 2008 at 01:31 UTC
|
zentara provided an example of pass by reference. You can also pass by array - but, as with perl, C sees the argument list as one big array, not 2 separate arrays. So you need to supply the array sizes as arguments.
In the demo below, both arrays are the same size, so it's only necessary to supply one "size" argument.
use warnings;
use Devel::Peek;
use Inline C => <<'EOC';
AV * foo(SV * x, ...) {
Inline_Stack_Vars;
long array_size, i;
AV * ret = newAV();
array_size = SvUV(Inline_Stack_Item(0));
/* check that array_size matches our expectations,
based on the total number of arguments supplied */
if(Inline_Stack_Items != (2 * array_size) + 1)
croak("Mismatch in number of arguments supplied to foo()");
/* for efficiency, make the array big enough in one fell swoop */
av_extend(ret, array_size - 1);
/* multiply corresponding array elements and push the result into ret
+*/
for(i = 1; i <= array_size; i++)
av_push(ret, newSVnv(SvNV(Inline_Stack_Item(i)) *
SvNV(Inline_Stack_Item(i + array_size)))
+);
return ret;
}
EOC
my @num1 = (2.2, 3.3, 4.4,5.5);
my @num2 = (6.6, 7.7, 8.8, 9.9);
$arref = foo(scalar(@num1), @num1, @num2);
print "@$arref\n";
# prints 14.52 25.41 38.72 54.45
If the values in the arrays are unsigned integers, replace SvNV with SvUV, and replace newSVnv with newSVuv.
If the values are signed integers, replace SvNV with SvIV, and replace newSVnv with newSViv.
Cheers, Rob | [reply] [d/l] |
|
|