使用 Microsoft HPC Pack 加速 Excel:为 Microsoft HPC Pack 生成 VBA 应用程序和工作簿

摘要

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 存储库的后续部分中找到一些示例文件。 示例文件包括可以导入到工作簿中的主干宏文件,以简化开发过程。 还有一个基本工作簿,我们将使用该工作簿作为为 Excel 的 HPC Services 开发工作簿的起点。

结构和格式设置

本文中的屏幕截图全部来自 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) 中包含的 UDF) 加快 Excel User-Defined Functions 的计算速度 C 或 C++ 编程、Excel 扩展库 (XLL) 开发 Cluster-Enabled Excel User-Defined 函数

我们将在以后的文章中介绍其中每个编程模型。 今天,我们将讨论第一个模型:仅使用 Excel VBA 并行计算 Excel 工作簿。 即使你希望使用 Excel 和 Microsoft HPC Pack 进行更复杂的开发,这也是一个很好的起点:这是让 Excel 应用程序在 HPC 群集上运行的最简单、最快的方法,它可以帮助你了解使用 HPC Services for Excel 的要求和优势。

为了支持 Excel 开发人员,适用于 Excel 的 HPC Services 包含一组库,可以直接在 Excel 中使用这些库(在桌面上)在 Microsoft HPC 群集上生成和运行 Excel 工作簿。 使用这些库,无需了解 .NET 编程或 C#。 所有工作都可以通过编写 VBA 宏从 Excel 完成。 使用此框架,只需编写 VBA 代码并在群集上执行工作簿,即可大幅加快 Excel 工作簿的计算速度。

HPC Services for Excel 包括一个客户端/服务框架,该框架专为在桌面上的 Excel 中使用而设计。 此框架有两个重要部分:安装在桌面上的客户端库:以及安装在群集计算节点上的服务库。 安装 Microsoft HPC Pack 时默认安装这些库,因此无需执行任何自定义配置来使用这些库。

安装客户端和服务库后,只需将一些 VBA 宏添加到工作簿,即可为群集生成和运行 Excel 工作簿。 我们将这些新的 VBA 宏描述为 宏框架。 当我们提到宏框架时,我们专门描述了你添加到工作簿以支持 HPC 群集上的计算的新宏。

在讨论特定宏之前,值得强调一些影响宏构造方式及其工作方式的关键概念。

异步计算

在开发 HPC Services for Excel 工作簿并使用宏框架时,要了解的最重要概念是 异步计算

典型的 Excel 应用程序 以同步方式运行。 也就是说,按按钮运行宏,宏将执行一些 VBA 代码。 在此代码中,你可以对电子表格进行更改、调用其他 VBA 函数、访问数据库或其他任何内容。 但你预计宏将从头到尾运行,然后完成。

异步模型略有不同。 在此模型中,可以编写 VBA 函数,但不直接调用它们。 相反,客户端库(与 HPC Services for Excel 一起安装的代码)将调用 VBA 函数。

如果你熟悉 VBA 中的 Excel 事件,那么你已经知道此模型。 例如,如果编写在 Workbook.Open 事件上运行的 VBA 函数,则知道每当打开工作簿时,代码都会运行。 但你不直接调用该代码;相反,Excel 会在打开工作簿时自动调用你编写的函数。

异步编程模型的工作方式相同。 编写将在计算期间调用的函数,但无需自己调用它们,而是等待 Excel(在本例中为 HPC Services for Excel 库)调用函数。

迭代计算和并行计算

另一个重要概念是迭代计算。 迭代计算是指对不同数据集运行相同基本计算步骤的一种计算类型。

迭代计算的最常见示例是蒙特卡罗模拟。 在蒙特卡罗模拟中,通常基于一些随机输入值(如预期回报)进行复杂的计算,例如生成证券价格。 蒙特卡罗模拟运行相同的计算数千次,每次使用不同的随机输入值。

这是一个迭代计算,因为每次使用不同的随机输入值计算价格时,都会再次运行整个计算。 这些计算是独立的:也就是说,在每个单独的计算中,没有对任何先前或后续计算的引用。

另一个常见示例涉及对数据库中的一组记录运行计算。 在这种情况下,你可能具有基于某些参数的复杂计算(例如保险或精算模型),例如保险单持有人的年龄和性别。 迭代计算将为成千上万的个人保单持有人运行相同的模型。 每个计算都独立于所有其他计算。

我们在此处讨论的宏框架旨在用于此类迭代计算。 这一点很重要,因为在 HPC 群集上运行计算时,我们将 并行 运行该计算--我们将多次运行相同的计算,但我们会在群集计算节点上同时运行多个计算实例。

如果计算不独立(如果任何单个计算的结果依赖于我们先前运行的计算)则不起作用。 因此,我们在此处讨论的框架仅适用于可并行化的迭代独立计算。

宏框架

若要支持 HPC Services for Excel,工作簿需要包含一组实现异步函数的宏。 这是宏框架。 框架中使用了六个宏,可以添加到任何 Excel 工作簿。 这六个宏,以及它们的用途的简要说明,

宏名 操作
HPC_Initialize 执行任何预计算或初始化步骤
HPC_Partition 收集单个计算步骤所需的参数
HPC_Execute 执行一个计算步骤,作为更大的整体计算的一部分
HPC_Merge 处理单个计算步骤的结果
HPC_Finalize 执行任何后计算处理
HPC_ExecutionError 处理计算产生的错误

(如果这些描述不立即清楚,请不要担心:我们将在) 下面更详细地描述每个函数。

