摘要
HPC Services for Excel 支持多种新的编程模型,可用于在 HPC 群集上运行 Excel 计算。 本文介绍如何使用 Excel VBA 宏生成利用 HPC 群集并行运行计算的工作簿。 若要支持 HPC Services for Excel,工作簿需要包含一组实现异步函数的宏。 本文介绍宏框架以及如何在框架中定义计算。 本文包括如何从头开始生成工作簿的示例,以及转换现有工作簿以在群集上运行的示例。
介绍
你会花一个小时让 Excel 工作簿运行四次更快吗? 快八倍,甚至更多? 如果你有长时间运行的或缓慢的 Excel 工作簿,则可以将 HPC Services for Excel 与 Microsoft HPC 群集配合使用,以显著提高计算性能。
HPC Services for Excel 支持各种新的编程模型,允许你在 HPC 群集上运行 Excel 计算。 但是,无需了解代码开发或了解 C#,才能使用 HPC Services for Excel。 在本文中,我们将讨论如何使用 Excel VBA 生成利用 HPC 群集进行计算的工作簿。
实际上,转换其中一个工作簿以在群集上运行可能需要一个多小时,具体取决于工作簿有多复杂。 但是,如果你花了一个小时完成本文中的示例,你将了解所涉及的概念以及转换任何工作簿以在 HPC 群集上运行的要求。 你还将看到通过对 VBA 代码进行一些简单更改可以获得的性能优势。
目标受众
本文适用于熟悉编写 VBA 宏的 Excel 开发人员。 熟悉 Microsoft HPC Pack 非常有用,但不需要。
如果对 HPC Services for Excel 的性能可能性感兴趣,但不想完成完整的开发过程,则下载文件包括本文中讨论的两个工作簿的完整版本。 如果想要尝试在 Microsoft HPC 群集上运行这些工作簿,请阅读“开始之前”部分,以确保在开始之前满足所有要求。 有关其他示例和文档,请转到 https://github.com/Azure-Samples/hpcpack-samples。
下载文件
可以在 HPC Pack 示例 Github 存储库后面的部分中找到一些示例文件。 示例文件包括可以导入到工作簿中的主干宏文件,以简化开发过程。 还有一个基本工作簿,我们将用作开发 HPC Services for Excel 工作簿的起点。
结构和格式设置
本文中的屏幕截图均来自 Excel 和 HPC 群集管理器。 在本文中包含的动手示例中,代码示例是使用表示 VBA 编辑器的代码样式进行格式设置:
'==========================================================
'
' Section 1: Variables and constants
'
'==========================================================
Dim SentRecords As Integer
Dim RcvRecords As Integer
添加或修改代码行时,它们将以绿色突出显示。 此处包含的大部分代码是从现有文件导入的,或从不同的 VBA 文件之间复制。 在这种情况下,可能会截断一些长代码行,以适应本文档的边距。 尝试从现有文档(而不是本文)复制和粘贴,以避免意外包括截断的代码示例。
概述
HPC Services for Excel 支持许多编程模型,用于在 Microsoft HPC 群集上开发和运行 Excel 计算。 你选择的解决方案应取决于你的特定需求和开发技能。
| 预期用途 | 所需的技能 | 解决方案 |
|---|---|---|
| 并行计算工作簿,以提高性能(更快的计算时间)或在同一时间内处理更多数据的能力 | Excel VBA (宏) 开发 | 使用 Excel VBA 在 HPC 群集上执行工作簿 |
| 生成在 HPC 群集上执行 Excel 工作簿的自定义应用程序和服务,并与独立应用程序或批处理脚本集成 | 使用 C#、VBA.NET 或 .NET CLR 支持的任何语言进行 .NET 编程 | 为 Excel 开发自定义 HPC 服务和客户端应用程序 |
| 加快 Excel 扩展库 (XL) 中包含的 Excel User-Defined 函数(UDF)的计算 | C 或C++编程、Excel 扩展库 (XLL) 开发 | Cluster-Enabled Excel User-Defined 函数 |
我们将在未来的文章中解决其中每个编程模型的问题。 今天,我们将讨论第一个模型:仅使用 Excel VBA 并行计算 Excel 工作簿。 即使想要使用 Excel 和 Microsoft HPC Pack 进行更复杂的开发,这也是一个很好的起点:这是在 HPC 群集上运行 Excel 应用程序的最简单、最快的方法,它可以帮助你了解使用 HPC Services for Excel 的要求和优势。
为了支持 Excel 开发人员,HPC Services for Excel 包含一组库,可直接在桌面上从 Excel 使用,以便在Microsoft HPC 群集上生成和运行 Excel 工作簿。 使用这些库,无需了解 .NET 编程或 C# 。 所有工作都可以通过编写 VBA 宏从 Excel 完成。 使用此框架,只需编写 VBA 代码并在群集上执行工作簿,即可大幅加快 Excel 工作簿的计算速度。
HPC Services for Excel 包括一个客户端/服务框架,该框架专为桌面上的 Excel 使用而设计。 此框架有两个重要部分:在桌面上安装的客户端库;以及安装在群集计算节点上的服务库。 安装Microsoft HPC Pack 时,默认情况下会安装这些库,因此无需执行任何自定义配置来使用这些库。
安装客户端和服务库后,只需将一些 VBA 宏添加到工作簿,即可为群集生成和运行 Excel 工作簿。 我们将这些新的 VBA 宏描述为 宏框架。 当我们引用宏框架时,我们专门描述添加到工作簿的新宏,以支持 HPC 群集上的计算。
在讨论特定宏之前,值得强调一些影响宏构造方式及其工作方式的关键概念。
异步计算
开发适用于 Excel 的 HPC Services 工作簿和使用宏框架时要了解的最重要概念是 异步计算。
典型的 Excel 应用程序 以同步方式运行。 也就是说,按按钮运行宏,宏执行一些 VBA 代码。 在此代码中,可以更改电子表格、调用其他 VBA 函数、访问数据库或其他任何内容。 但你预计宏将从头到尾运行,然后完成。
异步模型稍有不同。 在此模型中,你将编写 VBA 函数,但不直接调用它们。 相反,客户端库(随 HPC Services for Excel 一起安装的代码)将调用 VBA 函数。
如果熟悉 VBA 中的 Excel 事件,则已知道此模型。 例如,如果编写在 Workbook.Open 事件上运行的 VBA 函数,则你知道代码将在打开工作簿时运行。 但你不直接调用该代码;相反,Excel 会在打开工作簿时自动调用您编写的函数。
异步编程模型的工作方式相同。 编写将在计算期间调用的函数,但不要自己调用它们,而是等待 Excel(在本例中为 Excel 库)调用函数。
迭代计算和并行计算
另一个重要概念是迭代计算。 迭代计算是指针对不同数据集运行相同基本计算步骤的计算类型。
迭代计算的最常见示例是蒙特卡洛模拟。 在蒙特卡洛模拟中,通常有一个复杂的计算-如生成证券价格-基于一些随机输入值,如几年的预期回报。 蒙特卡洛模拟每次使用不同的随机输入值运行相同的计算数千次。
这是一个迭代计算,因为每次计算价格,使用不同的随机输入值,整个计算将再次运行。 这些计算是独立的;也就是说,在每个单独的计算中,没有任何对之前或后续计算的引用。
另一个常见示例涉及对数据库中的一组记录运行计算。 在这种情况下,你可能有一个复杂的计算(如保险或精算模型),该计算基于一些参数,如保险持有人的年龄和性别。 迭代计算将为成千上万的个人策略持有者运行相同的模型。 每个计算都独立于所有其他计算。
我们在这里讨论的宏框架旨在处理这些类型的迭代计算。 这一点很重要,因为在 HPC 群集上运行计算时,我们将 并行 运行计算 -- 我们将多次运行相同的计算,但我们会在群集计算节点上同时运行多个计算实例。
如果计算不独立 -- 如果任何单个计算的结果取决于之前运行的计算 -- 则这不起作用。 因此,我们在此处讨论的框架仅适用于迭代独立计算 -- 可并行化的计算。
宏框架
若要支持 HPC Services for Excel,工作簿需要包含一组实现异步函数的宏。 这是宏框架。 框架中使用了六个宏,你可以添加到任何 Excel 工作簿。 六个宏,简要描述它们执行的作,
| 宏名 | 行动 |
|---|---|
| HPC_Initialize | 执行任何预计算或初始化步骤 |
| HPC_Partition | 为单个计算步骤收集所需的参数 |
| HPC_Execute | 执行一个计算步骤,作为较大整体计算的一部分 |
| HPC_Merge | 处理单个计算步骤的结果 |
| HPC_Finalize | 执行任何计算后处理 |
| HPC_ExecutionError | 处理计算产生的错误 |
(如果这些说明没有立即清楚,请不要担心:我们将在下面更详细地描述每个函数)。
必须将相同的宏添加到任何电子表格以支持群集上的计算,但宏中的内容(特定的计算函数)可能有所不同。 为了简化此过程,本文附带的下载文件包含一个“主干”宏文件,你可以将其导入到工作簿中。 准备好基本宏后,可以根据需要填充这些宏以支持计算。
每个宏在宏框架中都有一个特定用途,每个宏将在群集计算过程中由 HPC Services for Excel 客户端库调用。 计算运行时,每个宏将在特定点调用。 典型的计算如下所示:
群集计算运行时,首先客户端库将调用 HPC_Initialize 宏。 用于处理任何必需的初始化;例如,你可能想要清除电子表格中的旧结果。
接下来,计算将运行三个宏: HPC_Partition、 HPC_Execute和 HPC_Merge。 在上图中,这些示例显示为循环。 这不是在计算过程中真正发生的情况,但从逻辑上讲,你可以将其视为循环。 首先,客户端库 调用HPC_Partition。 HPC_Partition 旨在收集单个计算步骤所需的任何数据。 例如,如果要逐个计算一组行, HPC_Partition 可能会返回单个步骤的行号:第一行 1、第 2 行等。
接下来,将调用 HPC_Execute 宏。 此宏运行实际计算。 如果要逐行计算,则会计算单个行。 HPC_Execute 将返回计算结果:例如,它可能会返回行中最后一个单元格,这是较长计算的最终结果。
HPC_Execute后,客户端库将调用HPC_Merge。 计算的任何 HPC_Execute 都将发送到 HPC_Merge。 HPC_Merge宏旨在获取这些结果并将其返回到桌面上运行的电子表格。 在 HPC_Merge 宏中,可以将计算结果插入电子表格中,也可以将它们写入日志文件。
这三个宏( HPC_Partition、 HPC_Execute和 HPC_Merge )将针对计算中的每个步骤调用多次,直到整个计算完成。 这些宏实现上面讨论的 迭代 计算模型。 宏可能会在单个工作簿计算期间多次调用,但每次调用宏时都表示单个计算步骤或 迭代。
完成最后一个计算步骤后,客户端库将调用 HPC_Finalize。 可以使用此宏执行任何计算后处理:例如,你可能希望使用另一个 VBA 宏查找所有以前的计算步骤的平均值。
仅当计算遇到一些错误时,才会使用第六个和最后一个宏 HPC_ExecutionError。 如果是这样,则会调用此宏,你可以添加一些错误处理代码-例如,你可能会向用户显示一个弹出对话框,警告他们出现错误。
在上图中, HPC_Execute 宏以橙色突出显示。 也就是说,此宏有点不同寻常。 所有其他宏都在桌面上运行,在工作站上的 Excel 中运行。 但是,当我们在 HPC 群集上运行计算时, HPC_Execute 宏实际上在群集计算节点上运行。
这就是 HPC Services for Excel 和宏框架支持群集计算的方式。 虽然我们在计算运行时在同一工作簿(桌面上的工作簿)中写入所有宏,但工作簿将复制到群集计算节点, HPC_Execute 宏将在其中一个计算节点上运行。
请务必了解这一点,因为它对工作簿在计算期间如何使用和管理数据有一些影响。 在计算过程中,三个主要计算宏( HPC_Partition、 HPC_Execute和 HPC_Merge )来回传递数据。 在典型的 VBA 应用程序中,可通过多种方式在不同宏之间共享数据。 例如,可以在 VBA 代码中使用全局变量,或将值写入电子表格单元格。 但是,当我们在 HPC 群集上运行计算时,我们需要在桌面上运行的宏与在计算节点上运行的 HPC_Execute 宏之间发送数据。
因此,我们需要运行计算的任何数据都必须直接从 HPC_Partition 宏发送到 HPC_Execute 宏。 我们通过使用HPC_Partition宏中的返回值来执行此作。 该返回值将成为HPC_Execute函数的参数(或参数)。 因此,数据将直接在这两个宏之间传递,并且可以在这些宏之间传递任何值或值集(作为数组)。 但是,不能使用全局变量或电子表格单元格在两个宏之间传递信息,因为当它在群集上运行时,实际上有多个同时运行的工作簿副本 -- 一个在桌面上运行,一个(或更多)在群集计算节点上运行。
同样,当 HPC_Execute 中的计算完成时,它将以宏函数的返回值的形式返回结果。 该结果将成为下一个宏的参数(或参数), HPC_Merge。 必须以这种方式发送要返回的任何结果(计算结果)作为函数的返回值。
使用 Excel 和 HPC Services for Excel 生成 Cluster-Enabled 工作簿
在后面的部分中,我们将逐步讲解如何构建支持 HPC 群集计算的两个工作簿。 在第一部分中,我们将从头开始生成工作簿。 这应该有助于了解所涉及的概念以及使用 HPC Services for Excel 的要求。 第二部分将采用一个现有工作簿(一个设计为在桌面上运行)并对其进行转换,以便它可以在群集上运行。
开始之前:先决条件和要求
HPC Services for Excel 是Microsoft HPC Pack 2008 R2 Beta 2 及更高版本随附的一组工具。 需要安装并配置Microsoft HPC Pack 群集。 安装 HPC 群集超出了本文的范围;我们将仅解决 HPC Services for Excel 所需的特定配置。 有关安装和配置群集的详细信息,请参阅Microsoft HPC Pack 随附的文档。 在桌面上,需要 HPC Pack 客户端实用工具。
在群集上
启动并运行 HPC 群集后,需要在群集计算节点上安装 Excel。 可以使用标准 Office 安装工具包来安装 Excel,或查看 HPC Pack 文档,了解有关自动执行 Excel 安装的详细信息。
在群集计算节点上安装 Excel 后,可以运行 HPC 诊断测试,以确保正确配置所有内容。 若要运行诊断测试,请使用 HPC 群集管理器(无论是在群集头节点上、默认安装群集头节点上,还是在桌面上安装客户端实用工具)。
验证 Excel 的 HPC 服务是否已正确配置
在 HPC 群集管理器中,单击“ 诊断”。
在 “测试”中,展开 Microsoft ,然后选择 “Excel”。
在视图窗格中,双击 “Excel 运行程序配置测试”。
在配置对话框中,选择 “所有节点 ”,然后单击“ 运行”。
在 导航窗格中, 单击“ 测试结果 ”以查看测试的进度。 正在运行的测试将显示在视图窗格中。
测试完成后,可以在主窗口中单击测试以查看结果。
如果测试显示 “失败”,请双击该测试并尝试更正弹出窗口中的任何错误。 你可能看到的常见错误是 Excel 未安装在计算节点上,或者 Excel 尚未激活。 进行所需的任何更改,然后使用上述说明重新运行诊断测试。
测试显示 “成功”后,即可继续。
最后需要的是共享目录。 在 HPC 群集上计算工作簿时,每个群集计算节点实际上都会加载并运行工作簿的副本。 若要支持这一点,需要创建一个对群集计算节点和桌面可见的共享目录。
如果有权访问群集头节点,请在头节点上创建共享目录。 这很方便,因为你知道群集计算节点可以访问头节点。 如果无法在头节点上创建共享目录,请在域中的任意位置创建共享目录,以便桌面和群集计算节点都可访问该目录。
在桌面上
在桌面上,需要安装 Excel 和 Microsoft HPC Pack 客户端实用工具(安装客户端实用工具需要管理员权限)。 若要安装客户端实用工具,请在桌面上运行 HPC Pack 安装程序。 它将提供一个选项来仅安装客户端实用工具。
概要
现在应具有:
已安装并配置Microsoft HPC 群集。
安装在群集计算节点上的 Excel 2016(或更高版本)。
网络共享目录。
Excel 2016(或更高版本)和安装在桌面上的 Windows HPC Pack 客户端实用工具。
准备就绪后,即可开始使用 HPC Services for Excel 在群集上运行的工作簿。
第一部分:从头开始生成工作簿
此部分包括以下部分:
启动新工作簿
准备工作簿
设计群集计算
在群集上运行工作簿
最后步骤:用户界面
启动新工作簿
使用新工作簿启动 Excel。 我们将使用 VBA,因此请确保 Excel 功能区上提供了 “开发人员 ”选项卡。
打开“开发人员”选项卡
单击 Excel 功能区上的“ 文件 ”选项卡。
单击“选项”。
在 Excel 选项对话框中,选择左侧的 “自定义功能区 ”。
选中 “开发人员 ”旁边的框,然后单击“ 确定 ”关闭对话框。
准备工作簿
若要准备工作簿,需要导入宏文件并添加 HPC 引用。
若要生成基本工作簿,我们需要两组宏。 第一组宏实现框架函数(上述概述部分所述的函数)。 计算过程中,HPC Services for Excel 客户端库使用这些宏。 第二组宏包含“control”函数 -- 这些宏运行实际计算。
示例文件包括可用于开始使用的每个基本“主干”宏文件。 你不必使用这些文件,也可以编写自己的宏(如果你愿意的话)但宏本身是通用的,你可能会发现使用这些文件是你所需要的。 还可以修改这些文件,以便对特定工作簿进行任何所需的更改。
我们需要为此项目添加两个引用才能使用 HPC Services: Microsoft_Hpc_Excel 和 Microsoft_Hpc_Scheduler_Properties。 Microsoft_Hpc_Excel 是客户端库,它提供可用于控制群集计算的对象。 Microsoft_Hpc_Scheduler_Properties 具有一些特定于群集的字段和类型的定义,我们需要这些字段和类型。
注意:在上面的“概述”部分中,我们将工具描述为客户端/服务器框架;安装 HPC Pack SDK 时已安装客户端库,现在可在 VBA 中使用。
导入宏文件并添加引用
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中,右键单击树视图顶部的 VBA 项目 ,然后单击“ 导入文件”。
在对话框中,找到下载的项目文件。 进入目录“第一个工作簿”,选择文件“HPC Excel Macros.bas”,然后单击“ 确定”。 这会将第一个宏文件添加到项目中,该宏文件将在 VBA 项目窗口的 “模块 ”文件夹中可用。
重复步骤 1-3 以导入“HPC 控制宏.bas”,然后单击“ 确定”。 这会将第二个宏文件添加到项目中。
验证这两个宏文件是否显示在 VBA 项目窗口中:

