在Scala 2.13中使用顺序和并行集合进行模拟


介绍 要构建的软件是对来自独立卫星系统的数据进行比较。当战斗机越过领空时,每个系统都会确定其身份,并记录喷气机类型的名称或等效的字母或数字缩写。对于随时间推移收集的数据,仿真软件必须确定系统是否在识别上达成一致,如果是,则通过单个或多个处理器对比数据是否会更加有效。

本文从Scala 2.12中的概念证明开始。一个带有通用收集类型参数功能的小程序,可以验证基本策略。实际的软件是在2.13中编写的,其中不赞成使用通用集合类型(那些继承了GenTraversable层次结构的集合类型)[1]。并行程序包也消失了,但可以收回[2]。

您将在实际的软件仿真系统中看到以下内容:重写在集合层次结构中定义的“对应”功能;用于定时顺序算法和并行算法的定时的自定义方法;和基本的统计结果读数。

对于本文,您需要Scala集合中的背景知识-至少是顺序集合。

此处提供2.12程序和完整的系统代码:https : //github.com/zironsys/sequential_parallel

中央战术:斯卡拉2.12 如前所述,自版本2.13开始,不建议使用通用集合类型,并且删除了对集合的多线程处理的支持。但是,我们可以使用2.12版中的这些资源来设计核心策略。

自动生成社交媒体帖子描述的屏幕截图

1594246874141.png 图1.使用收集功能“对应”来验证系统协议

这个概念验证程序要实现三个目标。第一种是形成一种数据结构,用于保存卫星系统之间已知的喷气式飞机标识符的正确关联。设置'correctSet'可以做到这一点。

该集合是硬连线的,用于关联全名和字符缩写。实际的软件将通用,以允许在字符串,字符和数字类型之间进行随机配对。

通过对correctSet进行转换,我们的第二个目标得以实现:创建模型数据的结构以进行比较。转换和解压缩Tuple2的集合将返回两个数组,分别模拟不同系统的输出。“ .par”方法产生ParArray对象,该对象的元素可以在多个处理器上的线程中进行水平分区和并行操作。

最后,我们需要一个基于集合的函数来进行逐项比较,并且该函数可以与两种类型的数组一起使用。局部函数“对应”采用GenSeq类型的参数,GenSeq是GenTraversable衍生的特征,可以采用连续或并行序列(或混合序列)。它调用的“对应”函数来自Array类,其含义与ParArray中的含义相同;这是它的签名:

def corresponds[B](that: GenSeq[B])(p: (T, B) ⇒ Boolean): Boolean

当序列的长度相同并且对应位置的所有元素都相关时,对应的返回true。使用用correctSet构建的序列进行测试的结果微不足道。谓词参数“ p”的使用更为有趣。每个paring(a,b)都在correctSet的“ apply”方法中尝试,该方法测试集合成员资格。该谓词将在2.13版的实际软件中使用。该软件包括其自己的对应用途的实现。

因此,设置correctSet至关重要地涉及所有目标:维护正确的配对,建立模拟数据以及测试对应性。在实际的软件中,它将被称为“验证集”。

填充模拟行:验证集 小型卫星软件和测试/模拟软件位于单独的软件包中。由于本文的重点是前者,因此下载后的对象TestTools中所包含的此方法都不会看到后者。我之所以将其包括在内,是因为它对于理解如何为所有模拟测试创建数据(使用已建立的验证集)具有重要意义。

buildtestdata-2020-07-04.png

图2.填充正确的数据对

函数“ buildTestData”的第一个参数控制是否将创建数千或数百万个数据元素。“ TestData”返回值(如图所示定义)是正在比较的仿真系统之间的Tuple2关联数组-实际测试数据。它被参数化以允许任何系统将喷气机识别信息表示为字符串,字符或数字。

重要参数“ systemPairs”是两个给定系统之间已知的正确关联的集合,从中形成所有最初正确的测试数据:验证集。它的类型是从卫星库中的“ CorrespondsCheck”伴随对象导入的,请参见以下内容:

type SystemPairs[A,B] = Set[(A,B)]

测试是在(字符串,字符)和(整数,字符串)对的集合上完成的;图1中的变量correctSet是前者的一个示例,将在测试中使用。

尽管此处生成的数据始终是正确的,但模拟运行可能会更改元素以测试失败案例。

解决方案空间:对应检查 该软件的版本为2.13.2。如前所述,有关添加和使用修订后的程序包“ parallel”的信息在[2]中。

与并发编程中使用的其他强调原子性正确性的资源(例如原子变量和期货)不同,并行包中的资源严格地与性能有关。至于准确的结果,在2.13(我假设)中,对于一个可以并行化的集合操作,鉴于它可以在一个处理器上正确运行,因此它必须在多个处理器上正确运行。

卫星库中的类CorrespondsCheck定义了一个方法,该方法先对数组执行并行函数,然后对并行数组执行,并返回两个运行时间。

类所需的资源在其伴随对象中。

对象对应检查 这是完整的伴随对象:

object-corrck-2020-06-28.png

图3.伴侣对象