必须将相同的宏添加到任何电子表格中,以支持群集上的计算,但宏中的内容(特定计算函数)可能有所不同。 为了简化此过程,本文随附的下载文件包括一个可导入工作簿的“主干”宏文件。 准备好基本宏后,可以根据需要填写它们以支持计算。

每个宏在宏框架中都有特定的用途,每个宏将在群集计算期间由 HPC Services for Excel 客户端库调用。 运行计算时,将在特定点调用每个宏。 典型的计算运行方式如下:

img

群集计算运行时,客户端库首先将调用 HPC_Initialize 宏。 用于处理任何必需的初始化;例如,你可能想要从电子表格中清除旧结果。

接下来,计算通过三个宏运行: HPC_PartitionHPC_ExecuteHPC_Merge。 在上图中,它们显示为循环。 这不是在计算过程中实际发生的情况,但从逻辑上讲,你可以将其视为一个循环。 首先,客户端库调用 HPC_PartitionHPC_Partition 旨在收集单个计算步骤所需的任何数据。 例如,如果要逐个计算一组行, HPC_Partition 可能会返回单个步骤的行号:第一行 1,然后是第 2 行,依此推。

接下来,调用 HPC_Execute 宏。 此宏运行实际计算。 如果逐行计算,则这将计算单个行。 HPC_Execute 将返回计算结果:例如,它可能会返回行中的最后一个单元格,这是较长计算的最终结果。

HPC_Execute后,客户端库将调用 HPC_Merge。 计算的任何 HPC_Execute 都将发送到 HPC_MergeHPC_Merge宏旨在获取这些结果并将其返回到桌面上运行的电子表格。 在 HPC_Merge 宏中,可以将计算结果插入回电子表格中,也可以将其写入日志文件。

这三个宏( HPC_PartitionHPC_ExecuteHPC_Merge )将针对计算中的每个步骤多次调用,直到整个计算完成。 这些宏实现上面讨论的 迭代 计算模型。 在单个工作簿计算过程中,可能会多次调用宏,但每次调用它们都表示单个计算步骤或 迭代

完成最后一个计算步骤后,客户端库将调用 HPC_Finalize。 可以使用此宏执行任何后计算处理:例如,你可能希望使用另一个 VBA 宏查找所有先前计算步骤的平均值。

仅当计算遇到一些错误时,才使用第六个也是最后一个宏 (HPC_ExecutionError)。 如果是这样,则调用此宏,你可以添加一些错误处理代码 -- 例如,你可能会向用户显示一个弹出对话框,警告他们错误。

在上图中, HPC_Execute 宏以橙色突出显示。 这表示此宏有些不寻常。 所有其他宏在工作站上的桌面上的 Excel 中运行。 但是,当我们在 HPC 群集上运行计算时, HPC_Execute 宏实际上在群集计算节点上运行。

这就是 HPC Services for Excel 和宏框架支持群集计算的方式。 运行计算时,我们会在同一工作簿(即桌面上的工作簿)中写入所有宏,但工作簿将复制到群集计算节点, HPC_Execute 宏将在其中一个计算节点上运行。

