Hi Garden Dwarf
Yes, batching is helpful to improve performance. I met to try Thread::Queue as well. For extra performance try passing an array ref like the demo to minimize memory copies inside Perl. Below is an example using Thread::Queue which does not involve sockets for IPC. It is faster than MCE::Shared on the Windows platform.
#!/bin/perl
# Win32::GUI and threads issue
# https://www.perlmonks.org/?node_id=1228580
use strict;
use warnings;
# One may run threads + MCE::Shared. This works very well.
# Why MCE::Shared one might ask? Well, it handles serialization
# of data objects automatically. E.g. passing array refs.
#
# Thread::Queue on the Windows platform will give you better
# performance. It means having to choose a serializer (a fast one)
# and handle data serialization at the application level.
use threads;
use Thread::Queue;
use CBOR::XS;
my $queue_in = Thread::Queue->new();
my $queue_ou = Thread::Queue->new();
my $t_amount = 3; # Amount of threads to create
my $running = 0;
my $textbox;
my $win;
# Important, spawn threads early before loading Win32::GUI.
threads->new('producer') for 1..$t_amount;
# Run the main app or consumer afterwards.
consumer();
# Voila :)
exit;
sub consumer {
require Win32::GUI;
# Initialize window
$win = new Win32::GUI::Window(
-left => 0,
-top => 0,
-width => 300,
-height => 300,
-name => "Window",
-text => "Test",
);
$win->InvalidateRect(1);
$textbox = $win->AddTextfield(
-name => "Output",
-left => 5,
-top => 5,
-width => 275,
-height => 255,
-text => "",
-multiline => 1,
);
# Start application (calls draw_Timer)
$win->AddTimer('draw', 333);
$win->Show();
Win32::GUI::Dialog();
}
sub producer {
threads->detach();
while ( defined ( my $next_args = $queue_in->dequeue ) ) {
my ( $c, $begin, $end ) = @{ decode_cbor($next_args) };
my @ret = compute($begin, $end);
sleep 2; # simulate a long running process
$queue_ou->enqueue( encode_cbor([ $c, @ret ]) );
}
}
sub Window_Terminate {
$queue_in->end();
-1;
}
sub draw_Timer {
# Enqueue range of computation to background threads
if ( $running == 0 ) {
$running = 1;
foreach my $c (1..$t_amount) {
my $d = $c - 1;
$queue_in->enqueue( encode_cbor([ $c, $d*10, $d*10+10 ]) );
}
}
# Obtain data once background threads have completed
if ( $running == 1 && $queue_ou->pending == $t_amount ) {
$running = 0;
my @ordered_ret;
foreach my $c (1..$t_amount) {
my $ret = decode_cbor( $queue_ou->dequeue() );
my $c = shift(@{ $ret }) - 1; # array-based-zero
$ordered_ret[$c] = "";
foreach my $data (@{ $ret }) {
$ordered_ret[$c] .= "|".$data;
}
$ordered_ret[$c] .= "\n";
}
$textbox->Append($_) for @ordered_ret;
}
}
sub compute {
my ($begin, $end) = (shift, shift);
my (@tbl, $cpt);
for ($cpt = $begin; $cpt < $end; $cpt++) {
push @tbl, $cpt;
}
return @tbl;
}
By all means, batch accordingly if possible to further increase performance. Enable your creativity in the design. Looks like fun :)
Regards, Mario |