在 VBA 编辑器窗口中,单击“ 工具”,然后单击“ 引用 ”以打开项目引用对话框。
在可用引用列表中,向下滚动,直到找到 Microsoft(R) HPC Pack 程序集,然后选择每个程序集旁边的复选框。 选中这些框后,对话框将如下所示:

单击“ 确定 ”关闭项目引用对话框。
如果未在列表中找到此条目,请单击 “浏览”。 找到文件 %CCP_HOME%\Bin\Microsoft.Hpc.Excel.tlb。 如果仍然找不到它,请验证是否已安装 HPC Pack 客户端组件。 如果安装了文件,“添加/删除程序”控制面板将显示“Microsoft HPC Pack 客户端组件”的条目。 如果未看到该条目,请尝试重新安装客户端组件,如上一部分“开始之前”中所述。
在宏框架中定义计算
构建此工作簿的目的是说明群集计算的工作原理,因此我们不需要构建非常复杂的电子表格。 我们将构造运行 100 个单独步骤的计算。 为此,我们将向 HPC Excel 宏文件添加计数器,并在计算运行时在宏中使用该计数器。
我们将使用计数器跟踪已发送的计算步骤数,以便我们可以将计算限制为 100 个步骤。 每当启动新计算时,将调用HPC_Initialize,因此我们可以在该函数中重置计数器。 我们希望为每个计算步骤递增计数器。 我们可以在 HPC_Partition 宏中执行此作。 请记住, HPC_Partition 宏用于收集单个计算步骤所需的任何数据;计算完成后, HPC_Partition 宏应返回 Null。 因此,若要对 100 个步骤运行计算,每次调用 HPC_Partition 时,我们将递增计数器;计数器达到 100 后,我们将返回 Null。
构造运行 100 个步骤的计算
在 VBA 项目窗口中的 “模块 ”文件夹中,双击 HPCExcelMacros。
在文件顶部的“第 1 节:变量和常量”中,添加名为 SentRecords 的计数器变量,如下所示:
'========================================================== ' ' Section 1: Variables and constants ' '========================================================== Dim SentRecords As Integer向下滚动,直到找到 HPC_Initialize 函数,然后添加一行来重置计数器变量:
'---------------------------------------------------------- ' ' HPC_Initialize will be called when the client starts ' a calculation. Put any pre-calculation steps in this ' function. ' '---------------------------------------------------------- Public Function HPC_Initialize() SentRecords = 0 End Function向下滚动,直到找到宏 HPC_Partition,然后对其进行修改,使其如下所示:
Public Function HPC_Partition() As Variant If SentRecords = 100 Then HPC_Partition = Null Else SentRecords = SentRecords + 1 HPC_Partition = SentRecords End If End Function保存工作簿。
注意:由于新工作簿包含宏,因此需要将其保存为启用宏的工作簿(XLSM 文件)或二进制工作簿(XLSB 文件)。 要么很好。 通常,我们建议将工作簿另存为二进制文件(XLSB),因为它们更小且效率更高。
现在,我们有一个计算,它将运行 100 个步骤,然后完成。 刚刚进行的修改是创建 Excel 群集计算所要做的。
在本地运行工作簿并浏览宏
有了计算后,可以添加一个按钮来运行工作簿,然后在桌面上运行工作簿。 若要查看宏的工作原理,我们将向宏框架中添加一些新代码片段,然后重新运行工作簿以查看更改执行的作。 在群集上运行工作簿之前,我们将在本地(桌面上)测试工作簿。
添加在本地运行工作簿的按钮
添加在本地运行工作簿的按钮
打开 Excel 工作簿后,单击功能区上的 “开发人员 ”选项卡。
在“ 开发人员 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。
单击该按钮后,在电子表格的某个位置绘制一个矩形,以在该位置插入该按钮。 定位按钮后,将显示“ 分配宏 ”对话框。
在对话框中,从列表中选择宏 CalculateWorkbookOnDesktop ,然后单击“ 确定”。 请务必选择桌面宏 - 我们希望先测试工作簿,并在群集上运行工作簿之前找到任何错误。
右键单击新按钮,然后选择“ 编辑文本 ”以更改标签。
将标签命名为“Desktop”或类似名称。
(可选)可以通过右键单击按钮并选择“ 分配宏”来验证是否已分配正确的宏。 在对话框中,验证 是否选择了 CalculateWorkbookOnDesktop 。
保存工作簿。
单击该按钮以在桌面上运行工作簿。
如果存在任何错误,你将看到一个错误对话框,其中将突出显示包含错误的 VBA 代码部分 -- 返回上述部分并检查一切是否正确。
如果一切正常,则不会发生任何作,因为我们的工作簿不执行任何作。 这有点不满意,所以让我们再做一些更改,以便我们可以看到计算。
修改HPC_Excecute和HPC_Merge宏
请记住,信息流从 HPC_Partition (收集计算所需的参数)到 HPC_Execute (运行计算),然后 HPC_Merge( 处理结果)。 执行计算时, 从HPC_Partition 宏返回的任何内容都用作 HPC_Execute 宏的输入。 HPC_Execute宏旨在使用该输入数据,执行一些计算步骤,然后返回结果。 然后,结果将传递给 HPC_Merge,该HPC_Merge可以在电子表格中插入结果。
在前面的步骤中,你向 HPC_Partition 宏添加了代码以更新计数器变量。 因此,在我们的示例中, HPC_Partition 返回计数器变量的值,并将该值传递给 HPC_Execute。 为了说明信息流并让工作簿返回一些结果,我们只需通过宏传递此计数器值。 我们将向 HPC_Execute 宏添加一行代码,以便它接受输入并将其作为返回值传递。 然后,计数器变量的值将传递给 HPC_Merge 宏,我们将对其进行修改,以便它将计数器的值插入到电子表格中。
修改HPC_Execute和HPC_Merge
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中的项目树中,双击 HPCExcelMacros 模块打开宏文件。
.滚动到 HPC_Execute 宏,并添加一行代码,以返回值的形式传递输入值。 宏应如下所示:
Public Function HPC_Execute(data As Variant) As Variant HPC_Execute = data End Function滚动到 HPC_Merge 宏,并添加一行代码以将输入值插入电子表格中。 宏应如下所示:
Public Function HPC_Merge(data As Variant) Cells(data, 1).Value = data End Function注意:“data”值是从 HPC_Execute返回的计数器。 因此,在 HPC_Merge 宏中,我们将填写电子表格中的单元格(使用计数器作为行号),并将值设置为计数器。 VBA 表达式“Cells”表示使用给定行号和列号在电子表格中创建引用。 因此,每次调用 HPC_Merge 宏时,都会将值插入第 1 列(电子表格中的列 A),并使用不同的行号。
现在返回到电子表格并单击“桌面”按钮以运行计算。
你将看到第一列填充 1-100 中的数字,即计数器值。 每次单击按钮时,它都会填充相同的数字,因此很难判断它正在运行;我们可以再做一个小的更改,以查看填充的数字。
修改HPC_Initialize宏
首次运行工作簿时,将调用 HPC_Initialize 宏。 在前面的步骤中,我们添加了一行代码来重置计数器变量。 我们还可以使用此宏擦除在上一次运行期间插入到电子表格中的值。
修改HPC_Initialize宏
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中的项目树中,双击 HPCExcelMacros 模块打开宏文件。
滚动到 HPC_Initialize 宏**。** 添加一行代码以清空第一列。 宏应如下所示:
Public Function HPC_Initialize() Range("A:A").Value = "" SentRecords = 0 End Function现在返回到电子表格并单击“桌面”按钮以运行计算。
现在应该很明显,每次单击该宏时,宏都会用数字填充该列。 如果出现任何错误,将看到错误消息,并突出显示带有错误的 VBA 代码 -- 仔细检查它是否与上面列出的代码匹配,然后重试。
在群集上运行工作簿
现在,我们有一个非常简单的 Excel 计算使用 HPC 宏框架,我们可以在群集上运行它。 为此,我们首先需要设置几个值来告知 Excel 如何联系群集。 这些值在 HPCControlMacros 模块**中定义。** 在此文件的顶部,需要填写两个值:群集计划程序和共享目录。 请记住,(桌面用户)必须具有对此共享目录的写入访问权限;和群集计算节点必须具有对目录的读取访问权限。 在大多数情况下,群集计算将在用户帐户下运行,但在连接到群集会话时可以使用其他用户帐户(稍后更多)。
指定头节点和共享目录
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中的项目树中,双击 HPCControlMacros 模块以打开宏文件。
对于群集计划程序,请使用群集头节点的名称 -- 这是将在网络上使用的计算机名称。 可以使用完全限定的名称(例如,headnode.mynetwork.com),但如果位于同一域中,则不需要这样做。
对于共享目录,请使用之前创建的共享目录的完整路径(在“开始之前”部分)。
例如,在我的群集中,计划程序名称为“HN01”;我在头节点上创建了一个名为“HPCTemp”的共享目录。 因此,我的设置如下所示:
'----------------------------------------------------------
'
' This is the cluster scheduler, or head node. Fill in
' the hostname of your cluster scheduler.
'
'----------------------------------------------------------
Private Const HPC_ClusterScheduler = "HN01"
'----------------------------------------------------------
'
' This is a network share used to store a temporary copy
' of the workbook. Make sure that the directory exists,
' that you have write access to the directory, and that
' the compute nodes in the cluster have read access.
'
'----------------------------------------------------------
Private Const HPC_NetworkShare = "\\HN01\HPCTemp"
现在返回到 Excel 电子表格。 我们将添加另一个按钮,这次在群集上运行计算。
添加在群集上运行工作簿的按钮
在“ 开发人员 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。
单击该按钮后,在电子表格的某个位置绘制一个矩形,以在该位置插入该按钮。 定位按钮后,将显示“ 分配宏 ”对话框。
在对话框中,从列表中选择宏 CalculateWorkbookOnCluster ,然后单击“ 确定”。
右键单击新按钮,然后选择“ 编辑文本 ”以更改标签。
将标签命名为“Cluster”或类似名称。
保存工作簿。
单击该按钮以在群集上运行工作簿。
如果这是首次使用 Excel 或使用任何其他应用程序运行任何群集作业,你将看到用户身份验证对话框。 键入用户名和密码,并选中该框以保存凭据。 如果要以其他用户身份在群集上运行计算,可以输入不同的用户帐户设置。
注意:请确保用户帐户(标准用户帐户或要使用的任何帐户)被指定为 HPC 群集管理器中的群集用户。
如果存在任何错误,你将看到一个描述错误的消息框。 如果计算在桌面上工作,则你现在收到的最有可能的错误与群集设置(头节点或共享目录)有关。 如果消息框描述了其中任一错误,请仔细检查在上一步中更改的设置。 请确保群集头节点的名称正确,并且共享目录可由用户帐户写入。
工作簿在群集上运行时会发生什么情况
让我们回顾一下运行群集计算时会发生什么情况。 单击按钮时,它会调用宏 CalculateWorkbookOnCluster。 该宏使用客户端库通过为群集头节点和共享目录提供的设置启动群集计算。
计算开始时,HPC Services for Excel 客户端代码将接管。 请记住,在本部分开头的 VBA 编辑器中添加了对 HPC 库的引用,即客户端库。 首先,客户端库将创建用于管理计算的群集“会话”。 这需要几秒钟,在单击按钮后,你会注意到几秒钟的延迟。
接下来,客户端库将调用 HPC_Initialize 宏。 这发生在桌面上。 在此工作簿中,该宏 (1) 重置内部计数器;和 (2) 清空电子表格中的 A 列。 因此,单击按钮时,首先会在创建会话时看到延迟;然后,你将看到 A 列已清除。 这就是 HPC_Initialize 宏。
接下来,客户端库将开始调用 HPC_Partition 宏。 这也会在桌面上发生。 在我们的代码中, HPC_Partition 宏递增计数器并返回计数器值。 每次此宏返回一个值(直到它返回 Null),最后一次调用时,客户端库都会向 HPC 计划程序发送请求。
当计划程序收到其中一个请求时,它会将请求转发到其中一个群集计算节点进行处理。 当计算节点收到请求时,它将启动 Excel,加载工作簿,然后调用 HPC_Execute 宏。
这是我们工作簿中的 HPC_Execute 宏,但在群集上运行计算时, HPC_Execute 宏实际上在计算节点上运行 ,这与所有其他宏不同。 这就是为什么我们要将数据从一个宏传递到另一个宏。 信息从一个宏流向下一个宏,但随着数据从 HPC_Partition 宏传递到 HPC_Execute 宏,它将从桌面、HPC 计划程序发送到其中一个计算节点。
在其中一个计算节点运行 HPC_Execute 宏后,当宏完成计算结果时(从 HPC_Execute 宏返回的值)将发送回计划程序,然后返回到桌面上的客户端库。 当客户端库收到计算结果时,它将调用桌面上的 HPC_Merge 宏。 因此,你可以再次想到从一个宏(HPC_Execute)流向下一个(HPC_Merge),但这在网络上发生:从计算节点上运行的工作簿到桌面上运行的工作簿。
请务必了解,这些宏函数调用---每次调用 HPC_Partition,当我们将数据发送到群集时,以及每次调用 HPC_Merge(作为数据接收)异步发生。 也就是说,在将计算发送到群集时,客户端库在发送下一个计算之前不会等待(或阻止),直到该特定计算完成。 如果 HPC_Partition 宏非常快,因为在这种情况下,在完成其中任一请求之前,可能会发送所有 100 个计算请求。
值得注意的是,由于结果从群集中返回,并且 HPC_Merge 宏在我们的工作簿中调用-它们可能不会按照我们发送它们的相同顺序返回。 这是因为,由于每个请求都发送到群集,计划程序会将它转发到一个特定的计算节点。 计算节点将使用工作簿中的 HPC_Execute 宏计算请求,然后发送回结果。 但出于多种原因,一个计算节点的运行速度可能比另一个计算节点慢或更快;如果发生这种情况,结果可能会按不同的顺序返回。
你可能不会注意到,在此工作簿中,因为宏太简单了。 但是,我们需要在更复杂的工作簿中做好准备,因为我们有更长的计算函数,我们将在生成“真实”工作簿时对其进行以下处理。
通信计算进度
此时,你已生成在 HPC 群集上运行的完整工作簿;你现在应该了解各种宏的用途,以及它们的设计方式的原因。 应该了解数据在宏之间移动的方式,以及在桌面和群集上执行哪些部分。
在完成简单工作簿之前,我们可以进行一些最终更改来添加一些视觉反馈 -- 基本上是一个简单的用户界面。 在群集上运行工作簿时,你唯一的反馈是电子表格中填写的数字。 这是一个开始,但我们可以做出一些更改,使工作簿的作用更加明显。 我们要执行的作是再添加一些变量来跟踪计算,然后在 Excel 状态栏中显示它们。
我们的代码将包含以下计数器:
我们已经有一个计数器变量,用于跟踪 调用宏HPC_Partition 次数。 这表示将数据从桌面发送到群集的次数。
我们可以添加另一个计数器,该计数器每次调用 HPC_Merge 宏时递增 -- 表示数据从群集返回到桌面的次数。
我们还可以添加一些变量来跟踪时间 -- 计算需要多长时间。 这对于检查群集计算是否实际上比桌面快(如果是这样,那么快多少)非常有用。
使用这些前两个计数器,可以看到未完成的请求数,并了解计算的总体进度。 我们将添加一个新的 VBA 函数,该函数可以报告计算状态。 此函数有三个部分:
它创建一个字符串,其中显示了发送的计算数(调用 HPC_Partition 次数)和返回的结果数(调用 HPC_Merge 次数)。
如果“CalculationComplete”变量为 true,它将更新字符串以显示总计算时间。
它使用 Application.StatusBar 设置 Excel 状态栏消息,其中包含刚刚创建的信息。
可以使用已准备好的 HPC 宏来更新这些值,并使用新函数更新状态栏消息。
以下过程介绍如何将这些新元素合并到示例中。
将进度变量添加到宏并更新 Excel 状态栏
返回到 VBA 编辑器,然后双击模块 HPCExcelMacros 打开宏代码。 在 VBA 代码顶部的原始计数器后面,添加以下新变量:
'========================================================== ' ' Section 1: Variables and constants ' '========================================================== Dim SentRecords As Integer Dim RcvRecords As Integer Dim CalculationComplete As Boolean Dim StartTime As Double Dim FinishTime As Double接下来,我们将添加一个名为“UpdateStatus”的新 VBA 函数,该函数可以报告计算状态。 向下滚动到文件的底部,并添加代码,如下所示:
Sub UpdateStatus() Dim statusMessage As String statusMessage = "Calculated " & RcvRecords & "/" & SentRecords If CalculationComplete Then statusMessage = statusMessage & "; Completed in " & _ FormatNumber(FinishTime - StartTime) & "s" End If Application.StatusBar = statusMessage End Sub我们需要初始化变量。 与原始计数器一样,我们将在 HPC_Initialize 宏中执行此作。 滚动到该宏,并添加几行:
Public Function HPC_Initialize() Range("A:A").Value = "" SentRecords = 0 RcvRecords = 0 StartTime = Timer CalculationComplete = False UpdateStatus End Function注意:我们在这里执行的作是(1)清除两个计数器,将其设置为零;(2) 将“StartTime”值设置为当前时间(VBA 中的“计时器”调用返回当前时间(以秒为单位):和 (3) 将“CalculationComplete”标志设置为 false,这意味着它尚未完成。 设置这些值后,我们将调用新的“UpdateStatus”宏,将消息写入状态栏。
向下滚动到 HPC_Partition 宏。 在此宏中,我们已经设置第一个计数器,因此只需进行更改:更新计数器后,我们将添加一行来调用新宏并更新状态栏。
Public Function HPC_Partition() As Variant If SentRecords = 100 Then HPC_Partition = Null Else SentRecords = SentRecords + 1 UpdateStatus HPC_Partition = SentRecords End If End Function现在向下滚动到 HPC_Merge 宏。 在这里,我们希望更新新计数器(表示返回到桌面的消息)并再次更新状态栏。 因此,修改 HPC_Merge 宏:
Public Function HPC_Merge(data As Variant) Cells(data, 1).Value = data RcvRecords = RcvRecords + 1 UpdateStatus End Function向下滚动到 HPC_Finalize 宏。 整个计算完成后调用此宏;因此,我们可以使用它来设置“CalculationComplete”标志,并找出总计算花费的时间:
Public Function HPC_Finalize() CalculationComplete = True FinishTime = Timer UpdateStatus ' Clean up the calculation. It's a good idea to ' leave this here, even if you make changes to ' this function. The function we call here is in ' the "HPCControlMacros" module. CleanUpClusterCalculation End Function注意:将“FinishTime”字段设置为计时器时,我们将用当前时间(以秒为单位)进行设置。 在 UpdateStatus 宏中,我们使用“StartTime”和“FinishTime”来确定总计算时间。
保存工作簿
返回到 Excel 电子表格并单击“群集”按钮。
在计算运行时,你将看到状态栏更新并显示数字;首先,你将看到发送的计算数(第二个数字)跳转到 100,然后在短暂延迟后,你会看到收到的结果数(第一个数字)攀升至 100。
此处看到的是上面讨论的异步消息传送。 由于 HPC_Partition 宏的速度如此之快,因此客户端库能够在任何计算完成之前发送所有 100 个请求。 有一个短暂的延迟 - 延迟是计算节点启动 Excel 并加载工作簿。 每个计算节点启动 Excel 后,计算结果的速度相当快,第一个数字可快速达到 100。
如果单击“桌面”按钮,你将看到数字以不同的方式移动。 在桌面上,客户端库调用相同的宏,但它正在同步执行(也就是说,不是异步的)。 若要运行桌面计算,客户端库将依次调用每个宏 -- 首先 HPC_Partition,然后 HPC_Execute,然后 HPC_Merge -- 但在这种情况下,它将等待每个宏完成,然后再调用下一个宏。 这就是为什么在桌面上运行时,你将看到这两个数字一起移动。
第 2 部分:转换“桌面”工作簿以在 HPC 群集上运行
在最后一节中,我们从头开始构造工作簿,以在 HPC 群集上计算。 在本部分中,我们将获取现有工作簿并将其修改为在群集上运行。 如果你遵循最后一部分,你应该了解所需的内容 -- 虽然有很多单独的步骤,但涉及的实际工作非常简单。 总之,我们:
添加了两个宏文件。
添加了对 VBA 项目的两个引用。
对宏进行了一些修改。
添加了用于运行电子表格的按钮。
转换现有工作簿大约是相同的工作量。 没有很多要添加的新 VBA 代码,如果它可以重复使用相同的主干宏文件,则大多数代码都是这样。 在大多数情况下,转换现有工作簿涉及移动代码 -- 将代码从现有宏复制到 HPC 宏中。
转换现有工作簿的难点在于确定要移动的代码以及该工作簿的移动位置。 Excel VBA 是一个全面的开发环境,可通过多种方式生成 Excel 应用程序:不同的开发人员可能以许多不同的方式编写相同的应用程序。
在本部分中,我们将获取现有工作簿并将其转换为在群集上运行。 应阅读本部分作为指南或示例,然后尝试将概念应用于自己的工作簿。 在本例中,我们有一个相当简单的示例(尽管它基于用于解决实际业务问题的实际工作簿)。 对于几乎所有工作簿,该过程将相同,但具体详细信息将有所不同。
分析工作簿
在 Excel 中,从“第二个工作簿”目录中打开工作簿“ConvertiblePricing.xlsb”。 可能会收到有关宏的警告;如果是,请单击“启用宏”以继续。
此工作簿根据左侧“模型参数”部分中的值计算可转换债券价格。 右侧的表用于了解参数(转换价格和息票率的变化)对最终价格的影响较小。
单击电子表格中的“计算表”按钮。 你将看到表格填充缓慢,因为计算每个价格。 在典型的工作站上,填写完整表需要 1-2 分钟。
若要了解此工作簿在单击按钮时执行的作,可以演练 VBA 代码。
标识宏并查看代码
右键单击“计算表”按钮,然后选择“ 分配宏”。
宏对话框显示按钮当前使用的宏。 在对话框中,单击“ 编辑 ”以跳转到代码中。 应会看到“CalculateTable”宏的代码。 这是在电子表格中单击按钮时执行的代码。
查看此宏,可以看到几个不同的部分。 首先,有一些变量声明。 接下来有一些初始化代码。 最后,有一个计算表的部分 -- 我们可以确定它在表中的行和列循环的位置,并填充值。
此宏相当短,但如果你查看循环,你将看到它调用一些其他函数 -- 函数“FullDiscountModel”和“BondPlusOptionModel”。 这些是实际计算例程,即为此工作簿提供支持的业务逻辑。
在 VBA 编辑器中,可以通过右键单击名称并选择“定义”来跳转到任何函数的源。 如果右键单击“FullDiscountModel”并选择“定义”,则会看到实际计算相当复杂。 但是,若要将工作簿转换为在群集上运行,我们不必修改甚至理解该计算。 我们只需要担心控制代码,即包含循环的函数。
此工作簿设计得非常简洁,因此业务逻辑包含在单独的函数中。 在其他情况下,所有这些代码都可能位于单个宏中,这将使工作簿难以理解。 但是,无论哪种情况,我们都将执行转换此工作簿的作是采用控制代码(启动例程和循环函数),并将其移动到 HPC 宏。
我们还知道,在此工作簿中,计算是独立的。 也就是说,计算表的每个单元格,而不引用表中的任何其他单元格。 这意味着工作簿是迭代的,它将支持并行化(如上面的概述部分所述)。 计算可能并不总是显而易见的,即计算是独立的。 确定这一点的一种方法是编写一些测试函数(例如,我们可以重新编写“CalculateTable”宏(在电子表格中按下按钮时调用的宏)以仅运行单个计算或表中的一个单元格。 这样,我们可以确定结果是否一致,如果是这样,我们可以确定计算是否事实上是独立的。
总之,支持在Microsoft HPC 群集上执行的任何工作簿最重要的功能如下所示:
工作簿是迭代的:它多次运行相同的计算,并使用不同的输入数据集;或它运行许多单独的计算,例如表中的行或单元格,一次一个。
单个计算是独立的:也就是说,单个计算的结果不会影响任何其他计算。
这两者都适用于此工作簿,因此非常适合在群集上运行。
准备工作簿
与上一个工作簿一样,我们将进行的第一次更改与设置 VBA 环境有关。 可以使用主干宏文件来节省一些时间 -- 其中包括计算所需的 HPC 宏。
在最后一个工作簿中,我们对主干宏文件进行了一些更改,以添加一些用户反馈(在 Excel 状态栏中显示计算进度)。 在此第二个工作簿中,主干宏文件已包含该状态条形代码。
导入宏文件并添加引用
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中,右键单击树视图顶部的 VBA 项目 ,然后单击“ 导入文件”。
在对话框中,找到下载的项目文件。 进入目录“第二个工作簿”,选择文件“HPCExcelMacros.bas”,然后单击“ 确定”。
重复步骤 1-3 以导入“HPCControlMacros.bas”,然后单击“ 确定”。 这会将第二个宏文件添加到项目中。
验证这两个宏文件是否显示在 VBA 项目窗口中:

在 VBA 编辑器窗口中,单击“ 工具”,然后单击“ 引用 ”以打开项目引用对话框。
在可用引用列表中,向下滚动,直到找到 Microsoft(R) HPC Pack 程序集,然后选择它旁边的复选框。 选中此框后,对话框将如下所示:

单击“ 确定 ”关闭项目引用对话框。
在宏框架中定义计算
设置 VBA 环境后,可以转到下一步 -- 修改宏以运行计算。 HPCExcelMacros 模块包含的宏与第一个工作簿中相同,但这次它们已包含计数器和更新状态栏的代码。
定义变量并从原始宏复制初始化代码
我们需要的第一件事是行和列的计数器。 在此工作簿中,我们将划分计算,以便单独计算表中的每一个单元格。 原始宏使用循环来运行表中的每个单元格,但在 HPC 宏函数中,我们使用异步框架,因此我们需要在发送每个单独的计算时跟踪行号和列号。 我们将使用宏中的行变量和列变量逐步浏览表中的每个单元格;它们反映了我们在每个步骤中正在计算的行和列。
我们将返回到原始宏,查看它在启动时执行的作,然后将其移动到 HPC_Initialize 宏。
设置变量和修改HPC_Initialize
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中的项目树中,双击 HPCExcelMacros 模块打开宏文件。
在文件顶部的“第 1 节:变量和常量”中,添加行和列变量,如下所示:
'========================================================== ' ' Section 1: Variables and constants ' '========================================================== Dim CurrentRow As Integer Dim CurrentCol As Integer Dim SentRecords As Integer Dim RcvRecords As Integer Dim CalculationComplete As Boolean Dim StartTime As Double Dim FinishTime As Double滚动到 HPC_Initialize 宏并初始化我们刚刚设置的行和列计数器:
Public Function HPC_Initialize() CurrentRow = 1 CurrentCol = 1 ' clear counters, capture starting time and update status bar SentRecords = 0 RcvRecords = 0 StartTime = Timer CalculationComplete = False UpdateStatus End Function在 VBA 编辑器中,双击 VBA 项目窗口中的“ScenarioAnalysis”模块。 这是包含原始宏代码的文件。 初始化部分是第一部分,用于确定表的大小并设置一些变量(NumRows 和 NumCols):
' clear values, then figure out the size of the table Range("cashtable").ClearContents NumCols = 1 While NumCols < MaxSensTblSize And CDbl(Range("table1").Offset(0, NumCols … NumCols = NumCols + 1 Wend NumRows = 1 While NumRows < MaxSensTblSize And _ CDbl(Range("table1").Offset(NumRows + 1, 0).Value) <> 0 And _ CDbl(Range("table1").Offset(NumRows + 1, 0).Value) <> … NumRows = NumRows + 1 Wend从原始宏复制代码,然后返回到 HPCExcelMacros 模块(在 VBA 项目窗口中双击 HPCExcelMacros ),然后将其粘贴到 HPC_Initialize 宏中。
HPC_Initialize中的最终代码现在如下所示:
Public Function HPC_Initialize()
' clear values, then figure out the size of the table
Range("cashtable").ClearContents
NumCols = 1
While NumCols < MaxSensTblSize And CDbl(Range("table1").Offset(0, NumCols + …
NumCols = NumCols + 1
Wend
NumRows = 1
While NumRows < MaxSensTblSize And _
CDbl(Range("table1").Offset(NumRows + 1, 0).Value) <> 0 And _
CDbl(Range("table1").Offset(NumRows + 1, 0).Value) <> …
NumRows = NumRows + 1
Wend
CurrentRow = 1
CurrentCol = 1
' clear counters, capture starting time and update status bar
SentRecords = 0
RcvRecords = 0
StartTime = Timer
CalculationComplete = False
UpdateStatus
End Function
修改HPC_Partition宏
要修改的下一个宏是 HPC_Partition 宏。 请记住,此宏收集运行单个计算所需的任何数据。 在这种情况下,这将是行号和列号。 对于此工作簿,我们希望单独计算群集上的每个表单元格;为此,我们将使用行号和列号。
HPC 框架中的所有宏都使用 Variant 数据类型进行输入和输出。 Variant 类型可以是任何基元类型(整数、长、双精度、字符串)或这些类型的数组。 我们需要同时包含行和列,以便可以使用数组。
在 HPC_Partition 宏中需要执行的其他作是在计算完成时确定的。 在这种情况下,当我们到达表末尾时,计算将完成。 我们可以通过在表之间移动时递增行号和列号来执行此作。 每当到达列的末尾时,我们将移动到下一列。 传递最后一列后,表已完成,我们希望函数返回 Null。
修改HPC_Partition宏
在 HPCExcelMacros 代码中,滚动到 HPC_Partition 部分,并添加代码以声明行和列的数组:
Public Function HPC_Partition() As Variant Dim data(3) As Variant ' update the status bar with the counters SentRecords = SentRecords + 1 UpdateStatus End Function添加代码以检查表末尾。 这将在完成表后结束计算。
Public Function HPC_Partition() As Variant Dim data(3) As Variant ' first check the row; if we're past the bottom ' of the table, increment the column and set the ' row back to the top If CurrentRow > NumRows Then CurrentRow = 1 CurrentCol = CurrentCol + 1 End If ' next check the column; if we're past the last ' column, then we're done If CurrentCol > NumCols Then ' return null to indicate the end of the calculation HPC_Partition = Null ' and exit the function now, we can skip the rest Exit Function End If ' update the status bar with the counters SentRecords = SentRecords + 1 UpdateStatus End Function如果尚未完成表,函数的最后一部分将存储行和列值,并将其用作函数的返回值;然后移动到下一行:
Public Function HPC_Partition() As Variant Dim data(3) As Variant ' first check the row; if we're past the bottom ' of the table, increment the column and set the ' row back to the top If CurrentRow > NumRows Then CurrentRow = 1 CurrentCol = CurrentCol + 1 End If ' next check the column; if we're past the last ' column, then we're done If CurrentCol > NumCols Then ' return null to indicate the end of the calculation HPC_Partition = Null ' and exit the function now, we can skip the rest Exit Function End If data(0) = CurrentRow data(1) = CurrentCol HPC_Partition = data ' move to the next row CurrentRow = CurrentRow + 1 ' update the status bar with the counters SentRecords = SentRecords + 1 UpdateStatus End Function
因此,若要查看, HPC_Partition 宏有两个用途:它收集单个计算所需的参数(下面是行号和列号):当计算完成时,它会通过返回 Null 来发出信号。 在已创建的函数中,我们通过逐行逐行和按列执行该作,并在最后一列传递时返回 Null 以指示表已完成。
修改HPC_Execute宏
下一个宏 是HPC_Execute。 请记住,此宏旨在运行实际计算或业务逻辑。 此函数的输入将是上一步 中从HPC_Partition 宏返回的任何内容。 由于我们刚刚编写 了HPC_Partition 宏,因此我们知道 HPC_Execute 宏的输入将是要计算的表单元格的行号和列号。
HPC_Execute宏的正文将从原始计算宏复制。 我们需要此代码的两个部分:顶部的变量声明和运行循环的代码。 我们已经使用了处理 HPC_Initialize 宏中表大小的部分,因此我们不需要该宏,但复制整个代码块会更容易,然后删除我们不需要的部分。 然后,我们将进行一些更改以支持异步框架。
修改HPC_Execute宏
通过双击 VBA 项目窗口中的“ScenarioAnalysis”模块返回到原始宏。
从第一行复制代码:
Dim Prem as double向下到最后一行:
Next cols返回到 HPCExcelMacros 模块(在 VBA 项目窗口中双击),并将代码粘贴到 HPC_Execute 宏中。
在 HPC_Execute 宏中,从以下开始删除已使用的行:
' clear values, then figure out the table size向下到前面一行:
' set up and run through the table验证 HPC_Execute 宏是否如下所示:
Public Function HPC_Execute(data As Variant) As Variant Dim Prem As Double Dim TempPIKdate As Date Dim TempPIKfreq As Double Dim rws As Integer Dim cols As Integer Dim Model As Integer ' set up and run through the table Call ReadSheetVariables TempPIKdate = PIKdate 'Save this value! TempPIKfreq = PIKfreq 'Save this value! Model = 1 If Range("PricingModel").Value2 = "Bond plus Option" Then Model = 2 End If For cols = 1 To NumCols For rws = 1 To NumRows Call ReadSheetVariables PIKdate = TempPIKdate PIKfreq = TempPIKfreq PIKrate = CDbl(Range("table1").Offset(rws, 0).Value) Prem = CDbl(Range("table1").Offset(0, cols).Value) If Prem = 0 Then End Ratio = CDbl(Face / Underlying / (1 + Prem)) Select Case Model Case 1 Range("cashtable").Cells(rws, cols).Value = … Case Else Range("cashtable").Cells(rws, cols).Value = … End Select Next rws Next cols End Function注意:在宏中,我们现在拥有函数的计算部分,包括运行表的每个单元格的循环。 对于异步框架,我们不想在此处使用循环;我们只想计算单个单元格。 请记住, HPC_Execute 宏是在计算节点上运行的代码,我们将为其提供要计算的行号和列号。
删除构成循环的行(下面突出显示的行):
Public Function HPC_Execute(data As Variant) As Variant Dim Prem As Double Dim TempPIKdate As Date Dim TempPIKfreq As Double Dim rws As Integer Dim cols As Integer Dim Model As Integer ' set up and run through the table Call ReadSheetVariables TempPIKdate = PIKdate 'Save this value! TempPIKfreq = PIKfreq 'Save this value! Model = 1 If Range("PricingModel").Value2 = "Bond plus Option" Then Model = 2 End If For cols = 1 To NumCols For rws = 1 To NumRows Call ReadSheetVariables PIKdate = TempPIKdate PIKfreq = TempPIKfreq PIKrate = CDbl(Range("table1").Offset(rws, 0).Value) Prem = CDbl(Range("table1").Offset(0, cols).Value) If Prem = 0 Then End Ratio = CDbl(Face / Underlying / (1 + Prem)) Select Case Model Case 1 Range("cashtable").Cells(rws, cols).Value = … Case Else Range("cashtable").Cells(rws, cols).Value = … End Select Next rws Next cols End Function将循环替换为传入函数的行号和列号。 修改后的代码应如下所示:
rws = data(0) cols = data(1) Call ReadSheetVariables PIKdate = TempPIKdate PIKfreq = TempPIKfreq PIKrate = CDbl(Range("table1").Offset(rws, 0).Value) Prem = CDbl(Range("table1").Offset(0, cols).Value) If Prem = 0 Then End Ratio = CDbl(Face / Underlying / (1 + Prem)) Select Case Model Case 1 Range("cashtable").Cells(rws, cols).Value = FullDiscountModel(360) / Face Case Else Range("cashtable").Cells(rws, cols).Value = BondPlusOptionModel(360) … End Select
现在,我们使用数据数组中的值(从 HPC_Partition 宏返回的数组),其中包含要计算的单元格的行和列,而不是循环。 因此,若要查看最后一步,我们删除了两个循环行,
For cols = 1 To NumCols
For rws = 1 To NumRows
以及循环末尾的“下一步”行,
Next rws
Next cols
并在循环上方添加了行以使用数据数组变量:
rws = data(0)
cols = data(1)
我们要对 HPC_Execute 函数进行的最后一项更改与返回数据有关。 请记住,数据在宏中移动,每个宏将结果传递到下一个宏。 此工作簿的原始 VBA 代码设计为在桌面上运行,因此,由于每个单元格都经过计算,结果将粘贴到电子表格中。 由于此工作簿要在群集上运行,而不是将结果粘贴到电子表格中,因此我们需要收集计算结果并从函数中返回结果。
我们需要考虑另外一件事:当 HPC_Execute 函数在计算节点上运行时,它将计算表中特定单元格的结果。 然后,此结果将返回到桌面并发送到 HPC_Merge 宏。 但请记住,在异步框架中,结果可以按任何顺序发送回 -- 不一定是发送它们的相同顺序。 因此,我们需要告诉 HPC_Merge 宏中我们计算的表中的单元格。
我们可以通过在结果数据中包含行号和列号来执行此作。 与HPC_Partition宏 一 样,我们可以从HPC_Excecute宏返回数组。 这样做将允许函数不仅返回结果,还可以返回计算的单元格。
事实上,我们只需使用传递给函数的同一数组来返回结果,因为该 Array 已包含行号和列号。
若要存储结果,请更改代码,使其按如下所示读取:
Select Case Model
Case 1
data(2) = FullDiscountModel(360) / Face
Case Else
data(2) = BondPlusOptionModel(360) / Face
End Select
HPC_Execute = data
我们更改了在电子表格中插入值的代码,以改为在数据数组中存储结果值。 最后一行使用数据数组作为函数的返回值,因此结果(以及行号和列号)将发送到下一个宏。
因此, HPC_Execute 宏的完整最终代码应如下所示:
Public Function HPC_Execute(data As Variant) As Variant
Dim Prem As Double
Dim TempPIKdate As Date
Dim TempPIKfreq As Double
Dim rws As Integer
Dim cols As Integer
Dim Model As Integer
' set up and run through the table
Call ReadSheetVariables
TempPIKdate = PIKdate 'Save this value!
TempPIKfreq = PIKfreq 'Save this value!
Model = 1
If Range("PricingModel").Value2 = "Bond plus Option" Then
Model = 2
End If
rws = data(0)
cols = data(1)
Call ReadSheetVariables
PIKdate = TempPIKdate
PIKfreq = TempPIKfreq
PIKrate = CDbl(Range("table1").Offset(rws, 0).Value)
Prem = CDbl(Range("table1").Offset(0, cols).Value)
If Prem = 0 Then End
Ratio = CDbl(Face / Underlying / (1 + Prem))
Select Case Model
Case 1
data(2) = FullDiscountModel(360) / Face
Case Else
data(2) = BondPlusOptionModel(360) / Face
End Select
HPC_Execute = data
End Function
修改HPC_Merge宏
我们需要修改的最后一个宏是 HPC_Merge 宏。 请记住,此宏从群集接收单个计算结果。 我们希望使用它将结果插入表中。
HPC_Merge的输入将是从HPC_Execute返回的任何内容。 由于我们刚刚编写 了HPC_Execute 宏,因此我们知道返回值将是包含行号和列号的数组以及计算结果。 若要更新表,可以使用原始宏中的代码(这是我们刚刚在 HPC_Execute 函数中更改的代码)。
ScenarioAnalysis 模块中的原始代码行如下所示:
Range("cashtable").Cells(rws, cols).Value = FullDiscountModel(360) / Face
我们将重写该行,以使用传入到数据参数中的宏的值。 已完成 的HPC_Merge 宏应如下所示:
Public Function HPC_Merge(data As Variant)
Range("cashtable").Cells(data(0), data(1)).Value = data(2)
' update the status bar with the counters
RcvRecords = RcvRecords + 1
UpdateStatus
End Function
这是刚刚从HPC_Execute宏中删除 的代码 行。 我们已将行和列值(原始行中的 rws 和 cols)替换为数组中的行和列。 我们还将函数调用替换为存储在同一数组中的结果。
这些都是在群集上运行此工作簿时必须所做的所有修改。 虽然它似乎有很多步骤,特别是如果这是你第一次使用 HPC Services for Excel,请查看宏以查看已更改的总代码。 这真的不是很多。 在大多数情况下,这涉及到将原始宏复制和粘贴到 HPC 宏中。 为了支持异步计算,我们对代码进行了许多更改,但即使是相对简单的。 我们对此工作簿中的实际业务逻辑没有做任何更改,你甚至不必了解业务逻辑函数才能转换工作簿。 更重要的是,如果用户将来对业务逻辑进行更改,则不需要更改群集计算。
运行工作簿
若要测试对代码的更改,我们将首先在桌面上运行它。 与在第一个工作簿中一样,我们将向电子表格添加新按钮,并使用该按钮来运行计算。 然后,我们将指定头节点和文件共享,添加在群集上运行工作簿的按钮,然后在群集上运行工作簿。
添加在本地运行工作簿的按钮
添加在本地运行工作簿的按钮
(1) 打开 Excel 工作簿后,单击功能区上的 “开发人员 ”选项卡。
(2) 在 “开发人员 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。
(3) 单击按钮后,在电子表格上绘制一个矩形以在该位置插入按钮。 定位按钮后,将显示“ 分配宏 ”对话框。 !
(4) 在对话框中,从列表中选择宏 CalculateWorkbookOnDesktop ,然后单击“ 确定”。 请务必选择桌面宏 - 我们希望先测试工作簿,并在群集上运行工作簿之前找到任何错误。
(5) 右键单击新按钮,然后选择“ 编辑文本 ”以更改标签。
(6) 将标签命名为“桌面”或类似名称。
(7) 保存工作簿。
(8) 单击按钮以在桌面上运行工作簿。
如果存在任何错误,你将看到一个错误对话框,并突出显示包含错误的 VBA 代码部分。 针对上面列出的代码进行仔细检查,以解决任何错误。
如果没有错误,你将看到正在填充的表。 这些值应与在电子表格上单击原始按钮时相同。 同样,这在桌面上运行,因此它的速度应该与原始按钮相同-在大多数工作站上大约在 1-2 分钟之间。
由于我们正在使用宏框架代码运行,因此还会看到 Excel 状态栏中显示的计算进度。 与第一个工作簿一样,在桌面上运行,你将看到数字一起移动,因为在桌面上,工作簿以同步方式运行。
在群集上运行工作簿
接下来,我们将尝试在群集上运行工作簿。 为此,我们首先需要设置几个值来告知 Excel 如何联系群集。 这些值在 HPCControlMacros 模块**中定义。** 在此文件的顶部,需要填写两个值:群集计划程序和共享目录。 请记住,(桌面用户)必须具有对此共享目录的写入访问权限;和群集计算节点必须具有对目录的读取访问权限。 在大多数情况下,群集计算将在用户帐户下运行,但在连接到群集会话时可以使用其他用户帐户(稍后更多)。
指定头节点和共享目录
在功能区的 “开发人员 ”选项卡上,单击 “Visual Basic ”打开 VBA 编辑器。
在 VBA 编辑器中的项目树中,双击 HPCControlMacros 模块以打开宏文件。
对于群集计划程序,请使用群集头节点的名称 -- 这是将在网络上使用的计算机名称。 可以使用完全限定的名称(例如,headnode.mynetwork.com),但如果位于同一域中,则不需要这样做。
对于共享目录,请使用之前创建的共享目录的完整路径(在“开始之前”部分)。
现在返回到 Excel 电子表格。 我们将添加另一个按钮,这次在群集上运行计算。
添加在群集上运行工作簿的按钮
在“ 开发人员 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。
单击该按钮后,在电子表格的某个位置绘制一个矩形,以在该位置插入该按钮。 定位按钮后,将显示“ 分配宏 ”对话框。
在对话框中,从列表中选择宏 CalculateWorkbookOnCluster ,然后单击“ 确定”。
右键单击新按钮,然后选择“ 编辑文本 ”以更改标签。
将标签命名为“Cluster”或类似名称。
保存工作簿。
单击该按钮以在群集上运行工作簿。
如果这是首次使用 Excel 或使用任何其他应用程序运行任何群集作业,你将看到用户身份验证对话框。 键入用户名和密码,并选中该框以保存凭据。 如果要以其他用户身份在群集上运行计算,可以输入不同的用户帐户设置。
注意:请确保用户帐户(标准用户帐户或要使用的任何帐户)被指定为 HPC 群集管理器中的群集用户。
如果出现任何错误,你将看到一个弹出对话框。 如果桌面计算正常工作,此时最有可能的错误与为群集头节点和共享目录创建的设置有关。 仔细检查这些设置,然后重试。
如果没有错误,你将看到群集计算开始。 首先,在客户端库创建 HPC 会话进行计算时,会短暂暂停。 接下来,客户端库将调用 HPC_Initialize 宏,你将看到该表已清除。 如果查看 Excel 状态栏,你将看到首先将所有记录发送到群集;第二个数字将跃升至 105 个(表中有 105 个单元格)。 之后,你将看到另一个短暂的暂停,然后返回任何结果。 每次计算节点启动 Excel 并加载工作簿时,都会发生此暂停。
在此暂停后,你将看到结果开始填充表,状态栏将开始显示返回的结果数。 由于这是一个长时间的计算,因此很可能你会看到返回无序的结果;表中将有空白,然后填充空格。 这再次是因为计算是异步的,某些计算节点的计算速度比其他节点要快。 最终,该表将完全填充,状态栏将显示计算时间。
即使 HPC 群集中只有两个计算节点,计算的速度应该比在桌面上运行要快得多。 如果有四个节点、八个节点或更多节点,则计算速度应非常快。 始终可以再次单击“桌面”按钮,将桌面计算的性能与群集计算进行比较。
Cluster-Enabled Excel 工作簿的最佳做法
以下说明介绍了在设计 Excel 工作簿以用于 EXCEL HPC Services 时要记住的一些重要注意事项。 此说明适用于使用从 VBA 或 .NET ExcelClient 库运行的 HPC/Excel 宏框架的工作簿。
群集应用程序性能
在 HPC 群集上设计用于计算的应用程序时,通常会将所需的工作划分为单个任务,并将这些任务发送到群集进行计算。 使用 HPC 的性能优势来自并行化-让许多计算节点同时工作。
HPC 计划程序(头节点)负责将这些任务分发到计算节点进行处理。 但是为了确保计算节点永远不会等待工作,应尝试确保 HPC 计划程序始终包含要处理的任务列表。 如果计划程序必须等待下一个处理任务,则一个或多个计算节点将处于空闲状态(不执行任何作),并且不会获得 HPC 群集的最佳利用率。
可以通过尽快发送计算请求来确保计划程序和计算节点始终有足够的工作要做。 在 HPC/Excel 宏框架中,请求随HPC_Partition宏一起发送。 因此,请务必确保分区宏尽可能快。 如果可以设计分区宏来快速发送任务,请确保充分利用群集,并从 HPC 获得最佳性能。
当群集计算节点上的单个计算完成时,结果将发送回桌面应用程序。 在 HPC/Excel 宏框架中,使用HPC_Merge宏处理结果。 出于两个原因,你希望使此宏尽可能快。 首先,如果合并宏处理结果速度较慢,则这可能会导致群集资源不必要。 其次,结果处理速度缓慢会降低总体计算速度,从而减少使用 HPC 的好处。 如果确保合并宏快速处理结果,你将释放任何群集资源以供其他使用,并使整体用户体验变得更好。
改进 HPC/Excel 宏性能
在本部分中,我们将介绍一些可以采取的步骤来帮助确保已启用 HPC 的 Excel 工作簿尽可能高效,以便在 HPC 群集上运行计算时获得最佳性能。
此列表并不全面,但你可能会发现一些适用于工作簿的提示。 请记住,这些是优化 - 设计已启用 HPC 的 Excel 工作簿时的最佳方法是先设计计算,尽可能简单:然后,在群集上正确运行工作簿后,进行此处介绍的更改类型以提高整体性能。
避免在分区和合并宏中打开和关闭资源
打开和关闭外部资源(如日志文件或数据库连接)可能需要很长时间,并且可能会减慢分区和合并宏的速度。 此外,如果可能,应避免在这些宏中读取大型数据集。 例如,你不想打开数据库连接,搜索记录,并为每个对 Partition 宏的调用找到一条记录。
更好的方法是在 Initialize 宏中打开这些资源一次,并执行任何初始处理步骤。 例如,如果需要从数据库加载大型数据集,请创建数据库连接并在 Initialize 宏中执行查询。 然后在分区宏中,只需移动到下一条记录即可。 可以在 Finalize 宏中关闭数据库连接。
同样,如果要在合并宏中写入日志文件,请考虑在 Initialize 宏中打开该文件。 在合并宏中,可以一次写入一个结果,然后在“完成”宏中关闭日志文件。
避免在合并宏中重新计算电子表格
如果要在合并宏中将结果插入电子表格中,则可能无意中重新计算电子表格 -- 如果电子表格非常复杂,这可能会降低处理速度。
默认情况下,每当更改单元格的值时,Excel 都会重新计算电子表格,包括是否从 VBA 宏更新单元格。 Excel 将仅重新计算受更改影响的电子表格的那些部分,因此,如果单元格本身处于此状态,则这并不重要。 但是,如果单元格用作较大计算的一部分,或者用作图表数据,则重新计算速度可能很慢。
如果要更新合并宏中的任何电子表格单元格,请考虑禁用自动重新计算。 可以在 Initialize 宏中禁用自动重新计算。 然后在“完成”宏中,可以重新计算整个电子表格,然后重新启用自动计算。
在 Initialize 宏中,可以使用
Application.Calculation = xlCalculationManual
然后,可以使用 重新计算电子表格并在 Finalize 宏中重新启用自动计算
Application.Calculate
Application.Calculation = xlCalculationAutomatic
插入数据块而不是单个值
从 VBA 更新电子表格单元格时,这涉及许多内部步骤,并且速度可能比预期慢。 几乎总是最好将多个单元格更改为单个块,而不是一次一个。
如果 Execute 宏返回大量值(例如 100 个数字的数组)可通过两种方法将此数据插入电子表格中。 可以在循环中一次插入一个:
For i = 1 To 100 Step 1
Cells(1, i).Value2 = v(i)
Next i
或者,可以将整个范围作为数组插入:
Range(Cells(2, 1), Cells(2, 100)).Value2 = v
插入一个单元格所需的时间大约与插入单元格块的时间相同。 因此,如果你有一个 100 个值的数组,第一种方法就是一次在循环中插入一个单元格,只要第二种方法,就最多需要 100 次,将单元格块作为数组插入。
关闭屏幕更新
如果要对合并宏中的电子表格进行任何更改(如果要更新单元格或更改图表数据)关闭屏幕更新可以减少处理时间。 这很有效,因为 Excel 在更新屏幕和用户界面时会消耗一些时间。
可以在 Initialize 宏中禁用屏幕更新,并在“完成”宏中重新启用屏幕更新。 在 Initialize 宏中,使用
Application.ScreenUpdating = False
在“完成”宏中,使用
Application.ScreenUpdating = True
这将在计算过程中禁用屏幕更新,并在计算完成后重新启用更新。 在“完成”宏中重新启用屏幕更新时,Excel 将自动刷新屏幕。
使用数据结构暂时保存内存中的信息
如果收集处理信息(在分区宏中执行的作)非常耗时,或者如果需要在合并宏中执行非常复杂的结果处理后处理,请考虑在计算过程中使用数据结构在内存中存储信息。
例如,如果 Partition 宏中的每个计算请求都需要一个非常复杂的数据库查询,这可能会降低处理速度并导致性能不佳。 在这种情况下,最好在计算开始前在 Initialize 宏中执行此复杂处理。
如果在 Initialize 宏中执行此复杂处理,则可以将每个计算请求存储在 VBA 中的数据结构中,例如变量数组。 然后在分区宏中,只需返回数组中的下一项即可。 这将有助于确保分区宏尽可能快。
同样,如果必须在结果到达时对结果执行非常复杂的后处理,建议在合并宏中的数据结构中存储结果,然后在计算完成后在“完成”宏中处理结果。
例如,如果要将结果存储在数据库中,但每个数据库写入都需要一个复杂的 insert 语句,则最好将此处理移动到 Finalize 宏。 可以创建数据结构(如 Variants 数组)并在合并宏中,只需将每个结果插入到数组中。 然后在 Finalize 宏中,可以循环访问数组并一次性执行必要的数据库写入作。
最终说明
将现有工作簿转换为使用 HPC Services for Excel 是一种艺术,然后是一种科学。 具体详细信息 -- 要移动的代码和移动位置 - 将始终取决于特定工作簿。
但是,现在应了解所涉及的概念,并了解为何使用异步宏框架。 若要将任何工作簿转换为使用 HPC Services for Excel,需要以基本相同的方式创建并行计算:将宏框架函数添加到工作簿中,并填写相应的代码。
可以在自己的工作簿中使用本文随附的“框架”宏文件。 按照上述示例中使用的步骤标识工作簿中的迭代代码,并将其移动到相应的宏。 请记住在桌面上测试计算,以在群集上运行计算之前识别任何错误。
还应了解使用 HPC Services for Excel 在 HPC 群集上运行 Excel 工作簿的性能优势。 在本文中,我们从一个已经相当快的工作簿开始,计算整个工作簿在桌面上花费了大约 1-2 分钟。 但是,即使该工作簿在 HPC 群集上可以运行两倍的速度、四倍的速度或更快的速度。 实际性能取决于群集中的可用计算节点数,并且始终可以通过向群集添加更多计算节点来提高性能。
使用相对较少的工作,可以将许多长时间运行的 Excel 工作簿转换为在具有 HPC Services for Excel 的 Microsoft HPC 群集上运行。 对于非常长时间运行的工作簿(需要数小时甚至数天来计算的工作簿),使用适用于 Excel 的 HPC Services 可能会导致性能真正显著改善。