了解这一点很重要,因为它对工作簿在计算期间如何使用和管理数据有一些影响。 这三个main计算宏(HPC_PartitionHPC_ExecuteHPC_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 服务

  1. 在 HPC 群集管理器中,单击“ 诊断”。

  2. “测试”中,展开“ Microsoft ”,然后选择“ Excel”。 1539324941017

  3. 在视图窗格中,双击“ Excel 运行器配置测试”。

  4. 在配置对话框中,选择“ 所有节点 ”,然后单击“ 运行”。 1539324956508

  5. 导航窗格中, 单击“ 测试结果 ”以查看测试进度。 正在运行的测试将显示在视图窗格中。

  6. 测试完成后,可以在“main”窗口中单击测试以查看结果。

如果测试显示 “失败”,请双击测试并尝试更正弹出窗口中的任何错误。 你可能会看到的常见错误是,Excel 未安装在计算节点上,或者 Excel 尚未激活。 进行所需的任何更改,然后使用上述说明重新运行诊断测试。

当测试显示 “成功”时,你已准备好继续。

最后需要的是共享目录。 在 HPC 群集上计算工作簿时,每个群集计算节点实际上都会加载并运行工作簿的副本。 若要支持这一点,需要创建一个共享目录,该目录对群集计算节点和桌面都可见。

如果有权访问群集头节点,请在头节点上创建共享目录。 这很方便,因为你知道群集计算节点可以访问头节点。 如果无法在头节点上创建共享目录,请在域中的任意位置创建桌面和群集计算节点都可以访问的共享目录。

在桌面上

在桌面上,需要安装 Excel 和 Microsoft HPC Pack 客户端实用工具, (安装客户端实用工具需要管理员权限) 。 若要安装客户端实用工具,请在桌面上运行 HPC Pack 安装程序。 它将提供一个选项,用于仅安装客户端实用工具。

1539325029907

摘要

现在,应已准备好以下各项:

  1. 已安装并配置的 Microsoft HPC 群集。

  2. Excel 2016 (或更高版本) 安装在群集计算节点上。

  3. 网络共享目录。

  4. Excel 2016 (或更高版本) 和桌面上安装的 Windows HPC Pack 客户端实用工具。

准备就绪后,可以使用 HPC Services for Excel 开始生成在群集上运行的工作簿。

第 I 部分:从头开始生成工作簿

此部分包括以下部分:

  1. 启动新工作簿

  2. 准备工作簿

  3. 设计群集计算

  4. 在群集上运行工作簿

  5. 最后步骤:用户界面

启动新工作簿

使用新工作簿启动 Excel。 我们将使用 VBA,因此请确保 Excel 功能区上提供了“ 开发人员 ”选项卡。

打开“开发人员”选项卡

  1. 单击 Excel 功能区上的“ 文件 ”选项卡。

  2. 单击“选项”。

  3. 在“Excel 选项”对话框中,选择左侧的“ 自定义功能区 ”。

  4. 选中“ 开发人员 ”旁边的框,然后单击“ 确定 ”关闭对话框。

1539325069612

准备工作簿

若要准备工作簿,需要导入宏文件并添加 HPC 引用。

若要生成基本工作簿,需要两组宏。 第一组宏实现框架函数 (上述) 概述部分所述的函数。 计算期间,HPC Services for Excel 客户端库使用这些宏。 第二组宏包含“控制”函数,这些宏运行实际计算。

示例文件包括可用于入门的每个基本“主干”宏文件。 你不必使用这些文件,你也可以编写自己的文件(如果你愿意的话),但是宏本身是通用的,你可能会发现使用这些文件就是你所需要的。 还可以修改这些文件,以便对特定工作簿进行任何所需的更改。

我们需要添加对此项目的两个引用才能使用 HPC 服务: Microsoft_Hpc_ExcelMicrosoft_Hpc_Scheduler_PropertiesMicrosoft_Hpc_Excel 是客户端库,它提供可用于控制群集计算的对象。 Microsoft_Hpc_Scheduler_Properties 具有我们所需的一些特定于群集的字段和类型的定义。

注意:在上面的概述部分中,我们将工具描述为客户端/服务器框架;安装 HPC Pack SDK 时已安装客户端库,现在可在 VBA 中使用。

导入宏文件并添加引用

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器中,右键单击树视图顶部的 “VBA 项目 ”,然后单击“ 导入文件”。 1539325159545

  3. 在对话框中,找到下载的项目文件。 转到目录“第一个工作簿”,选择文件“HPC Excel Macros.bas”,然后单击 “确定”。 这会将第一个宏文件添加到项目中,它将在 VBA 项目窗口的 Modules 文件夹中提供。

  4. 重复步骤 1-3 以导入“HPC Control Macros.bas”,然后单击“ 确定”。 这会将第二个宏文件添加到项目中。 1539325199006

  5. 验证这两个宏文件是否显示在 VBA 项目窗口中: 1539325222876

  6. 在 VBA 编辑器窗口中,单击“ 工具”,然后单击“ 引用 ”打开项目引用对话框。

  7. 在可用引用列表中,向下滚动,直到找到 Microsoft (R) HPC Pack 程序集,然后选择每个引用旁边的检查框。 选中这些框后,对话框将如下所示: 1539581076740

  8. 单击“ 确定” 关闭项目引用对话框。

如果在列表中找不到此项,请单击“ 浏览 ”。并找到文件 %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 个步骤的计算

  1. 在 VBA 项目窗口的 “模块” 文件夹中,双击“ HPCExcelMacros”。

  2. 在文件顶部的“第 1 部分:变量和常量”中,添加名为 SentRecords 的计数器变量,如下所示:

    '==========================================================
    '
    ' Section 1: Variables and constants
    '
    '==========================================================
    
    Dim SentRecords As Integer
    
  3. 向下滚动,直到找到 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
    
  4. 向下滚动,直到找到 宏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
    
  5. 保存该工作簿。

注意:由于新工作簿包含宏,因此需要将其保存为启用宏的工作簿 (XLSM 文件) 或二进制工作簿 (XLSB 文件) 。 任何一个都没关系。 通常,我们建议将工作簿保存为二进制 (XLSB) 文件,因为它们更小,效率更高。

现在,我们有一个计算,它将运行 100 个步骤,然后完成。 只需完成刚刚进行的修改即可创建 Excel 群集计算。

在本地运行工作簿并浏览宏

现在我们有了一个计算,我们可以添加一个按钮来运行工作簿,然后在桌面上运行工作簿。 若要查看宏的工作原理,我们将向宏主干添加一些新代码段,然后重新运行工作簿以查看所做的更改。 在群集上运行工作簿之前,我们将在桌面) 本地 (测试工作簿。

添加在本地运行工作簿的按钮

添加在本地运行工作簿的按钮

  1. 打开 Excel 工作簿后,单击功能区上的“ 开发人员 ”选项卡。

  2. 在“ 开发工具 ”选项卡上,单击“ 插入 ”,然后选择按钮控件,即列表中的第一个控件。 1539325548400

  3. 单击按钮后,在电子表格上的某个位置绘制一个矩形,以将按钮插入该位置。 定位按钮后,将显示“ 分配宏 ”对话框。 1539325577358

  4. 在对话框中,从列表中选择宏 CalculateWorkbookOnDesktop ,然后单击“ 确定”。 请务必选择桌面宏 -- 我们希望先测试工作簿并找到任何错误,然后再在群集上运行它。

  5. 右键单击新按钮,然后选择 “编辑文本 ”以更改标签。

  6. 将标签命名为“桌面”或类似名称。

  7. (可选)可以通过右键单击按钮并选择“分配宏”来验证是否已 分配正确的宏。 在对话框中,验证是否选择了 CalculateWorkbookOnDesktop

  8. 保存该工作簿。

  9. 单击按钮在桌面上运行工作簿。

如果存在任何错误,你将看到一个错误对话框,并且将突出显示包含错误的 VBA 代码部分 -- 返回上述部分,检查所有内容看起来都正确。

如果一切正常,则不会发生任何操作,因为我们的工作簿不执行任何操作。 这有点令人不满意,因此让我们再做一些更改,以便查看计算结果。

修改HPC_Excecute和HPC_Merge宏

请记住,信息流从收集计算 ) 所需参数的HPC_Partition (到 运行计算) HPC_Execute (,再到 HPC_Merge () 处理结果。 执行计算时,从 HPC_Partition 宏返回的任何内容都用作 HPC_Execute 宏的输入。 HPC_Execute宏旨在使用该输入数据,执行一些计算步骤,然后返回结果。 然后,结果将传递到 HPC_Merge,后者可以在电子表格中插入结果。

在前面的步骤中,你向 HPC_Partition 宏添加了代码以更新计数器变量。 因此,在我们的示例中, HPC_Partition 返回计数器变量的值,并将该值传递给 HPC_Execute。 为了说明信息流并使工作簿返回一些结果,我们只需通过宏传递此计数器值。 我们将向 HPC_Execute 宏添加一行代码,以便它接受输入并将其作为返回值传递。 然后,计数器变量的值将传递到 HPC_Merge 宏,我们将修改该宏,以便它将计数器的值插入到电子表格中。

修改HPC_Execute和HPC_Merge

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器的项目树中,双击 HPCExcelMacros 模块以打开宏文件。

  3. .滚动到 HPC_Execute 宏,并添加一行代码,将输入值作为返回值传递。 宏应如下所示:

    Public Function HPC_Execute(data As Variant) As Variant
    
        HPC_Execute = data
    
    End Function
    
  4. 滚动到 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 中,并使用不同的行号。

  5. 现在返回到电子表格,单击“桌面”按钮以运行计算。

你将看到第一列填满了 1-100 的数字,这些数字是计数器值。 每次单击按钮时,都会填充相同的数字,因此很难判断它正在运行;我们可以再做一个小的更改,以查看数字填充。

修改HPC_Initialize宏

当我们第一次运行工作簿时,将调用 HPC_Initialize 宏。 在前面的步骤中,我们添加了一行代码来重置计数器变量。 我们还可以使用此宏擦除在上一次运行期间插入到电子表格中的值。

修改HPC_Initialize宏

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器的项目树中,双击 HPCExcelMacros 模块以打开宏文件。

  3. 滚动到 HPC_Initialize 宏**.** 添加一行代码,以清空第一列。 宏应如下所示:

    Public Function HPC_Initialize()
    
        Range("A:A").Value = ""
        SentRecords = 0
    
    End Function
    
  4. 现在返回到电子表格,单击“桌面”按钮以运行计算。

现在应该很明显,每次单击宏时都会在列中填充数字。 如果有任何错误,你将看到一条错误消息,并突出显示包含错误的 VBA 代码 -- 双检查它与上面列出的代码匹配,然后重试。

在群集上运行工作簿

现在,我们有了一个使用 HPC 宏框架的非常简单的 Excel 计算,我们可以在群集上运行它。 为此,我们首先需要设置几个值,以告知 Excel 如何联系群集。 这些值在 HPCControlMacros 模块中定义。** 在此文件的顶部,需要填写两个值:群集计划程序和共享目录。 请记住, (桌面用户) 必须对此共享目录具有写入访问权限;和 群集计算节点必须具有对目录的读取访问权限。 在大多数情况下,群集计算将在用户帐户下运行,但在连接到群集会话时,可以使用其他用户帐户, (稍后) 。

指定头节点和共享目录

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器的项目树中,双击 HPCControlMacros 模块以打开宏文件。

  3. 对于群集计划程序,请使用群集头节点的名称 -- 这是将在网络上使用的计算机名称。 可以使用完全限定的名称 (例如,headnode.mynetwork.com) ,但如果在同一域中,则不需要这样做。

  4. 对于共享目录,请使用之前创建的共享目录的完整路径, (“开始之前”部分) 。

例如,在群集中,计划程序名称为“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 电子表格。 我们将添加另一个按钮,这次用于在群集上运行计算。

添加在群集上运行工作簿的按钮

  1. 在“ 开发工具 ”选项卡上,单击“ 插入 ”,然后选择按钮控件,即列表中的第一个控件。 1539326054654

  2. 单击按钮后,在电子表格上的某个位置绘制一个矩形,以将按钮插入该位置。 定位按钮后,将显示“ 分配宏 ”对话框。

  3. 在对话框中,从列表中选择宏 CalculateWorkbookOnCluster ,然后单击“ 确定”。 1539326062393

  4. 右键单击新按钮,然后选择 “编辑文本 ”以更改标签。

  5. 将标签命名为“Cluster”或类似名称。

  6. 保存该工作簿。

  7. 单击按钮在群集上运行工作簿。

  8. 如果这是你第一次使用 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 状态栏

  1. 返回 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
    
  2. 接下来,我们将添加一个名为“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
    
  3. 我们需要初始化变量。 与原始计数器一样,我们将在 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”宏,将消息写入状态栏。

  4. 向下滚动到 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
    
  5. 现在向下滚动到 HPC_Merge 宏。 在这里,我们想要更新新的计数器(表示返回给桌面的消息的计数器),并再次更新状态栏。 因此,请修改 HPC_Merge 宏:

    Public Function HPC_Merge(data As Variant)
    
        Cells(data, 1).Value = data
        RcvRecords = RcvRecords + 1
        UpdateStatus
    
    End Function
    
  6. 向下滚动到 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”字段设置为 Timer 时,我们使用当前时间(以秒为单位)对其进行设置。 在 UpdateStatus 宏中,我们使用“StartTime”和“FinishTime”来计算总计算时间。

  7. 保存工作簿

  8. 返回 Excel 电子表格,然后单击“群集”按钮。

在计算运行时,你将看到状态栏更新为数字;首先,你将看到第二个数字 (发送的计算数) 跃升至 100,然后在短暂延迟后,你将看到第一个数字 (收到的结果数) 攀升至 100。

1539326872659

你在此处看到的是我们上面讨论的异步消息传送。 由于 HPC_Partition 宏的速度太快,客户端库能够在任何计算完成之前发送所有 100 个请求。 有一个短暂的延迟 - 延迟是启动 Excel 并加载工作簿的计算节点。 每个计算节点启动 Excel 后,计算结果的速度相当快,第一个数字可快速达到 100。

如果单击“桌面”按钮,将看到数字以不同的方式移动。 在桌面上,客户端库调用相同的宏,但它 (同步执行,即,而不是异步) 。 若要运行桌面计算,客户端库将依次调用每个宏(首先 HPC_Partition,然后 HPC_Execute,然后 HPC_Merge ),但在这种情况下,它将等待每个宏完成,然后再调用下一个宏。 这就是为什么在桌面上运行时,会看到这两个数字一起移动。

第 2 部分:转换“桌面”工作簿以在 HPC 群集上运行

在上一部分中,我们从头开始构造了一个工作簿,用于在 HPC 群集上进行计算。 在本部分中,我们将获取一个现有工作簿,并将其修改为在群集上运行。 如果你遵循最后一部分,你应该了解需要什么 -- 虽然有很多单独的步骤,涉及的实际工作非常简单。 总之,我们:

  • 添加了两个宏文件。

  • 添加了对 VBA 项目的两个引用。

  • 对宏进行了一些修改。

  • 添加了用于运行电子表格的按钮。

转换现有工作簿的工作量大致相同。 无需添加很多新的 VBA 代码,并且大多数代码都可以重复使用相同的主干宏文件。 在大多数情况下,转换现有工作簿涉及移动代码 -- 将代码从现有宏复制到 HPC 宏中。

转换现有工作簿的难点在于确定要移动的代码以及代码应移动到何处。 Excel VBA 是一个全面的开发环境,可通过多种方式生成 Excel 应用程序:不同的开发人员可能以许多不同的方式编写同一应用程序。

在本部分中,我们将获取一个现有工作簿,并将其转换为在群集上运行。 你应该阅读本部分作为指南或示例,然后尝试将这些概念应用于你自己的工作簿。 在本例中,我们有一个相当简单的示例 (尽管它基于用于解决) 实际业务问题的真实工作簿。 对于几乎所有工作簿,过程都是相同的,但具体的详细信息会有所不同。

分析工作簿

在 Excel 中,打开“Second Workbook”目录中的工作簿“ConvertiblePricing.xlsb”。 你可能会收到有关宏的警告;如果是这样,请单击“启用宏”继续。 1539326930792

此工作簿根据左侧“模型参数”部分中的值计算可转换债券价格。 右侧的表用于了解参数(转换价格和优惠券率的更改)对最终价格的影响。

单击电子表格中的“计算表”按钮。 在计算每个价格时,你会看到表格缓慢填充。 在典型的工作站上,填写完整的表需要 1-2 分钟。

若要了解单击按钮时此工作簿的作用,可以演练 VBA 代码。

标识宏并查看代码

  1. 右键单击“计算表”按钮,然后选择“ 分配宏”。 1539326971647

  2. 宏对话框显示按钮当前使用的宏。 在对话框中,单击“ 编辑” 以跳转到代码中。 应会看到“CalculateTable”宏的代码。 这是单击电子表格中的按钮时执行的代码。 1539326985898

查看此宏,可以看到几个不同的部分。 首先,有一些变量声明。 接下来是一些初始化代码。 最后,有一个计算表的部分 -- 我们可以确定它循环访问表中的行和列的位置,并填充值。

此宏相当短,但如果你查看循环,你会发现它调用一些其他函数 -- 函数“FullDiscountModel”和“BondPlusOptionModel”。 这些是实际的计算例程,即为此工作簿提供支持的业务逻辑。

在 VBA 编辑器中,可以通过右键单击名称并选择“定义”,跳转到任何函数的源。 如果右键单击“FullDiscountModel”并选择“定义”,则会看到实际计算相当复杂。 但是,若要将工作簿转换为在群集上运行,我们不必修改甚至理解该计算。 我们只需要担心控制代码,即包含 循环的函数。

此工作簿设计非常简洁,业务逻辑包含在单独的函数中。 在其他情况下,所有这些代码可能位于单个宏中,这会使工作簿更难理解。 但在任一情况下,转换此工作簿所要执行的操作都是获取控制代码(启动例程和循环函数),并将其移动到 HPC 宏。

我们还知道,在此工作簿中,计算是独立的。 也就是说,计算表的每个单元格时,不引用表中的任何其他单元格。 这意味着工作簿是迭代的,它将支持并行化 (,如上面的概述部分所述,) 。 计算不一定总是很明显是独立的。 确定这一点的一种方法是编写一些测试函数,例如,我们可以重新编写“CalculateTable”宏 (在电子表格) 中按下按钮时调用的宏,以便仅运行一个计算或表中的一个单元格。 这样,我们可以确定结果是否一致,如果一致,我们可以确定计算是否实际上是独立的。

总之,对于支持在 Microsoft HPC 群集上执行的任何工作簿最重要的功能如下:

  • 工作簿是迭代的:它多次运行相同的计算,具有不同的输入数据集;或者一次运行一个计算,例如表中的行或单元格。

  • 单个计算是独立的:也就是说,一个计算的结果不会影响任何其他计算。

这两者都适用于此工作簿,因此非常适合在群集上运行。

准备工作簿

与上一个工作簿中一样,我们将进行的第一个更改与设置 VBA 环境有关。 我们可以使用主干宏文件来节省一些时间 -- 这些文件将包括计算所需的 HPC 宏。

在上一个工作簿中,我们对主干宏文件进行了一些更改,以添加一些用户反馈, (在 Excel 状态栏中显示计算进度) 。 在这第二个工作簿中,主干宏文件已包含该状态条形代码。

导入宏文件并添加引用

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器中,右键单击树视图顶部的 “VBA 项目 ”,然后单击“ 导入文件”。

  3. 在对话框中,找到下载的项目文件。 转到目录“Second Workbook”,选择文件“HPCExcelMacros.bas”,然后单击“ 确定”。

    1539573917919

  4. 重复步骤 1-3 以导入“HPCControlMacros.bas”,然后单击“ 确定”。 这会将第二个宏文件添加到项目中。

  5. 验证这两个宏文件是否显示在 VBA 项目窗口中: 1539573929095

  6. 在 VBA 编辑器窗口中,单击“ 工具”,然后单击“ 引用 ”打开项目引用对话框。

  7. 在可用引用列表中,向下滚动,直到找到 Microsoft (R) HPC Pack 程序集,然后选择它旁边的检查框。 选中该框后,对话框将如下所示: 1539573937680

  8. 单击“ 确定” 关闭项目引用对话框。

在宏框架中定义计算

设置 VBA 环境后,我们可以转到下一步 -- 修改宏以运行计算。 HPCExcelMacros 模块包含与第一个工作簿中相同的宏,仅这次它们已包含用于更新状态栏的计数器和代码。

定义变量并从原始宏复制初始化代码

首先需要行和列的计数器。 在此工作簿中,我们将计算进行划分,以便单独计算表格中的每个单元格。 原始宏使用循环遍历表中的每个单元格,但在 HPC 宏函数中,我们使用的是异步框架,因此在发送每个单独计算时,我们需要跟踪行号和列号。 我们将使用宏中的行和列变量单步执行表中的每个单元格;它们反映了我们在每个步骤中计算的行和列。

我们将返回到原始宏,查看它在启动时执行的操作,然后将其移动到 HPC_Initialize 宏。

设置变量和修改HPC_Initialize

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器的项目树中,双击 HPCExcelMacros 模块以打开宏文件。

  3. 在文件顶部的“第 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
    
  4. 滚动到 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
    
    
  5. 在 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
    
  6. 从原始宏复制代码,然后返回到 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 类型可以是任何基元类型, (整数、长、双精度、字符串) 或这些类型的数组。 我们需要同时包含行和列,以便可以使用 Array。

我们需要在 HPC_Partition 宏中执行的另一项操作是计算完成时间。 在这种情况下,当我们到达表的末尾时,计算将完成。 可以通过在表中移动时递增行号和列号来执行此操作。 每当到达列的末尾时,我们移动到下一列。 传递最后一列后,表已完成,我们希望函数返回 Null。

修改HPC_Partition宏

  1. 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
    
  2. 将代码添加到表末尾检查。 完成表后,这将结束计算。

    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
    
  3. 如果尚未完成表,函数的最后一部分将存储行值和列值,并将它们用作函数的返回值;然后移动到下一行:

    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宏

  1. 通过在 VBA 项目窗口中双击“ScenarioAnalysis”模块,返回原始宏。

  2. 复制第一行中的代码:

    Dim Prem as double
    

    向下到最后一行:

    Next cols
    
  3. 返回 HPCExcelMacros 模块 (,方法是在 VBA 项目窗口中双击) 并将代码粘贴到HPC_Execute宏中。

  4. HPC_Execute 宏中,从以下开始删除我们已经使用的行:

    ' clear values, then figure out the table size
    

    下到前面的行:

    ' set up and run through the table
    
  5. 验证 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 宏是在计算节点上运行的代码,我们将为其提供要计算的行号和列号。

  6. 删除构成循环的行 (下面突出显示的行) :

    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
    
  7. 将 循环替换为传入函数的行号和列号。 修改后的代码应如下所示:

        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 宏返回的 Array)中的值,它包含要计算的单元格的行和列,而不是循环。 因此,为了回顾最后一步,我们删除了两个循环行,

    For cols = 1 To NumCols
        For rws = 1 To NumRows

