There is an ongoing outage on the primary CPAN mirror. It is possible to work around the issue by using MetaCPAN as a mirror.

NAME

Task::MemManager::View::PDL - Create PDL views of Task::MemManager buffers

VERSION

version 0.02

SYNOPSIS

use Task::MemManager::View::PDL; ## this will also import Task::MemManager

my $length = 10;
my $mem = Task::MemManager->new(
  $length, 1,
  {
      init_value => 40,
      death_stub => sub {
          my ($obj_ref) = @_;
          printf "\n ======> Killing 0x%8x <======\n", $obj_ref->{identifier};
      },
      #allocator => 'CMalloc',
  }
);
# Create a PDL view of a Task::MemManager buffer
my $pdl_view =  $mem->create_view('PDL');

# The same region as uint16_t
my $pdl_intview =
$mem->create_view( 'PDL', { pdl_type => 'short', view_name => 'PDL_short' } );

DESCRIPTION

This module provides an interface to create PDL views of Task::MemManager buffers. It uses Inline::C to interface with the PDL C API. The created views will share memory with the Task::MemManager buffers, so any changes made to the view may be reflected in the buffer and vice versa. The emphasis is on may because PDL may decide to copy the data to a new memory location for its own purposes, e.g. during a transformation operation. In that case, the view will no longer share memory with the original buffer. This opens up some interesting possibilities, e.g. using PDL to transform data in a buffer and then having Task::MemManager manage the transformed data. Refer to the examples to see what is possible.

METHODS

create_view

Usage       : $view =  $buffer->create_view($buffer_address, $buffer_size \%options);
Purpose     : Create a PDL view of the specified type for the buffer
Returns     : The created view (a PDL ndarray) or undef on failure
              The view is created using the buffer's memory.
Parameters  : $buffer_address - address of the buffer's memory
              $buffer_size    - size of the buffer's memory in bytes
              \%options - hash reference with options for the view. The
              supported options are:
                  pdl_type - the PDL type of the view. Default is 'byte'.
                             See the %PDL_types hash for the supported types.
                  dims     - array reference with the dimensions of the view.
                             Default is a one-dimensional view with as many
                             elements as fit in the buffer.
Throws      : The function will die if an unknown PDL type is specified.
              It will warn (but not die) if the buffer size is not a
              multiple of the requested number of elements times the
              element size.
Comments    : Returns undef if the view creation fails for any reason (e.g.
              stuff happening inside PDL).
              Warnings will be generated if DEBUG is set to a non-zero value.

The standard PDL types are supported during view creation. The size of these types is hardcoded in the %PDL_types hash, except for 'indx', 'ldouble', and 'cldouble', which are determined at runtime using the sizeof operator. The sizes below are based on GCC in an x86_64 environment and may need to be adjusted for other compilers:

my %PDL_types = (
    sbyte     => [ 0,  1 ],
    byte      => [ 1,  1 ],
    short     => [ 2,  2 ],
    ushort    => [ 3,  2 ],
    long      => [ 4,  4 ],
    ulong     => [ 5,  4 ],
    indx      => [ 6,  4 ],
    ulonglong => [ 7,  8 ],
    longlong  => [ 8,  8 ],
    float     => [ 9,  4 ],
    double    => [ 10, 8 ],
    ldouble   => [ 11, 8 ],
    cfloat    => [ 12, 8 ],
    cdouble   => [ 13, 8 ],
    cldouble  => [ 14, 16 ],
);

The hash keys are the PDL type names and the values are [ type_code, size_in_bytes ].

clone_view

Usage       : $view_clone = $view->clone_view();
Purpose     : Clone a PDL view
Returns     : The cloned view
Parameters  : $view - the PDL view to clone
Throws      : Nothing
Comments    : The cloned view will NOT share memory with the original view.
              This is a deep copy.

EXAMPLES

Some of the examples are assumed to run sequentially, i.e. the same buffer is used in multiple examples.

Example 1: Creating views

use Task::MemManager;
use Task::MemManager::View::PDL;

