65.9K
CodeProject 正在变化。 阅读更多。
Home

使用并行异步 Web 服务结合 .NET 2.0 进行蒙特卡洛模拟

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (31投票s)

2006年9月10日

14分钟阅读

viewsIcon

99180

downloadIcon

535

一个使用 C# 编写的 .NET 2.0 的并行 Web 服务示例

引言

如今,金融工程师和科学界面临的许多计算问题都需要海量的计算能力。有多种方法可以提供这种计算性能。对称多处理系统或消息传递接口一直是满足高性能计算需求的标准。然而,使用基于标准协议并在商品 x86 服务器上运行的并行 Web 服务,可能是提供所需计算能力的一种更简单、成本更低的方式。

本文通过解释如何使用 .NET 2.0 设置并行 Web 服务来处理蒙特卡洛模拟,该模拟可以将 Pi 的值近似到小数点后几位,从而展示了异步 Web 服务的强大功能。蒙特卡洛模拟使用随机生成的数字来评估那些不易用更传统数学方法解决的复杂问题。蒙特卡洛方法用于建模科学研究中广泛的具有挑战性的金融问题和物理系统。

使用蒙特卡洛模拟估算 Pi (π)

圆的面积是 Pi*r^2,其中 r 代表圆的半径。要估算 Pi 的值,请考虑第一象限内的圆的四分之一,其面积为 (Pi*r^2)/4。参见下图。四分之一圆所处的正方形面积为 r^2。将半径设为 1,则四分之一圆的面积为 Pi/4,正方形的面积为 1。四分之一圆面积与正方形面积之比为 (Pi/4)/1 = Pi/4。

因此,Pi 等于四分之一圆面积与正方形面积之比的 4 倍。蒙特卡洛模拟可用于估算此比率,从而近似 Pi 的值。此比率可以通过在正方形内随机选择点 (x,y) 来估算,并跟踪落在四分之一圆内的点数(即 Sqrt(x^2+y^2)<=1)与尝试的总点数之比。一旦建立起对该比率的良好估计,就可以通过将比率乘以 4 来估算 Pi。Pi 估算的质量取决于使用的点数。使用的点越多,Pi 的估计值就越好。根据所需的精度,可能需要尝试数百万甚至数十亿个点。将数十亿个点计算分布在多个运行蒙特卡洛 Web 服务的服务器上,可以并行化该过程并快速生成准确的结果。

理解 Web 服务

Web 服务本质上是一个可以通过网络供其他机器使用的函数或方法。因为 Web 服务使用 Web 服务描述语言 (WSDL)、SOAP、XML 和 HTTP 等标准化接口,并且与 Visual Studio 2005 等开发工具集成,所以利用 Web 服务所需的大部分工作已经完成。从应用程序调用远程 Web 服务比调用本地函数只困难一点。

通过网络调用 Web 服务或函数可能会涉及大量的延迟。良好的并行 Web 服务架构将最小化网络调用频率与每次调用所执行的处理量之间的比例。这通常通过将主要的计算循环设计到 Web 服务中,并设计客户端应用程序在调用 Web 服务时指定所需迭代次数来实现。这种设计将使网络流量相对于服务器集群执行的处理量保持较低水平。

在 Microsoft 环境中,Web 服务有效地运行在 IIS 之上。这意味着对 Web 服务的多个调用在服务器端会自动实现多线程。Web 服务的另一个优点是,只需将 Web 服务部署在许多不同的服务器上,即可轻松地对其进行集群化。因此,使用 Web 服务的应用程序可以通过将计算需求分散到许多 CPU 上的多个线程来实现并行处理。这对于“易于并行化”的工作负载尤其有利,这些工作负载运行许多并行计算,并且这些计算之间几乎没有依赖关系。大多数蒙特卡洛模拟都属于易于并行化的范畴。

服务器负载均衡器使并行 Web 服务更加容易。服务器负载均衡器 (SLB) 将多个客户端请求分发到许多不同的服务器。通过在服务器集群前使用服务器负载均衡器,应用程序可以指向负载均衡器并将其视为单个服务器。如果需要额外的服务器来满足客户端应用程序的计算需求,可以将它们添加到 SLB 后面,而无需修改 Web 服务或客户端应用程序。SLB 已经变得非常先进。然而,要并行化 Web 服务,只需要最基本的功能。“轮询”分发且无会话亲和性对于大多数蒙特卡洛模拟来说效果很好。更高级的 SLB 可以监视集群中每台服务器的网络或 CPU 利用率,并将下一个请求传递给利用率最低的服务器。根据正在解决问题的类型,这些功能可能非常有价值,但通常不是必需的。

