Speed up calculation by running in parallel

2023-11-09

原文链接:https://perlmaven.com/speed-up-calculation-by-running-in-parallel

In this example we have a bunch of numbers. We need to make some heavy calculation on each number (using our calc function), and then collect the results in a hash where the keys are the original numbers and the values are the results.

For the sake of our example the “heavy computation” is generating and summing up lots of random numbers.

The linear solution

This is quite simple to do in linear code. We just need to iterate over the elements of the original array that holds the input numbers and call the calc function on each one of them. When the function is done it returns the result that we can then assign to the appropriate element in the hash.

examples/calc_no_fork.pl

use strict;
use warnings;
use Data::Dumper qw(Dumper);
 
my @numbers = map { $_ * 2000000 } reverse 1 .. 10;
my %results;
 
foreach my $q (@numbers) {
    $results{$q} = calc($q);
}
 
print Dumper \%results;
 
sub calc {
    my ($n) = @_;
    my $sum = 0;
    for (1 .. $n) {
        $sum += 1 + rand()/100;
    }
    return $sum;
}

(If it is unclear what is in the @numbers array and how it is generated then use the Dumper function to print the content of @numbers right after it was created and read about map in Perl.

We can run this code using the time program of the Unix/Linux shell:

$ time perl calc_no_fork.pl

The result on my computer was:

real    0m21.032s

It took 21 seconds to do all the calculations.

While the program was running I’ve also used the htop program in another console and saw that none of the 4 cores of my computer is fully used.

在这里插入图片描述
I think the reason that none of them is fully used is that the operating system moves the process around form CPU to CPU, but I am not 100% sure in this.

Using fork

In order to allow the computer to better utilize all its resources we could use either threads or fork. As threads are not a recommended technique in Perl we opt to use fork as described in the article on Using fork to spread load to multiple cores. The problem, as it is also mentioned at the end of that article is that forked processes don’t have shared memory, so the forked process cannot simple write back to the common %results hash. That’s where the module Parallel::ForkManager comes into play.

It is a wrapper around the regular fork function of Perl that provides us with various nice extra services. Including the possibility to return data from the child processes to the parent process that launched them.

examples/calc_fork_manager.pl

use strict;
use warnings;
use Parallel::ForkManager;
use Data::Dumper qw(Dumper);
 
my $forks = shift or die "Usage: $0 N\n";
 
my @numbers = map { $_ * 2000000 } reverse 1 .. 10;
my %results;
 
print "Forking up to $forks at a time\n";
my $pm = Parallel::ForkManager->new($forks);
 
$pm->run_on_finish( sub {
    my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
    my $q = $data_structure_reference->{input};
    $results{$q} = $data_structure_reference->{result};
});
 
foreach my $q (@numbers) {
    my $pid = $pm->start and next;
    my $res = calc($q);
    $pm->finish(0, { result => $res, input => $q });
}
$pm->wait_all_children;
 
print Dumper \%results;
 
sub calc {
    my ($n) = @_;
    my $sum = 0;
    for (1 .. $n) {
        $sum += 1 + rand()/100;
    }
    return $sum;
}

The code starts with the creation of the Parallel::ForkManager object and setting the maximum number of parallel child processes.

my $pm = Parallel::ForkManager->new($forks);

Then we create an anonymous function (a sub without a name) and pass it to the run_on_finish method of Parallel::ForkManager. This function will be called once for each child process immediately as the child process terminates. It receives a number of parameters, but the one that is interesting to us now is the 6th, the last parameter which we assigned to the $data_structure_reference variable.

This variable will hold everything we sent back from the child process. In our case that will be a hash reference with two keys. “input” will contain the value from the original @numbers array the specific child process is dealing with. The “result” will contain the value returned by the calc() function.

$pm->run_on_finish( sub {
    my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
    my $q = $data_structure_reference->{input};
    $results{$q} = $data_structure_reference->{result};
});

Then comes the main part of the code.

We have a simple foreach loop iterating over the @numbers array. For each iteration we call my $pid = $pm->start and next; This will try to create a new fork. If it is successful then at this point two processes will continue to run almost exactly the same way: the value returned by the start method is assigned to $pid. There is however a small difference in the two processes.

In the parent process, this value is going to be the process ID of the child process, a non-zero number, and therefore the right-hand side of the and boolean operator will be evaluated and the main process will go to the next iteration of the foreach loop.

In the child process the value returned by start will be 0. Which is false. Which means the right-hand side of the and operator will not be executed. In the child process the next evaluated statement will be the calc($q);. While the child process is calculating using one of the CPUs of the computer, the main process can run using the other CPU and it can create more child-processes.

The Parallel::Forkmanager will also count how many child processes have been forked and if we reach the value passed to the new constructor then the start command will wait till one of the earlier child-processes finishes and will only fork a new child-process after that.

In the meantime all the child processes are running on one of the CPUs of the computer. When one of them finishes the calc function it will call the finish method of Parallel::Forkmanager and it will pass to it two values. The first one is the exit code it wishes to have. 0 means success. The second one is a reference to a data structure it wishes to send back to the main process. This is the data structure we have used in the anonymous subroutine in $data_structure_reference.

foreach my $q (@numbers) {
    my $pid = $pm->start and next;
    my $res = calc($q);
    $pm->finish(0, { result => $res, input => $q });
}
$pm->wait_all_children;

The call to wait_all_children makes sure that the parent process will indeed wait till all the processes it created have finished and will only continue running once that happened and once it had a chance to run the respective run_on_finish function.

We can run this script as

time perl ~/work/perlmaven.com/examples/calc_fork_manager.pl 8

The result is about twice as fast as the linear version:

real    0m11.138s

At the same time htop shows that all the CPUs are saturated.
在这里插入图片描述
In some other measurements I’ve seen a 3-time speedup, but I think you can’t expect anything better with a 4-core machine. After all there are many other tasks running in the system, so we don’t really have 4 times more free CPU power than earlier, and the whole forking and managing the communication has some overhead.

Combined example

Just in case you’d like to tweak the calc() function and would like to further experiment with this code, I’ve included a version of the two scripts combined together. If you run it without any parameter it will run the linear version. If you run it with any positive number, it will use that many parallel child processes.

examples/calc_fork_manager_full.pl

use strict;
use warnings;
use Parallel::ForkManager;
use Data::Dumper qw(Dumper);
 
 
my $forks = shift;
 
my @numbers = map { $_ * 2000000 } reverse 1 .. 10;
my %results;
 
if ($forks) {
    print "Forking up to $forks at a time\n";
    my $pm = Parallel::ForkManager->new($forks);
    $pm->run_on_finish( sub {
        my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $data_structure_reference) = @_;
#die Dumper \@_;
        my $q = $data_structure_reference->{input};
        $results{$q} = $data_structure_reference->{result};
    });
 
    DATA_LOOP:
    foreach my $q (@numbers) {
        my $pid = $pm->start and next DATA_LOOP;
        my $res = calc($q);
        $pm->finish(0, { result => $res, input => $q });
    }
    $pm->wait_all_children;
} else {
    print "Non-forking\n";
    foreach my $q (@numbers) {
        $results{$q} = calc($q);
    }
}
 