my $buffer = Task::MemManager->new_buffer(1024);
my $view = $buffer->create_view(0, 1024, { pdl_type => 'float', dims => [ 256, 4 ] });

if ($view) {
    print "Created PDL view successfully\n";
} else {
    print "Failed to create PDL view\n";
}

Example 2: Accessing and modifying data through the view

use Task::MemManager;
use Task::MemManager::View ;
use PDL;
use PDL::NiceSlice;

my $length = 10;
my $mem = Task::MemManager->new(
    $length, 1,
    {
        init_value => 40,
        death_stub => sub {
            my ($obj_ref) = @_;
            printf "\n ======> Killing 0x%8x <======\n", $obj_ref->{identifier};
        },
    }
);

# allows to print hex values of a string
sub print_hex_values {
    my ( $string, $bytes_per_line ) = @_;
    $bytes_per_line //= 8;    # Default to 8 bytes per line if not provided

    my @bytes =
      unpack( 'C*', $string );    # Unpack the string into a list of bytes

    for ( my $i = 0 ; $i < @bytes ; $i++ ) {
        printf( "%02X ", $bytes[$i] );   # Print each byte in hexadecimal format
        print "\n"
          if ( ( $i + 1 ) % $bytes_per_line == 0 )
          ;    # Print a newline after every $bytes_per_line bytes
    }
    print "\n"
      if ( @bytes % $bytes_per_line != 0 )
      ;        # Print a final newline if the last line wasn't complete
}

my $task_buffer = $mem->get_buffer();
my $pdl_view =  $mem->create_view('PDL');

say $pdl_view;
print_hex_values($mem->extract_buffer_region(0,9),10);

Output should be (40 is 0x28 in hex):

[40 40 40 40 40 40 40 40 40 40]
28 28 28 28 28 28 28 28 28 28 

Example 3: Modifying the PDL view in place, modifies the buffer

This continues from Example 2.

$pdl_view(0:4).=20;
$pdl_view +=1;  # implied in place
say $pdl_view->inplace->sqrt;  # PDL view
print_hex_values($mem->extract_buffer_region(0,9),10); # stored Task::MemManager object
say $mem->get_view('PDL_default'); # view through Task::MemManager 

All three outputs should be identical:

[4 4 4 4 4 6 6 6 6 6]
04 04 04 04 04 06 06 06 06 06 
[4 4 4 4 4 6 6 6 6 6]

Example 4: Cloning a view

say $mem->get_view('PDL_default');
say "Clone the view and increment it by one";
my $pdl_clone= $mem->clone_view('PDL_default');
say "Get an uint16_t view";
my $pdl_intview=$mem->create_view('PDL',{pdl_type=>'short',view_name=>'PDL_short'});

say "Initial View : ",$pdl_view;
say " Cloned View : ", $pdl_clone;
say "  Int32 View : ",$pdl_intview;

Output should be:

Clone the view and increment it by one
Get an uint16_t view
Initial View : [4 4 4 4 4 6 6 6 6 6]
Cloned View : [4 4 4 4 4 6 6 6 6 6]
  Int32 View : [1028 1028 1540 1542 1542]

DIAGNOSTICS

If you set up the environment variable DEBUG to a non-zero value, then a number of sanity checks will be performed, and the module will carp with an (informative message ?) if something is wrong.

DEPENDENCIES

The module extends the Task::MemManager::View module so this is definitely a dependency. It (obviously) requires the PDL (Perl Data Language) module to be installed and the Inline::C module to interface with the PDL C API.

TODO

Open to suggestions. One idea is to add Magic to the views to support various operations triggered via accessing or modifying the view. For example, one could support GPU memory mapping.

SEE ALSO

AUTHOR

Christos Argyropoulos, <chrisarg at cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2025 by Christos Argyropoulos.

This is free software; you can redistribute it and/or modify it under the MIT license. The full text of the license can be found in the LICENSE file See https://en.wikipedia.org/wiki/MIT_License for more information.