配置 SLB 以用于 Web 服务集群时,请确保关闭会话亲和性和会话持久性。会话亲和性和持久性每次都将流量从客户端系统路由到同一服务器。如果 SLB 位于处理 Web 购物车应用程序的服务器前面,则需要这样做,但这与并行 Web 服务集群所需的情况完全相反。

客户端应用程序也可以在没有服务器负载均衡器的情况下利用并行 Web 服务架构。调用应用程序本身处理跨服务器池分发请求。但是,Windows Server 2003 附带网络负载均衡功能,该功能可以在没有物理 SLB 的情况下有效地平衡多达 32 台服务器。还有像“Balancer”这样的开源 SLB,可以将基于 Linux 的服务器变成计算集群的廉价 SLB。

构建蒙特卡洛 Web 服务

下面显示的 MonteLoop Web 服务生成随机点并检查每个点是否落在四分之一圆内。MonteLoop 需要 TriesSeed 参数。Tries 代表要生成的随机点数。Seed 是随机数生成器的种子。

在多系统或多线程中使用随机数生成器时,确保每个随机类的实例化使用不同的种子非常重要。默认情况下,.NET 随机类使用系统时钟生成种子。当调用分散到多个线程或多个系统时,使用系统时钟生成相同的随机序列是可能的。因此,种子应由调用应用程序指定。

MonteLoop 返回对象类 Monte,该类包含 InCircle(落在四分之一圆内的点数)和 Tries(测试的总点数)。如上所述,这两个输出创建了计算 Pi 所需的比率。严格来说,Tries 无需返回,因为调用应用程序已经知道请求的点数。然而,为了充分演示 Web 服务在蒙特卡洛模拟中的用途,展示如何返回类非常重要。

MonteLoop Web 服务代码

using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
  public Service () {
    //Uncomment the following line if using designed components

    //InitializeComponent();

  }

  public class Monte
  {
    public int InCircle = 0;
    public int Tries = 0;
  }

  [WebMethod]
  public Monte MonteLoop(int Tries, int Seed)
  {
    Monte Values = new Monte();
    Random Rnd = new Random(Seed);
    double x, y;
    Values.InCircle = 0;
    for (int i = 1; i < Tries; i++)
    {
      x = Rnd.NextDouble();
      y = Rnd.NextDouble();
      if (Math.Sqrt(x * x + y * y) <= 1) Values.InCircle++;
        //Optimization Note: Sqrt is not needed 1^2=1

    }
    Values.Tries = Tries;
    return (Values);
  }
}

在 Visual Studio 2005 中创建 Web 服务

要在 Visual Studio 2005 中创建 MonteLoop Web 服务,请选择“文件”>“新建网站”>“ASP.NET Web 服务”。确保选择的语言是 Visual C#。编辑“文件系统”名称,使其指向正确的目录并以有意义的名称结尾。然后单击“确定”。将上面的 Monte 类和 MonteLoop 复制到公共类 Service 中。如果工作站已配置 IIS 和 .NET 2.0,请按 Ctrl+F5 测试 Web 服务而不进行调试。Internet Explorer 将打开并显示服务描述。单击 MonteLoop 链接。在 Tries 中输入 4000,在 Seed 中输入 12345,然后单击“调用”。XML 结果将显示在新窗口中。InCircle 应为 3142,Tries 应为 4000。

接下来,通过单击“生成”然后“发布网站”来发布 Web 服务。然后单击“目标位置”下的“...”图标。要在本地主机上进行测试,请选择“本地 IIS”图标,选择“默认网站”,然后单击“添加目录”图标。输入新目录的名称,然后单击“打开”,再单击“确定”。通过打开 Internet Explorer 并输入 https:///{directory_name}/service.asmx 来测试以确保一切正常工作。

一种将 Web 服务推送到多个服务器的简单方法是将本地主机目录中的文件复制到相应服务器的 IIS 目录中,通常是“C:\Inetpub\wwwroot\{directory name}”,然后指定这些目录在 IIS 中包含应用程序。要指定目录包含应用程序,请打开“应用程序服务器”控制面板,单击“IIS 管理器”,找到包含 Web 服务的新目录,右键单击该目录,在“目录”选项卡中选择“属性”,单击“创建”按钮,然后单击“确定”。要在服务器上测试 Web 服务,只需打开 IE 并输入 http://{server IP address}/{directory name}/service.asmx。

异步调用 Web 服务