以及循环末尾的“Next”行,

        Next rws
    Next cols

和 在循环正上方添加了行,以使用数据数组变量:

    rws = data(0)
    cols = data(1)

我们要对 HPC_Execute 函数进行的最后一个更改与返回数据有关。 请记住,数据在宏中移动,每个宏都会将结果传递到下一个宏。 此工作簿的原始 VBA 代码设计为在桌面上运行,因此在计算每个单元格时,结果将粘贴到电子表格中。 由于此工作簿将在群集上运行,因此我们需要收集计算结果并从函数返回,而不是将结果粘贴到电子表格中。

还有一个需要考虑的其他事项:当 HPC_Execute 函数在计算节点上运行时,它将计算表中特定单元格的结果。 然后,此结果将返回到桌面并发送到 HPC_Merge 宏。 但请记住,在异步框架中,结果可以按任何顺序发送回,不一定是发送结果的顺序。 因此,我们需要告诉 HPC_Merge 宏我们在表中计算了哪个单元格。

为此,可以在结果数据中包含行号和列号。 与 HPC_Partition 宏一样,我们可以从HPC_Excecute宏返回 Array。 这样做不仅允许函数返回结果,还可以返回计算的单元格。

事实上,我们只需重复使用传递给函数的同一 Array 来返回结果,因为该 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

