Java 计算RMI版本与并发版本范围内的素数

Java 计算RMI版本与并发版本范围内的素数,java,concurrency,parallel-processing,rmi,distributed-computing,Java,Concurrency,Parallel Processing,Rmi,Distributed Computing,我有两个版本的程序,目的相同:计算0到n之间有多少素数 第一个版本使用并发,一个可调用的类“计算”,结果通过Future数组检索。在我的计算机中,创建的线程与处理器的数量一样多(4) 第二个版本是通过RMI实现的。所有四台服务器都在本地主机中注册。显然,服务器也在并行工作 我预计第二个版本会比第一个版本慢,因为我猜网络会涉及延迟,而另一个版本只会同时运行程序 然而,RMI版本的速度大约是并行版本的两倍。。。为什么会这样 我没有粘贴任何代码,因为这将是巨大的,但要求它的情况下,你需要它,我会看看我

我有两个版本的程序,目的相同:计算0到n之间有多少素数

第一个版本使用并发,一个可调用的类“计算”,结果通过
Future
数组检索。在我的计算机中,创建的线程与处理器的数量一样多(4)

第二个版本是通过RMI实现的。所有四台服务器都在本地主机中注册。显然,服务器也在并行工作

我预计第二个版本会比第一个版本慢,因为我猜网络会涉及延迟,而另一个版本只会同时运行程序

然而,RMI版本的速度大约是并行版本的两倍。。。为什么会这样

我没有粘贴任何代码,因为这将是巨大的,但要求它的情况下,你需要它,我会看看我能做什么

编辑:添加代码。我对那些需要发布不必要代码的部分进行了评论

并行版本

public class taskPrimes implements Callable
{
  private final long x;
  private final long y;
  private Long total = new Long(0);

  public taskPrimes(long x, long y)
  {
    this.x = x;
    this.y = y;
  }

  public static boolean isPrime(long n)
  {
    if (n<=1) return false ;
    for (long i=2; i<=Math.sqrt(n); i++)
      if (n%i == 0) return false;
    return true;
  } 

  public Long call()
  {   
    for (long i=linf; i<=lsup;i++)
      if (isPrime(i)) total++;
  return total;
  }
}