使用 Visual C# 2005 和 .NET 2.0,异步 Web 服务调用的方式与以前的版本略有不同。在 .NET 2.0 中,Web 服务代理使用以 AsyncCompleted 结尾的方法进行异步调用。为了使用这些异步调用,必须使用 CompletedEventHandler 注册 Completed 事件的句柄。异步 Web 服务调用以 Async 方法开始。一旦 Web 服务执行完成,Completed 事件就会触发,从而执行指定的事件处理程序。指定的事件处理程序在第二个参数中将 Web 服务结果作为事件参数接收。下一个代码示例演示了这一点的工作原理。

为了利用 Web 服务的并行性,客户端应用程序必须能够在收到任何结果之前异步调用 Web 服务多次。如果 Web 服务位于服务器负载均衡器之后,或者调用应用程序将一次向特定服务器发出两个以上的调用(在使用多核或超线程处理器时很常见),则应用程序必须指定允许从客户端到同一服务器地址的连接数超过 2 个。W3C 要求客户端机器与同一地址之间的连接数最多为 2 个。Visual Studio 和 .NET 默认强制执行此规则。要覆盖默认设置,请在 app.config 文件的配置部分中设置最大连接数。一种策略是将最大连接数设置为可用 CPU 或超线程 CPU 的总数。但是,一个通用的经验法则是将最大连接数设置为 Web 服务分发的 CPU 数量的 12 倍。将以下内容添加到 app.config 文件内的标签

<system.net>
  <connectionManagement>
     <add address="*" maxconnection="8"/>
  </connectionManagement>
</system.net>

设计客户端应用程序

客户端应用程序包含两个主要部分:1) 用于异步调用 Web 服务的类;2) main 函数。该类包含两个方法:StartDoneCallBack

Start 方法配置 Web 服务,设置事件处理程序,调用 Web 服务并增加计数器 ActiveWebServiceCounter 以跟踪尚未返回结果的已调用 Web 服务数量。当 Web 服务 Complete 事件触发时,将调用 DoneCallBack 方法。此方法将每个单独调用的 Web 服务的计算结果添加到四分之一圆内的总点数和尝试的总点数中。DoneCallBack 然后显示 Pi 的当前值和迄今为止尝试的总点数。将结果显示在 Web 服务调用返回时,可以通知用户程序仍在运行,并能感受到当前的精度水平。DoneCallBack 还会递减 ActiveWebServiceCounter。程序的 main 部分将检查此计数器以确定所有 Web 服务调用是否已全部完成,然后再报告最终结果。

程序的 main 函数为将要进行的每次 Web 服务调用实例化一个 Cluster 类对象。它生成一个随机数列表,用于为 Web 服务调用的随机数生成器设置种子,然后启动一个循环以触发 Web 服务调用。尽管调用是异步的,但循环将受 app.config 文件中指定的 maxconnections 数量的限制。因此,一旦请求数量超过 maxconnections,应用程序将等待一个 Web 服务完成,然后才能发出另一个 Web 服务调用。循环完成预定数量的 Web 服务调用后,程序将等待所有 Web 服务完成。每次 Web 服务调用完成时,ActiveWebServiceCounter 都会递减。当 ActiveWebServiceCounter 达到零时,所有 Web 服务都已完成,并且总数已更新。然后,程序输出 Pi 的最终值和 Tries 的总数。

出于测试目的,程序会跟踪开始时间、结束时间并计算蒙特卡洛模拟所需的总经过时间。请注意,PiConsoleApp 设计用于使用远程 Web 引用。将 PiConsoleApp 指向本地主机可能会产生意外结果。无论 maxconnection 设置如何,都允许与本地主机建立无限数量的连接。主循环可能很快就会使本地系统不堪重负。

PiConsoleApp 代码

using System;
using System.Collections.Generic;
using System.Text;

namespace PiConsoleApp
{
  class Program
  {
    const int TriesPerWSCall = 10000000;
    const int NumWSCalls = 100;
    //TriesPerWSCall * NumWSCalls = Total Number of Points


    private static int ActiveWebServiceCounter = 0;
    public static long ttl_incirc, ttl_tries;

    class WSAsyncCall
    {
      public void DoneCallBack(Object source,
        WSReference.MonteLoopCompletedEventArgs e)
        //Create Web Reference named "WSReference"

      {
        WSReference.Monte data = new WSReference.Monte();
        data = e.Result;
        ttl_incirc += data.InCircle;
        ttl_tries += data.Tries;
        Console.WriteLine(" Pi = {0:N9}  T = {1:N0}",
          (double)ttl_incirc / (double)ttl_tries * 4.0, ttl_tries);
        ActiveWebServiceCounter--;
      }