我们将重写该行,以使用传递到 data 参数中的宏的值。 已完成 的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 替换为 Array 中的行和列。 我们还将函数调用替换为结果,结果存储在同一数组中。

这些是我们为了在群集上运行此工作簿而必须进行的所有修改。 尽管这看起来可能有很多步骤,尤其是首次使用 HPC Services for Excel 时,请回顾宏,查看已更改的总代码。 真的不是很多。 在大多数情况下,这涉及到从原始宏复制和粘贴到 HPC 宏中。 为了支持异步计算,我们对代码进行了大量更改,但即使这样也相对简单。 我们没有对此工作簿中的实际业务逻辑进行更改,您甚至无需了解业务逻辑函数即可转换工作簿。 更重要的是,如果用户将来对业务逻辑进行更改,则不需要更改群集计算。

运行工作簿

为了测试对代码所做的更改,我们将首先在桌面上运行它。 与在第一个工作簿中一样,我们将向电子表格添加新按钮,并使用该按钮运行计算。 然后,我们将指定头节点和文件共享,添加在群集上运行工作簿的按钮,然后在群集上运行工作簿。

添加在本地运行工作簿的按钮

添加在本地运行工作簿的按钮

(1) Excel 工作簿打开后,单击功能区上的“ 开发工具 ”选项卡。