public class paralelPrimes 
{
  public static void main(String[] args) throws Exception 
  {
    // here some variables...
    int nTasks = Runtime.getRuntime().availableProcessors();

    ArrayList<Future<Long>> partial = new ArrayList<Future<Long>>(); 
    ThreadPoolExecutor ept = new ThreadPoolExecutor();
    for(int i=0; i<nTasks; i++)
    {
      partial.add(ept.submit(new taskPrimes(x, y))); // x and y are the limits of the range
      // sliding window here
    }  
    for(Future<Long> iterator:partial)
      try { total +=  iterator.get(); } catch (Exception e) {}
  }   
}
公共类taskPrime实现可调用
{
私人最终长x;
私人终审长y;
私人长时间总计=新长时间(0);
公共任务素数(长x,长y)
{
这个.x=x;
这个。y=y;
}
公共静态布尔iPrime(长n)
{

如果(n这取决于您的算法是如何为并行和并行解决方案设计的。没有一个标准规定并行必须优于并行或反之亦然。举例来说,如果您的并行解决方案有许多同步块,它可能会降低您的性能,在另一种情况下,并行算法中的通信可能是最小的嗯,那么网络上就没有开销了


如果你能得到Peter Pacheco的一本书,它可以清楚地理解一些想法:

< P>一个有趣的事情是默认情况下,JVM在客户端模式下运行。这意味着线程不会以最具侵略性的方式跨越核。尝试用-server选项运行程序会影响结果,尽管如Algor所提到的那样。ithm设计至关重要并发版本可能会有瓶颈。考虑到这个问题,在您的算法中出现瓶颈的可能性很小,但确实需要加以考虑

rmi版本确实是并行运行的,因为每个对象在不同的机器上运行,因为这往往是一个处理问题而不是通信问题,所以延迟起着不重要的作用

[更新]

现在,我看到了您的代码,让我们了解更多细节

您依赖ThreadExecutorPool和Future为您执行线程控制和同步,这意味着(根据文档)您正在运行的对象将被分配到现有线程上,并且一旦您的对象完成了计算,线程将返回到该池,另一方面,未来将定期检查计算是否已完成,以便它可以收集值

此场景最适合于定期执行的某些计算,线程池可以通过预先分配线程来提高性能(只有在线程第一次不存在时才有线程创建的开销)

您的实现是正确的,但它更关注程序员的便利性(这没有错,我一直在捍卫这一观点),而不是系统性能

RMI版本的性能有所不同,主要原因有两个:

1-您说过您在同一台机器上运行,大多数操作系统会将localhost、127.0.0.1甚至真实的self-ip地址识别为其self-address,并对通信进行一些优化,因此这里的网络开销很小

2-RMI系统将为您创建的每个服务器对象创建一个单独的线程(正如我前面提到的),这些服务器一被调用就会开始计算

你应该尝试做的事情:

1-尝试在网络上真正运行您的RMI版本,如果您可以将其配置为10Mbps,则最好查看通信开销(尽管,由于这是一次性通信,可能不会产生太大影响,您可能会让客户端应用程序多次调用计算,然后您会看到延迟)

2-尝试将并行实现更改为直接使用线程而没有未来(可以使用Thread.join来监视执行端),然后在机器上使用-server选项(尽管有时JVM会执行检查,以查看机器配置是否可以真正称为服务器,并拒绝移动到该配置文件)。主要的问题是,如果您的线程不能使用所有的计算机内核,您将看不到任何性能改进。还可以尝试多次执行计算,以克服创建线程的开销

希望这有助于澄清情况:)


干杯

鉴于您提供的详细信息,这主要取决于您使用的范围有多大,以及您将工作分配到服务器的效率有多高

例如,我敢打赌,对于小范围的N,通过RMI分发可能不会有任何加速开销可能会超过在多台服务器上分发的好处。当N变大时,使用有效的分发算法,相对于实际计算时间,此开销将变得越来越微不足道


例如,假设服务器是同质的,一个相对有效的分发可以是告诉每台服务器计算所有数字n的素数,这样
n%p=i
,其中n如果您的代码太大,但有些人可能需要它,请粘贴您的代码并在此处给我们链接。这样,或另一个网站来粘贴您的代码。查找算法是相同的
public class serverPrimes 
    extends UnicastRemoteObject 
        implements interfacePrimes
{
    public serverPrimes() throws RemoteException {}

    @Override
    public int primes(int x, int y) throws RemoteException
    {
        int total = 0;
        for(int i=x; i<=y; i++)
            if(isPrime(i)) total++;
        return total;
    }

    @Override
    public boolean isPrime(int n) throws RemoteException
    {
        if (n<=1) return false;
        for (int i=2; i<=Math.sqrt(n); i++)
            if (n%i == 0) return false ;
        return true;
    }

    public static void main(String[] args) throws Exception 
    {
        interfacePrimes RemoteObject1 = new serverPrimes();
        interfacePrimes RemoteObject2 = new serverPrimes();
        interfacePrimes RemoteObject3 = new serverPrimes();
        interfacePrimes RemoteObject4 = new serverPrimes();

        Naming.bind("Server1", RemoteObject1);
        Naming.bind("Server2", RemoteObject2);
        Naming.bind("Server3", RemoteObject3);
        Naming.bind("Server4", RemoteObject4);
    }
}
public class clientPrimes implements Runnable
{
    private int x;
    private int y;
    private interfacePrimes RemoteObjectReference;
    private static AtomicInteger total = new AtomicInteger();

    public clientPrimes(int x, int y, interfacePrimes RemoteObjectReference)
    {
        this.x = x;
        this.y = y;
        this.RemoteObjectReference = RemoteObjectReference;
    }

    @Override
    public void run()
    {
        try
        {
            total.addAndGet(RemoteObjectReference.primes(x, y));
        }
        catch (RemoteException e) {}
    }

    public static void main(String[] args) throws Exception
    {
        // some variables here...
        int nServers = 4;
        ExecutorService e = Executors.newFixedThreadPool(nServers);

        double t = System.nanoTime();
        for (int i=1; i<=nServers; i++)
        {
            e.submit(new clientPrimes(xVentana, yVentana, (interfacePrimes)Naming.lookup("//localhost/Server"+i)));
            // sliding window here
        }
        e.shutdown();
        while(!e.isTerminated());
        t = System.nanoTime()-t;
    }
}