      public void Start(int seed)
      {
        WSReference.Service ser = new
          PiConsoleApp.WSReference.Service();
        ser.MonteLoopCompleted += new
          WSReference.MonteLoopCompletedEventHandler(DoneCallBack);
        ActiveWebServiceCounter++;
        ser.MonteLoopAsync(TriesPerWSCall, seed);
      }
    }


    static void Main(string[] args)
    {
      DateTime startTime = DateTime.Now;
      Console.WriteLine("Starting Time:  " + startTime);

      WSAsyncCall[] WS = new WSAsyncCall[NumWSCalls + 1];
      Random seed = new Random();
      for (int i = 1; i <= NumWSCalls; i++)
      {
        WS[i] = new WSAsyncCall();
        WS[i].Start(seed.Next());
      }
      while (ActiveWebServiceCounter > 0) ;

      DateTime stopTime = DateTime.Now;
      Console.WriteLine("Finish Time:  " + stopTime);
      TimeSpan duration = stopTime - startTime;
      Console.WriteLine("Elapsed Time:  " + duration);
      Console.WriteLine();
      Console.WriteLine(" Pi = {0:N9}  T = {1:N0}",
        (double)ttl_incirc / (double)ttl_tries * 4.0, ttl_tries);

      Console.Read();
    }
  }
}

在 Visual Studio 2005 中创建应用程序

要创建 PiConsoleApp 应用程序,请选择“文件”>“新建项目”。再次确保选择了 Visual C#。选择“控制台应用程序”图标并输入应用程序名称。输入的名称将用作命名空间。接下来,将上面的 WSAsyncCall 类、Main 函数和变量复制到 Program 类下的新应用程序中。通过单击“项目”下拉菜单并选择“添加 Web 引用”来创建指向运行 PiMonte Web 服务的服务器或服务器的 Web 引用。在 URL 文本框中,输入 Web 服务的完整 URL,即 http://{server_IP_address}/{directory_name}/service.asmx。请记住不要使用 localhost。如果 Web 服务集群位于服务器负载均衡器之后运行,请输入虚拟服务器名称或 IP 地址,后跟任何服务器目录和带有 .asmx 扩展名的服务名称。要使用未修改的源代码,请将 Web 引用的名称输入为 "WSReference",然后单击“添加引用”。Visual Studio 并不总是能正确处理引用的重命名或命名空间,因此请从一开始就保持一致。接下来,如上所述编辑 app.config 文件的 maxconnection 标签。此时,应用程序应该可以运行了。

没有负载均衡器的并行 Web 服务

在上面的示例代码中,创建了一个 Web 引用,指向一个负载均衡器服务器的 IP 地址,该服务器将请求分发给多个服务器。如果您没有负载均衡器,或者不想配置 Windows 2003 网络负载均衡,您仍然可以使用 Web 服务来并行化应用程序。通过为每个服务器创建 Web 引用,然后为每个引用创建一个类似于 WSAsyncCall 类的类,您可以在主循环中将 Web 服务调用分布到多个服务器上。只需通过可用服务器数量轮询 Web 服务器请求。

并行 Web 服务性能

使用两台运行 Microsoft Windows Server 2003 R2 的双核 2.8GHz Xeon 处理器服务器测试了此基于并行 Web 服务的应用程序的性能。网络是 100Mb 网络,客户端应用程序和服务器之间有多个跳跃。每项测试包含 160 亿个蒙特卡洛点。

一台运行单线程蒙特卡洛模拟的服务器需要 34 分 34 秒才能完成。使用 Server 2003 附带的网络负载均衡功能运行并行 Web 服务的两台服务器通常需要 4 分 30 秒。使用外部服务器负载均衡器的相同两台服务器完成 160 亿次蒙特卡洛尝试通常只需要 3 分 50 秒。此类型应用程序并行化的性能提升是惊人的。

运行 7 次 160 亿次尝试的蒙特卡洛模拟,Pi 的值在 3.1415728 到 3.141623 之间,平均值为 3.14159295。Pi 的实际值是 3.14159265。蒙特卡洛结果相当不错!

结论

通过使用 Web 服务将蒙特卡洛模拟分发到多个服务器和多个线程,可以大大提高性能。由于 Web 服务基本上就像传统函数一样编写,因此无需手工编写多线程应用程序、自定义编写消息传递接口或使用其他高性能计算管理软件,即可轻松实现并行化。Web 服务提供了一种相对简单直接的方法,可以将并行问题分发到多个计算平台。

历史

  • 2006 年 9 月 10 日 - 发布原始版本
  • 2007 年 5 月 29 日 - 文章编辑并发布到 CodeProject.com 的主文章库
© . All rights reserved.