(2) 在“ 开发工具 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。 1539574494629

(3) 单击按钮后,在电子表格的某个位置绘制一个矩形,将按钮插入该位置。 定位按钮后,将显示“ 分配宏 ”对话框。 ! 1539574504903

(4) 在对话框中,从列表中选择宏 CalculateWorkbookOnDesktop ,然后单击“ 确定”。 请务必选择桌面宏 -- 我们希望先测试工作簿并查找任何错误,然后再在群集上运行它。

(5) 右键单击新按钮,然后选择 “编辑文本 ”以更改标签。

(6) 将标签命名为“桌面”或类似内容。

(7) 保存工作簿。

(8) 单击按钮在桌面上运行工作簿。

如果出现任何错误,你将看到一个错误对话框,并且将突出显示包含错误的 VBA 代码部分。 针对上面列出的代码进行双重检查以解决任何错误。

如果没有错误,你将看到正在填充的表。 这些值应与单击电子表格上的原始按钮时相同。 同样,这是在桌面上运行的,因此它的速度应该与原始按钮大致相同 -- 在大多数工作站上大约 1-2 分钟。

由于我们运行的是宏框架代码,因此您还将在 Excel 状态栏中看到计算进度。 与第一个工作簿一样,在桌面上运行,你将看到数字一起移动,因为在桌面上,工作簿同步运行。

