3

我已经实现了我的第一个 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 有一个作业,那么如果一个作业失败,则必须从头开始另一个节点。反正我赢不了。

我认为最好的方法是如果我能做两件事:

  • 增加心跳的超时时间
  • 限制每个节点,使其一次只执行一项工作。

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

谁能告诉我该怎么做?我应该在这里做什么?

4

2 回答 2

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 进行比较,以了解所有不同的选项,以使其运行得更快。

于 2010-12-19T19:01:18.727 回答
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。执行器服务控制执行网格增益作业的线程池。默认值为 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 语句,它会启动一个具有所有默认选项的网格节点,而不是具有上述编辑的配置文件,这正是我想要的。

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

于 2010-12-19T07:51:33.173 回答