在上一节中,您已经看到测试代码使用的SystemPairs类型。

底部的时间函数来自Prokopec [3]。我修改了它们以在这里使用。所有顺序和并行执行都是通过它们完成的。

两者中使用的参数名“ body”是要运行的对应函数,“ resultDummy”是零值,仅用于初始化执行结果。正如您将看到的,对于顺序运行,类型T为Long;对于并行运行,类型为Boolean。

函数“ timed”通过“ warmTimed”调用,该函数执行相应的函数“ iterations”多次,然后从timed返回Tuple2结果。对于较大的项目,建议将默认值设置为200,以使JVM进入稳定状态–即,不干预JIT编译和垃圾回收,这可能会使定时结果产生偏差。如果运行代码,建议您使用一个小于或等于20的值,否则可能需要一些时间才能完成。

对象顶部用大写字母表示的Long值将在后面说明。

类对应检查 这是整个类:

class-corrck-2020-06-28.png

图4.类CorrespondsCheck

该类已参数化,以允许任何喷气机识别系统使用各种类型(字符串等)。它的“ verificationSet”构造函数参数也与您期望的一样:用于验证输入。

一个公共函数返回一个Either Disjoint联合,我将在这里阐明其目的。让我们从其嵌套函数开始。

在Scala标准库2.13中,数组具有对应功能的两个版本,一个版本在特征SeqOps中实现,另一个版本在特征IterableOnceOps中实现。除了参数类型,它们的含义相同,并且可通过隐式转换用于Array。

这两个对应的函数都返回布尔值,但是我们需要一个数字返回值以获取更完整的错误信息。嵌套函数'sequentialCheck'从'_Ops'特性中改编而成。

其代码和注释应不言自明;不过,我确实请您注意以下代码段:

while (a.hasNext && b.hasNext && (CORRESPONDS_SUCCESS == outcome)) {
        curIdx += 1
        if (!(verificationSet(a.next(), b.next()))) {
          outcome = curIdx // end loop
        }
}

包含此比较测试的循环本质上是图1中本地对应函数的声明式代码单行的迭代形式。它完成了相同的集合成员资格测试:

arrayA.corresponds(arrayB)((a, b) => correctSet((a, b)))

外部函数对应检查调用包装在warmTimed方法中的函数。如果在此单线程调用中发现了失败– Tuple2中类型T的“结果” –多线程ParArray版本不会被调用,并且对应检查用ARRAY_LENGTH_FAILURE或谓词所在的索引填充Either的Left类分支顺序检查首先失败。根据假设,如果成功到达ParArray,则必须成功,并且绝不能将UNKNOWN_PARALLEL_ERROR填充到Left中。

成功后,Right类将在顺序执行和并行执行的各个时间中以(字符串,字符串)填充(以毫秒为单位)。定时函数将伴随类中的十进制格式对象用于良好的String读出。

测验 结果可能因机器而异。我正在具有八核且在Ubuntu 18.04上具有超线程的计算机上进行测试。以下读数的warmTimed默认为:调用定时方法之前的200次迭代。

前四个测试使用如图1所示的验证集,第五个测试使用(Int,String)的元组。测试一个样本使用1,000个元素的样本大小,而其他样本使用50,000,000个元素。

在较小的尺寸下,测试一表明使用多个内核显然效率低下。但是,在较大尺寸的测试中,并行算法的收益非常可观,如第二项和第五项测试所示。

测试3和4是我更改了正确数组的错误情况。第三项测试特别显示了卫星系统如何准确识别错误元素的索引。

test-results-2020-07-04.png

图5.五个测试用例

综上所述 您看到了一个小的概念证明程序如何有助于定义和实现实际的软件目标。用Scala版本2.12编写的程序是最后一个支持GenTraversable层次结构的程序,它演示了正确的喷嘴识别关联的存储,创建模拟数据以及测试数据的单线程和多线程类型的正确性。

尽管未提供测试系统,但我的确讨论了其功能,该功能通常可创建任何大小的正确仿真数据。

在实际的软件中,基于目标和POC,所有资源都包含在单个类和伴随对象中。后者包括确定执行时间和预热时间的功能。该类利用了自定义的嵌套函数,该函数扩展了顺序集合Scala库中的对应函数,以提供更多功能。

在整个过程中,您看到了验证集所扮演的核心角色。

与单处理器和多处理器算法相比,我们可以安全地得出结论,性能部分取决于数据的大小。尽管如此,还有更多需要考虑的地方。总有。

参考 [1] Scala标准库2.13.3,“程序包收集”。[在线的]。可用:https : //www.scala-lang.org/api/current/scala/collection/index.html。[“已弃用的价值成员”部分。

[2] Scaladex,“ scala / scala-parallel-collections”。[在线的]。可用:https : //www.scala-lang.org/api/current/scala/collection/index.html。

[3] A. Prokopec,《在Scala中学习并行编程》。 英国伯明翰:Packt Publishing,2014年。[第5章:数据并行集合]

https://github.com/zironsys/sequential_parallel


原文链接:http://codingdict.com