在群集上运行工作簿

接下来,我们将尝试在群集上运行工作簿。 为此,我们首先需要设置几个值,告诉 Excel 如何联系群集。 这些值在 HPCControlMacros 模块中定义。** 在此文件的顶部,需要填写两个值:群集计划程序和共享目录。 请记住, (桌面用户) 必须对此共享目录具有写入访问权限;和群集计算节点必须具有对目录的读取访问权限。 在大多数情况下,群集计算将在用户帐户下运行,但连接到群集会话时,可以使用其他用户帐户,) 片刻后 (更多内容。

指定头节点和共享目录

  1. 在功能区的“ 开发工具 ”选项卡上,单击“ Visual Basic ”打开 VBA 编辑器。

  2. 在 VBA 编辑器的项目树中,双击 HPCControlMacros 模块以打开宏文件。

  3. 对于群集计划程序,请使用群集头节点的名称 -- 这是将在网络上使用的计算机名称。 可以使用完全限定的名称 (例如,headnode.mynetwork.com) ,但如果在同一域中,则不需要这样做。

  4. 对于共享目录,请使用前面创建的共享目录的完整路径, (“开始之前”部分中) 。

现在返回到 Excel 电子表格。 我们将添加另一个按钮,这次用于在群集上运行计算。

添加在群集上运行工作簿的按钮

  1. 在“ 开发工具 ”选项卡上,单击“ 插入 ”,然后选择按钮控件 -- 列表中的第一个控件。

  2. 单击该按钮后,在电子表格的某个位置绘制一个矩形,将按钮插入该位置。 定位按钮后,将显示“ 分配宏 ”对话框。

  3. 在对话框中,从列表中选择宏 CalculateWorkbookOnCluster ,然后单击“ 确定”。

  4. 右键单击新按钮,然后选择“ 编辑文本 ”以更改标签。

  5. 将标签命名为“群集”或类似名称。

  6. 保存该工作簿。

  7. 单击按钮,在群集上运行工作簿。

  8. 如果这是你第一次使用 Excel 或任何其他应用程序运行任何群集作业,你将看到用户身份验证对话框。 键入用户名和密码,如果需要) 检查 保存凭据的框,请 (。 如果要以其他用户身份在群集上运行计算,可以输入不同的用户帐户设置。

注意:确保在 HPC 群集管理器中将用户帐户(标准用户帐户或要使用的任何帐户)指定为群集用户。

如果有任何错误,你将看到一个弹出对话框。 如果桌面计算正常工作,则此时最可能的错误将与为群集头节点和共享目录创建的设置有关。 请仔细检查这些设置,然后重试。

如果没有错误,你将看到群集计算开始。 首先,当客户端库创建 HPC 会话用于计算时,将进行短暂的暂停。 接下来,客户端库将调用 HPC_Initialize 宏,你将看到表被擦除。 如果查看 Excel 状态栏,将首先看到所有记录都发送到群集;第二个数字将跃升至 105 个, (表中有 105 个单元格) 。 之后,你将看到另一个短暂的暂停,然后返回任何结果。 当每个计算节点启动 Excel 并加载工作簿时,会发生此暂停。

在此暂停后,你将看到结果开始填充表,状态栏将开始显示返回的结果数。 由于这是一个较长的计算,因此你很可能看到返回的结果无序;表中将有空格,然后填充这些空格。 同样,这是因为计算是异步的,并且某些计算节点的计算速度比其他计算节点快。 最终,表格将完全填充,状态栏将显示计算时间。

即使 HPC 群集中只有两个计算节点,计算速度应该比在桌面上运行快得多。 如果有 4 个节点、8 个节点或更多节点,则计算速度应非常快。 始终可以再次单击“桌面”按钮,将桌面计算的性能与群集计算进行比较。

Cluster-Enabled Excel 工作簿的最佳做法

以下说明介绍了在设计 Excel 工作簿以用于 HPC Services for Excel 时要记住的一些重要注意事项。 此说明适用于使用从 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 宏中关闭数据库连接。

同样,如果要写入 Merge 宏中的日志文件,请考虑在 Initialize 宏中打开该文件。 在 Merge 宏中,可以一次写入一个结果,然后可以在 Finalize 宏中关闭日志文件。

避免在 Merge 宏中重新计算电子表格

如果要在 Merge 宏中将结果插入电子表格,可能会无意中重新计算电子表格 -- 如果电子表格非常复杂,这可能会减慢处理速度。

默认情况下,每当更改单元格的值时(包括从 VBA 宏更新单元格时),Excel 都会重新计算电子表格。 Excel 将仅重新计算电子表格中受更改影响的那些部分,因此,如果单元格单独放置,则这并不重要。 但是,如果单元格用作较大计算的一部分或图表数据,则重新计算可能很慢。

如果要更新 Merge 宏中的任何电子表格单元格,请考虑禁用自动重新计算。 可以在 Initialize 宏中禁用自动重新计算。 然后在 Finalize 宏中,可以重新计算整个电子表格,并重新启用自动计算。

在 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 在更新屏幕和用户界面时会消耗一些时间。

可以在“初始化”宏中禁用屏幕更新,并在“完成”宏中重新启用屏幕更新。 在初始化宏中,使用

Application.ScreenUpdating = False

在 Finalize 宏中,使用

Application.ScreenUpdating = True

这将在计算过程中禁用屏幕更新,并在计算完成后重新启用更新。 在 Finalize 宏中重新启用屏幕更新时,Excel 将自动刷新屏幕。

使用数据结构暂时保存内存中的信息

如果收集用于处理的信息(在分区宏中执行的操作)非常耗时,或者需要在 Merge 宏中对结果执行非常复杂的后处理,请考虑在计算期间使用数据结构将信息存储在内存中。

例如,如果 Partition 宏中的每个计算请求都需要非常复杂的数据库查询,则可能会减慢处理速度并导致性能不佳。 在这种情况下,最好在计算开始前在 Initialize 宏中执行此复杂处理。

如果在 Initialize 宏中执行此复杂处理,则可以将每个计算请求存储在 VBA 的数据结构中,例如 Variants 数组。 然后,在 Partition 宏中,只需返回 Array 中的下一个条目。 这将有助于确保分区宏尽可能快。

同样,如果必须在结果到达时对结果执行非常复杂的后处理,建议将结果存储在合并宏的数据结构中,然后在计算完成后在 Finalize 宏中处理结果。

例如,如果要将结果存储在数据库中,但每次数据库写入都需要一个复杂的 insert 语句,最好将此处理移动到 Finalize 宏。 可以创建数据结构(例如 Variants 数组),并在 Merge 宏中,只需将每个结果插入 Array。 然后在 Finalize 宏中,可以循环访问 Array 并一次性执行必要的数据库写入操作。

最终备注

将现有工作簿转换为使用 HPC Services for Excel 更不是一种艺术,不如说是一种科学。 具体的详细信息(要移动的代码和移动位置)将始终取决于特定的工作簿。

但是,你现在应该了解所涉及的概念,并了解我们为何使用异步宏框架。 若要转换任何工作簿以使用 HPC Services for Excel,需要以基本相同的方式创建并行计算:将宏框架函数添加到工作簿,然后填写相应的代码。

可以在自己的工作簿中使用本文随附的“主干”宏文件。 按照上述示例中使用的步骤来标识工作簿中的迭代代码,并将其移动到相应的宏。 在群集上运行计算之前,请记得在桌面上测试计算以识别任何错误。

还应了解使用 HPC Services for Excel 在 HPC 群集上运行 Excel 工作簿可以获得的性能优势。 在本文中,我们从一个已经相当快的工作簿开始计算整个工作簿,只需大约 1-2 分钟即可在桌面上完成。 但是,即使在 HPC 群集上,该工作簿的运行速度也可能是其速度的两倍、四倍或更快。 实际性能取决于群集中可用计算节点的数量,始终可以通过向群集添加更多计算节点来提高性能。

通过相对较少的工作量,可以将许多长时间运行的 Excel 工作簿转换为在具有 HPC Services for Excel 的 Microsoft HPC 群集上运行。 对于非常长时间运行的工作簿(需要数小时甚至数天的工作簿来计算),使用 HPC Services for Excel 可以大幅提高性能。