GridGain应用程序比一台机器上的多线程应用程序慢

时间:2010-12-19 04:51:29

标签: scala gridgain

我已经实现了我的第一个GridGain应用程序,并没有得到我预期的性能改进。可悲的是它慢了。我想帮助改进我的实施,以便更快。

我的应用的要点是我正在进行蛮力优化,其中包含数百万个可能的参数,每个功能评估需要几分之一秒。我已经通过将数百万次迭代划分为几个组来实现这一点,并且每个组都作为一个作业执行。

相关的代码如下。函数maxAppliedRange为x范围内的每个值调用函数foo,并返回最大值,结果将成为每个作业找到的所有最大值的最大值。

  scalar {
    result = grid !*~
      (for (x <- (1 to threads).map(i => ((i - 1) * iterations / threads, i * iterations / threads)))
        yield () => maxAppliedRange(x, foo), (s: Seq[(Double, Long)]) => s.max)
  }

我的代码可以在一台机器上进行多线程执行,也可以使用上面的代码使用多个GridGain节点。当我运行gridgain版本时,它会开始变得更快,但随后会发生一些事情:

  • 其中一个节点(在另一台机器上)错过了心跳,导致我的主计算机上的节点放弃该节点并再次开始执行该作业。
  • 错过心跳的节点继续执行相同的工作。现在我有两个节点做同样的事情。
  • 最终,所有作业都在我的主机上执行,但由于某些作业在稍后开始,所以一切都要花费更长的时间。
  • 有时GridGain会抛出异常,因为节点超时且整个任务失败。
  • 我生气了。

我尝试将其设置为有很多工作,所以如果一个失败则不会那么大,但是当我这样做时,我最终会在每个节点上执行许多工作。这给每台机器带来了更大的负担,使得节点更有可能错过心跳,导致一切都变得更快。如果每个CPU有一个作业,那么如果一个作业失败,则必须从头开始重新启动另一个节点。无论哪种方式,我都无法获胜。

我认为最有效的方法是我可以做两件事:

  • 增加心跳超时
  • 限制每个节点,使其一次只能完成一个作业。

如果我能做到这一点,我可以将我的任务分成许多工作。每个节点一次只能完成一项工作,并且没有机器会因负载过重而导致错过心跳。如果工作失败,那么很少会丢失工作,恢复会很快。

谁能告诉我怎么做?我该怎么办?

2 个答案:

答案 0 :(得分:2)

我明白了。

首先,有一个xml配置文件,用于控制网格节点的运行方式。默认配置文件位于GRIDGAIN_HOME / config / default-spring.xml中。我可以编辑它或复制它,并在我启动网格节点时将新文件传递给ggstart.sh。我需要添加的两件事是:

    <property name="networkTimeout" value="25000"/>

将网络消息的超时设置为25秒,

   <property name="executorService">
        <bean class="org.gridgain.grid.thread.GridThreadPoolExecutor">
            <constructor-arg type="int" value="1"/>
            <constructor-arg type="int" value="1"/>
            <constructor-arg type="long">
                <util:constant static-field="java.lang.Long.MAX_VALUE"/>
            </constructor-arg>
            <constructor-arg type="java.util.concurrent.BlockingQueue">
                <bean class="java.util.concurrent.LinkedBlockingQueue"/>
            </constructor-arg>
        </bean>
    </property>

前两个构造函数参数用于启动1个线程,最大线程大小为1.执行程序服务控制执行gridgain作业的线程池。默认值为100,这就是为什么我的应用程序不堪重负并且心跳超时的原因。

我必须对代码进行的另一项更改是:

  scalar.apply("/path/to/gridgain home/config/custom-spring.xml") {
    result = grid !*~
      (for (x <- (1 to threads).map(i => ((i - 1) * iterations / threads, i * iterations / threads)))
        yield () => maxAppliedRange(x, kalmanBruteForceObj.performKalmanIteration), (s: Seq[(Double, Long)]) => s.max)
  }

因为没有.apply语句,它会启动一个带有所有默认选项的网格节点,而不是带有上述编辑的配置文件,这就是我想要的。

现在它完全符合我的需要。我可以将任务分成小块,即使是最弱,最慢的计算机也可以为这项工作做出贡献。

答案 1 :(得分:2)

现在我的工作正常了。在我的应用程序的情况下,我在一台机器上的多线程应用程序的速度提高了50%,但这不是我能做的最好的。还有更多的工作要做。

要使用gridgain,似乎配置文件对于使一切正常运行至关重要。这是设置节点行为的地方,必须符合您的应用程序需求。

我的xml配置文件中需要的一件事是:

    <property name="discoverySpi">
        <bean class="org.gridgain.grid.spi.discovery.multicast.GridMulticastDiscoverySpi">
            <property name="maxMissedHeartbeats" value="20"/>
            <property name="leaveAttempts" value="10"/>
        </bean>
    </property>

设置在认为节点丢失之前可能错过的最大心跳。我把它设置为一个很高的值,因为我一直有节点离开和几秒钟后返回的问题。或者,我可以使用配置文件中的其他属性修复运行节点的机器的IP,而不是使用多播。我没有这样做,但如果你一遍又一遍地使用相同的机器,它可能会更可靠。

我做的另一件事是:

    <property name="collisionSpi">
        <bean class="org.gridgain.grid.spi.collision.jobstealing.GridJobStealingCollisionSpi">
            <property name="activeJobsThreshold" value="2"/>
            <property name="waitJobsThreshold" value="4"/>
            <property name="maximumStealingAttempts" value="10"/>
            <property name="stealingEnabled" value="true"/>
            <property name="messageExpireTime" value="1000"/>
        </bean>
    </property>

    <property name="failoverSpi">
        <bean class="org.gridgain.grid.spi.failover.jobstealing.GridJobStealingFailoverSpi">
            <property name="maximumFailoverAttempts" value="10"/>
        </bean>
    </property>

对于第一个,activeJobsThreshold值告诉节点它可以同时运行多少个作业。这是一种更好的限制方式,而不是更改执行程序服务中的线程数。此外,它还可以实现一些负载平衡,并且空闲节点可以从其他节点“窃取”工作,从而更快地完成所有工作。

还有更好的方法可以做到这一点。 Gridgain可以根据每个节点的测量性能确定作业的大小,显然,这可以提高整体性能,特别是如果网格中有快速和慢速的计算机。

对于未来,我将研究配置文件并将其与javadocs进行比较,以了解所有不同选项,以便更快地运行。