print Dumper \%results;
 
sub calc {
    my ($n) = @_;
    my $sum = 0;
    for (1 .. $n) {
        $sum += 1 + rand()/100;
    }
    return $sum;
}

BTW, the “DATA_LOOP” in this example is not really needed, it only tries to make the code a bit more readable.
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Speed up calculation by running in parallel 的相关文章

  • 如何将 cpanminus 与本地 CPAN::Mini 一起使用?

    我已经创建了自己的 CPAN 本地副本minicpan http search cpan org rjbs CPAN Mini 1 111007 bin minicpan并设法重新配置 cpan 来使用它 太棒了 但是我将如何使用它cpan
  • 使用 os.forkpty() 创建一个伪终端以 ssh 到远程服务器并与其通信

    我正在尝试编写一个 python 脚本 它可以 ssh 到远程服务器 并可以从 python 客户端执行 ls cd 等简单命令 但是 在成功 ssh 到服务器后 我无法读取伪终端的输出 任何人都可以在这里帮助我 以便我可以在服务器上执行一
  • 在 Objective-C 中使用 perl?

    CPAN 有大量非常有用的库 除了将它们移植到 Objective C 之外 是否还有在 iOS 上将 Perl 代码嵌入到 Objective C 中的方法 我对使用特别感兴趣电子表格 阅读 http kobesearch cpan or
  • perlbrew 可以在 Windows 上运行吗?

    使用 ActiveState 5 8 8Windows XP http en wikipedia org wiki Windows XP 我想安装更新的 Perl 进行测试 迁移 有一天 我们将不再以为自己只能使用一台计算机 当我想测试这类
  • 从 Perl 线程生成 Expect

    我正在编写一个脚本 该脚本需要定期 每 5 分钟 生成一个 Expect 进程来完成一些工作 下面是我的代码 它生成一个 Expect 进程并执行一些工作 脚本的主要进程始终在做一些其他工作 例如它可能等待用户输入 因为我在一个线程中调用这
  • 如何从 Perl 中的 Subversion 预提交挂钩访问提交的文件?

    我需要执行以下操作 用 Perl 编写预提交钩子 Hook 应检查所有提交的文件是否存在某些文本 如果未找到该文本则失败 基本上 我需要一个读取正在提交的文件的 Perl 钩子示例 我真的在寻找一些代码量最少的优雅解决方案 笔记 钩子应该使
  • 更新命令行输出

    我的程序 碰巧是用 Perl 编写的 尽管我不认为这个问题是 Perl 特定的 在程序中的某一点输出状态消息 Progress x yy where x and yy是一个数字 例如 Progress 4 38 我想在打印新的状态消息时 覆
  • 多维哈希排序 - Perl [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我真的需要一些帮助来理解这个哈希并对
  • Perl 脚本(或任何东西)来合计 CSV 列

    我写了 在其他人的很多帮助下 awk command https stackoverflow com questions 4159224 excel and awk disagree about csv totals 4159404 415
  • Perl6:我怎样才能使所有警告都是致命的?

    我怎样才能使 Perl6 中的所有警告都是致命的 以便脚本在屏幕上出现警告时立即终止 CONTROL when CX Warn note exit 1 更频繁地死亡 该脚本终止于CONTROL when CX Warn note exit
  • Perl 删除目录中的所有文件

    我怎样才能删除allPerl 中目录中的文件 不删除目录 我的主机只允许最多 250 000 个 文件 而我的 tmp 文件夹会在所有会话 cookie 运行的情况下快速填充 250 000 个 qouta 在这种情况下我无法删除 tmp
  • Perl:LWP::UserAgent 对于重定向 URL 始终返回代码 200

    我有一个简单的 url 它执行 302 临时错误 移至另一页 我尝试在 URL 返回代码 200 表示 OK 时检索它 并在返回 200 以外的其他内容时停止 My code my ua LWP UserAgent gt new env p
  • 如何确保我的代码永远不会直接退出?

    eval require file subsequent code goes here If file包含一个exit语句 后面的代码就没有机会运行 如何解决以便后续代码始终有机会运行eval已经完成了 中止是不可能的exit call f
  • 使用 Perl 获取 值

    因此 我有一个报告工具 可以在 HTML 文件中输出作业调度统计信息 并且我希望使用 Perl 来使用这些数据 但我不知道如何单步浏览 HTML 表 我知道如何使用 jQuery 来做到这一点 find tr each function v
  • perl-5.10 之前的高效版本相当于 pack("Q>")

    更新 萨尔瓦正确地指出我对 Q 包模板的介绍是错误的 这是 gt 修饰符 不会返回到 5 8 Perl 5 10 引入了 pack 修饰符 gt 对于我使用 Q 的用例 它将一个无符号四边形 64 位 值打包在大尾数法 现在 我正在寻找一个
  • 如何在 Linux 中使用 C 语言使用共享内存

    我的一个项目有点问题 我一直在试图找到一个有据可查的使用共享内存的例子fork 但没有成功 基本上情况是 当用户启动程序时 我需要在共享内存中存储两个值 当前路径这是一个char and a 文件名这也是char 根据命令参数 启动一个新进
  • 在 Google 表格应用程序中进行身份验证

    我有一个类似批处理的应用程序 由调度程序定期调用 无需人类用户参与 它使用 PerlNet Google 电子表格 http metacpan org pod Net Google Spreadsheets包通过从数据库获取的数据来更新 G
  • C++,如何在进程或线程之间共享数据

    我有一个运行两个不同操作的程序 我想在它们之间共享变量 目前 我正在使用线程而不是 fork 进程 但即使我将它们声明为易失性 我在共享变量时也遇到问题 我尝试使用 boost 做 boost thread collisions threa
  • 使用正则表达式提取两个短语之间的所有单词[重复]

    这个问题在这里已经有答案了 我正在尝试使用以下正则表达式提取两个短语之间的所有单词 b item W w W 0 2 1 one W w W 0 3 business b b item W w W 0 2 3 three W w W 0 3
  • 在perl中更改多维哈希的第一个键

    我在 perl 中有一个多维哈希 我想更改所选值的第一个键 例如 我有哈希 my Hash1 Hash1 1 12 1 Hash1 1 10 1 Hash1 2 31 1 Hash1 3 52 1 Hash1 3 58 1 Hash1 4

随机推荐

  • 2020年研究生数学建模竞赛总结复盘

    文章目录 一 前言 二 赛题选择 三 做题思路 问题一 数据清洗 问题二 数据降维 问题三 建模预测 问题四 分析模型预测结果与实际值 问题五 可视化 四 总结 五 结果 三等奖 一 前言 今天是2020年研究生数学建模竞赛的最后一天 今早
  • git deamon 一个简单的git服务器

    git deamon 一个简单的git服务器 一 Git daemon 二 操作 三 参考 四 总结 一 Git daemon Git daemon是一个简单的git仓库服务器 可以用来共享局域网的本地仓库 二 操作 以下示例A电脑共享gi
  • 使用BindingList实现DataGridView的动态绑定

    在DataGridView数据绑定时使用BindingSource中转可以起到很好的控制DataGridView
  • 指针基础(2)【数组与指针】

    文章目录 写在前面 1 数组概述 1 1 一维数组 1 2 二维数组 1 3 多维数组 1 4 小结 2 C 中 vector 容器 2 1 定义和初始化 vector 对象 2 2 向 vector 对象中增加元素 2 3 vector
  • MQTT异常断开

    MQTT异常断开 讨论一下TCP链路的影响 MQTT异常断开 TCP链路原因的几种情况 三种情况 1 客户端发送了心跳请求 但是MQTT代理服务器Broker没有收到心跳请求 所以也不会回复客户端心跳响应 MQTT代理服务器Broker在
  • 前端 115道 面试题总结【持续更新...】

    前端面试题 1 说说你对useEffect的理解 可以模拟哪些生命周期 2 说说Real DOM和Virtual DOM的区别 优缺点 3 说说React中setState和replaceState的区别 4 说说React生命周期有哪些不
  • Web 开发中 20 个很有用的 CSS 库

    http www oschina net translate css libraries for developers
  • Android 开机加速优化

    文章目录 Android 开机加速优化 关闭BootLoader的企鹅 关闭开机动画Android 关闭锁屏 删除预装APP 查看预装APP 删除编译生成的APK 不编译APK 1 统一配置 2 修改单个APK的Android mk 谷歌A
  • 2022年5月计划(UE4视频教程+osgearth源码调试+ogreRenderSystem源码抄写)

    按照年度计划走就可以了 五一期间突击完了ue4第七套视频教程 客户端差不多了 各项终于达到什么都会 什么都不精了 没有短板 也没有长处 平衡进行就行了 包括久违的渲染 也可以引进了 以前如果单单干渲染是不行的 毕竟这种工作少 还要会引擎架构
  • springboot集成dubbo(详细配置)

    前言 首先要搭建zookeeper环境并启动 可参照window下搭建zookeeper 半生归来仍少年的博客 CSDN博客 dubbo管理平台搭建 下载 dubbo admin 2 5 8 war 互联网文档类资源 CSDN下载 放到to
  • 编译 java_如何编译java

    展开全部 用命令32313133353236313431303231363533e58685e5aeb931333337613139提示符编译java程序的步骤 1 先新建文本文档 输入自己的java程序 这里我写一个简单的java程序 来
  • 计算机网络arp表作用,arp映射表是什么?有什么作用

    在如今每一天的生活中大家都需要有网络的陪伴 相比于手机的4G流量和无线网卡 大家更钟爱于wifi 因为它能够更加方便的使用 可是在我们刚刚购买或者安装路由器的时候也会遇到很多的难题 比如说什么是arp arp映射表有什么作用 接下来就让我们
  • 关于医学影像中的轴位面(横断面)、冠状面、矢状面

    冠状位矢状位轴位图解 第1页 概述 该页主题为冠状位矢状位轴位图解的图片集 内容包含有冠状位 矢状位 横断位具体怎么辨别 谢谢 ct 解剖 颞骨大体解剖 轴位及冠状位ct断层图像 解剖 颞骨大体解剖 轴位及冠状位ct断层图像 图1 a 矢状
  • vue制作幻灯片时涉及的transition动画(动图)

    幻灯片使用频率很高 就是各个网站的轮播大图 为了使图片更加平滑的过渡 就考虑给幻灯片加上transition动画 先看实现的效果 然后再分析动画原理 上图可以看出 幻灯片是慢慢的滑出来 而不是一下一下的跳出来 1 transition动画原
  • 前端面试--大众点评

    学习了这么久 第一次面试前端 虽然只是电话面试 但是还是很紧张 主要问题 1 介绍你的项目 2 html的状态 3html5新增加的标签 4 css的display none和visibility区别 5 怎么清除浮动 6 jquery的选
  • 【TensorFlow 入门】7、定义图变量的方法

    1 tf Variable tf Variable init initial value trainable True collections None validate shape True name None 参数名称 参数类型 参数含
  • DNS服务器列表

    Public DNS IPv4 地址 首选 119 29 29 29 AliDNS 阿里公共 DNS IPv4 地址 首选 223 5 5 5 备用 223 6 6 6 114 DNS 常规公共 DNS 干净无劫持 首选 114 114 1
  • SpringCloud---Sentinel

    文章目录 限流 sentinel使用环境搭建 设置限流 默认直接模式 关联模式 链路模式 关闭URL PATH聚合 熔断 降级 设置模拟环境 满调用比例规则 Sentinel 异常处理模式 异常处理 自定义异常处理 热点 测试热点环境搭建
  • 用mysqldump备份及结合binlog日志恢复的全过程

    1 查看更新备份时的数据 mysql gt select from t1 id 1 2 3 2 因为我的存储引擎是Myisam 为了保证数据的一直我加了参数 l 备份时不能对数据更新 如果是innodb引擎加参数 single transc
  • Speed up calculation by running in parallel

    原文链接 https perlmaven com speed up calculation by running in parallel In this example we have a bunch of numbers We need