Javaの計算グリッドを手軽に構築できるようにしました!!

最初にお断り

パフォーマンス計測で時間切れになり、記事を最後まで完成できませんでした。来週には完成版を公開する予定です。

本題

計算グリッドには昔から興味がありました。クックパッドには奥さんが非常にお世話になっています。そしてひょんなことからGridGainというJavaの計算グリッドフレームワークを見つけました。

↓GridGain+Scalaで簡単グリッドコンピューティング 浜本階生さん tllt#1↓

動画は1台のPCの中でGridGianを走らせて、Scala(関数言語onJava?)で分散処理を実行しています。

試しにどんなものか環境を作ってみました

手持ちのDebian Lennyを母艦にしてカスタムKNOPPIXがネットワークブート(PXEBoot)し、それぞれがGridGainの計算ノードになるようにしました。ミソはKNOPPIXなのでどんなPCでも一時拝借して計算ノードをどんどん追加できる点です。

構成
用途 CPU メモリ 台数
母艦 Pentium3 800MHz 256MB 1
計算ノード Pentium3 800MHz 256MB 4
計算ノード Celeron 2GHzくらい 512MBくらい 4

これらをハブ2台カスケード接続したネットワークで接続します。全部100Baseでリンクアップです。

計算ノード起動の様子

準備中20010.01.12追記

パフォーマンス計測(Grid vs nonGrid)
桁数 処理時間(Grid) 処理時間(nonGrid)
14 15,889ms 5,746ms 56,699,993,299,403=
5,674,033×9,992,891
15 12,761ms 32,584ms 432,671,265,179,137=
9,992,891×43,297,907
16 17,935ms 139,895ms 3,151,549,059,914,119=
9,992,891×315,379,109
17 16,518ms 384,152ms 51,874,848,423,388,477=
100,105,519×518,201,683
18 24,895ms 1,010,127ms 148,464,226,937,944,979=
100,105,519×1,483,077,341
19 213,594ms 3,650,277ms 9,223,372,021,822,390,277=
2,147,483,647×4,294,967,291

走らせた処理は、最初に「素数×素数」の数を与えてそれを素因数分解するというもの。Gridは母艦も含め9Node・9CPUで母艦から処理を実行、nonGridはGridGainを使わずに母艦で通常のJavaプログラムとして実行しています。

nonGridは非常にライナーな処理なので、桁数が多くなるほど処理を分割・並列化しているGridのほうがパフォーマンスが良いです。※数字は書き写しなので不正確かもしれません。

パフォーマンス計測(計算ノード数)
計算ノード数 処理時間(1回目) 処理時間(2回目) 備考
1 29,545ms 30,606ms 母艦のみ
2 28,905ms 27,214ms Pentium3 800MHz
3 29,516ms 28,760ms Pentium3 800MHz
4 28,066ms 29,744ms Pentium3 800MHz
5 29,252ms 26,974ms Pentium3 800MHz
6 30,238ms 27,177ms Celeron 2GHzくらい
7 25,662ms 18,717ms Celeron 2GHzくらい
8 17,391ms 28,192ms Celeron 2GHzくらい
9 26,793ms 17,696ms Celeron 2GHzくらい

走らせた処理は同じで、最初に16桁の数(3,151,549,059,914,119=9,992,891×315,379,109)を与えてそれを素因数分解するというもの。

処理を分割・並列化しているにもかかわらず、台数と処理時間に相関関係がありません。これはおそらくですが解を含んだ処理が高性能PCに振り分けられたときだけ速いという結果が出ている気がします。分割の粒度をもっと細かくすれば平滑化されるかもしれません。今後の課題です。

ちなみにGridGainに付属するサンプルの「org.gridgain.examples.primenumbers.gridify.GridifyPrimeExample」では台数と処理時間に相関関係が認められたので、ただ単純に計測プログラムの問題かもしれません。

パフォーマンス結果の推察

同じ16桁の処理でも計算ノード1台のGridとnonGridでは、前者295,45ms、後者139,895msと大きな差があります。考え付く限りの理由は以下の通りですが正直謎です。わかる方ご指摘ください。

  • GridGainのClassLoaderはキャッシュが効いてる?
  • Gridでは走査範囲を分割しているのでその中でたまたま早期に解が見つかった?
  • nonGridでは走査範囲が分割されないのでGCが頻発している?
  • MapReduceは単一スレッドでも性能が向上する?
GridGain実行時のログ
[17:46:46,770][INFO ][main][GridKernal%null] 
  _____      _     _  _____       _        
 / ____|    (_)   | |/ ____|     (_)       
__ _ __ _ __ __ __ _ _ _ __
_ '__ / _` _ / _` '_ \
__ (_ __ (_
\_____|_| |_|\__,_|\_____|\__,_|_|_| |_| >>> GridGain ver. 2.1.1-26022009 >>> Copyright (C) 2005-2009 GridGain Systems. [17:46:46,779][INFO ][main][GridKernal%null] GRIDGAIN_HOME=/usr/local/gridgain-2.1.1 [17:46:46,782][INFO ][main][GridKernal%null] VM arguments: [-javaagent:/usr/local/gridgain-2.1.1/libs/aspectjweaver-1.5.3.jar] [17:46:48,453][INFO ][main][GridKernal%null] License file can be found at: /usr/local/gridgain-2.1.1/license.txt [17:46:48,461][INFO ][main][GridKernal%null] 3-rd party licenses can be found at: /usr/local/gridgain-2.1.1/libs/licenses [17:46:49,367][WARN ][gridgain-#6%null%][GridUpdateNotifier$UpdateChecker] Failed to receive update information from (safely ignoring): www.gridgain.org [17:46:49,895][INFO ][main][GridKernal%null] User class loader version: 0 [17:46:49,896][INFO ][main][GridKernal%null] Local node ID: cfecd114-271b-45c6-86d5-8a1026f52040 [17:46:49,898][INFO ][main][GridKernal%null] P2P exclude path: [17:46:49,899][INFO ][main][GridKernal%null] Peer class loading enabled: true [17:46:49,899][INFO ][main][GridKernal%null] Peer class loading missed resources cache size: 100 [17:46:49,899][INFO ][main][GridKernal%null] Peer class loading timeout (ms): 5000 [17:46:49,900][INFO ][main][GridKernal%null] Metrics expiration time: 9223372036854775807 [17:46:49,900][INFO ][main][GridKernal%null] Metrics history size: 10000 [17:46:49,900][INFO ][main][GridKernal%null] Discovery startup delay (ms): 0 [17:46:51,133][INFO ][main][GridKernal%null] Object marshaller: GridJBossMarshaller [17:46:51,134][INFO ][main][GridKernal%null] Grid executor service [name=executorService, corePoolSize=100, maxPoolSize=100, keepAliveTime=0ms, queueCls=LinkedBlockingQueue, threadFactoryCls=GridThreadFactory, rejectionHandlerCls=AbortPolicy] [17:46:51,135][INFO ][main][GridKernal%null] Grid executor service [name=systemExecutorService, corePoolSize=5, maxPoolSize=5, keepAliveTime=0ms, queueCls=LinkedBlockingQueue, threadFactoryCls=GridThreadFactory, rejectionHandlerCls=AbortPolicy] [17:46:51,135][INFO ][main][GridKernal%null] Grid executor service [name=peerClassLoadingExecutorService, corePoolSize=20, maxPoolSize=20, keepAliveTime=0ms, queueCls=LinkedBlockingQueue, threadFactoryCls=GridThreadFactory, rejectionHandlerCls=AbortPolicy] [17:47:02,999][INFO ][main][GridLocalMetricsManager] Starting SPI implementation: org.gridgain.grid.spi.metrics.jdk.GridJdkLocalMetricsSpi [17:47:03,001][INFO ][main][GridJdkLocalMetricsSpi] Using parameter [isPreferSigar=true] [17:47:05,095][INFO ][main][GridJdkLocalMetricsSpi] Hyperic Sigar 'CpuPerc.getCombined()' method will be used to detect average CPU load. Note that Hyperic Sigar is licensed under GPL. For more information visit: http://support.hyperic.com/confluence/display/SIGAR/Home [17:47:06,067][INFO ][main][GridJdkLocalMetricsSpi] SPI started ok [startMs=3067, spiMBean=org.gridgain:group=SPIs,name=GridJdkLocalMetricsSpi] [17:47:08,797][INFO ][main][GridTcpCommunicationSpi] Successfully bound to TCP port: 47100 [17:47:08,798][INFO ][main][GridCommunicationManager] Starting SPI implementation: org.gridgain.grid.spi.communication.tcp.GridTcpCommunicationSpi [17:47:08,799][INFO ][main][GridTcpCommunicationSpi] Using parameter [localAddr=null] [17:47:08,802][INFO ][main][GridTcpCommunicationSpi] Using parameter [msgThreads=5] [17:47:08,802][INFO ][main][GridTcpCommunicationSpi] Using parameter [localPort=47100] [17:47:08,802][INFO ][main][GridTcpCommunicationSpi] Using parameter [localPortRange=100] [17:47:08,803][INFO ][main][GridTcpCommunicationSpi] Using parameter [idleConnTimeout=30000] [17:47:08,803][INFO ][main][GridTcpCommunicationSpi] Using parameter [directBuf=true] [17:47:08,911][INFO ][main][GridTcpCommunicationSpi] SPI started ok [startMs=112, spiMBean=org.gridgain:group=SPIs,name=GridTcpCommunicationSpi] [17:47:09,993][INFO ][main][GridCheckpointManager] Starting SPI implementation: org.gridgain.grid.spi.checkpoint.sharedfs.GridSharedFsCheckpointSpi [17:47:10,145][INFO ][main][GridSharedFsCheckpointSpi] Using parameter [folder=/usr/local/gridgain-2.1.1/work/checkpoint/sharedfs] [17:47:10,454][INFO ][main][GridSharedFsCheckpointSpi] SPI started ok [startMs=460, spiMBean=org.gridgain:group=SPIs,name=GridSharedFsCheckpointSpi] [17:47:11,163][INFO ][main][GridEventStorageManager] Starting SPI implementation: org.gridgain.grid.spi.eventstorage.memory.GridMemoryEventStorageSpi [17:47:11,164][INFO ][main][GridMemoryEventStorageSpi] Using parameter [expireAgeMs=9223372036854775807] [17:47:11,164][INFO ][main][GridMemoryEventStorageSpi] Using parameter [expireCnt=10000] [17:47:11,171][INFO ][main][GridMemoryEventStorageSpi] SPI started ok [startMs=7, spiMBean=org.gridgain:group=SPIs,name=GridMemoryEventStorageSpi] [17:47:12,059][INFO ][main][GridDeploymentManager] Starting SPI implementation: org.gridgain.grid.spi.deployment.local.GridLocalDeploymentSpi [17:47:12,066][INFO ][main][GridLocalDeploymentSpi] SPI started ok [startMs=6, spiMBean=org.gridgain:group=SPIs,name=GridLocalDeploymentSpi] [17:47:15,279][INFO ][main][GridDeploymentLocalStore] Deployment store started: GridDeploymentLocalStore [17:47:15,281][INFO ][main][GridDeploymentPerLoaderStore] Deployment store started: GridDeploymentPerLoaderStore [17:47:15,282][INFO ][main][GridDeploymentPerVersionStore] Deployment store started: GridDeploymentPerVersionStore [] [17:47:15,814][INFO ][main][GridLoadBalancingManager] Starting SPI implementation: org.gridgain.grid.spi.loadbalancing.roundrobin.GridRoundRobinLoadBalancingSpi [17:47:15,815][INFO ][main][GridRoundRobinLoadBalancingSpi] Using parameter [isPerTask=true] [17:47:15,821][INFO ][main][GridRoundRobinLoadBalancingSpi] SPI started ok [startMs=6, spiMBean=org.gridgain:group=SPIs,name=GridRoundRobinLoadBalancingSpi] [17:47:16,075][INFO ][main][GridFailoverManager] Starting SPI implementation: org.gridgain.grid.spi.failover.always.GridAlwaysFailoverSpi [17:47:16,076][INFO ][main][GridAlwaysFailoverSpi] Using parameter [maximumFailoverAttempts=5] [17:47:16,086][INFO ][main][GridAlwaysFailoverSpi] SPI started ok [startMs=10, spiMBean=org.gridgain:group=SPIs,name=GridAlwaysFailoverSpi] [17:47:16,256][INFO ][main][GridCollisionManager] Starting SPI implementation: org.gridgain.grid.spi.collision.fifoqueue.GridFifoQueueCollisionSpi [17:47:16,257][INFO ][main][GridFifoQueueCollisionSpi] Using parameter [parallelJobsNum=95] [17:47:16,269][INFO ][main][GridFifoQueueCollisionSpi] SPI started ok [startMs=12, spiMBean=org.gridgain:group=SPIs,name=GridFifoQueueCollisionSpi] [17:47:16,493][INFO ][main][GridTopologyManager] Starting SPI implementation: org.gridgain.grid.spi.topology.basic.GridBasicTopologySpi [17:47:16,500][INFO ][main][GridBasicTopologySpi] Using parameter [isLocalNode=true] [17:47:16,500][INFO ][main][GridBasicTopologySpi] Using parameter [isRmtNodes=true] [17:47:16,500][INFO ][main][GridBasicTopologySpi] SPI started ok [startMs=6, spiMBean=org.gridgain:group=SPIs,name=GridBasicTopologySpi] [17:47:20,789][INFO ][main][GridDiscoveryManager] Starting SPI implementation: org.gridgain.grid.spi.discovery.multicast.GridMulticastDiscoverySpi [17:47:20,802][INFO ][main][GridMulticastDiscoverySpi] Using parameter [mcastGroup=228.1.2.4] [17:47:20,817][INFO ][main][GridMulticastDiscoverySpi] Using parameter [mcastPort=47200] [17:47:20,844][INFO ][main][GridMulticastDiscoverySpi] Using parameter [tcpPort=47300] [17:47:20,844][INFO ][main][GridMulticastDiscoverySpi] Using parameter [localPortRange=10] [17:47:20,844][INFO ][main][GridMulticastDiscoverySpi] Using parameter [beatFreq=3000] [17:47:20,844][INFO ][main][GridMulticastDiscoverySpi] Using parameter [maxMissedBeats=3] [17:47:20,844][INFO ][main][GridMulticastDiscoverySpi] Using parameter [leaveAttempts=3] [17:47:20,845][INFO ][main][GridMulticastDiscoverySpi] Using parameter [localHost=xxx.xxx.xxx.2] [17:47:20,845][INFO ][main][GridMulticastDiscoverySpi] Using parameter [ttl=8] [17:47:20,890][INFO ][main][GridMulticastDiscoverySpi] Successfully bound to TCP port: 47300 [17:47:20,914][INFO ][main][GridMulticastDiscoverySpi] Successfully bound to Multicast port: 47200 [17:47:23,448][INFO ][main][GridMulticastDiscoverySpi] Local node: GridMulticastDiscoveryNode [id=cfecd114-271b-45c6-86d5-8a1026f52040, state=READY, lastHeartbeat=1262940441261, addr=HOSTNAME.local/xxx.xxx.xxx.2, tcpPort=47300, startTime=1262940440791] [17:47:23,449][INFO ][main][GridMulticastDiscoverySpi] Waiting for initial heartbeat timeout (3000 milliseconds) [17:47:26,450][INFO ][main][GridMulticastDiscoverySpi] SPI started ok [startMs=5660, spiMBean=org.gridgain:group=SPIs,name=GridMulticastDiscoverySpi] [17:47:27,053][INFO ][main][GridDiscoveryManager] >>> ------------------- >>> Discovery Snapshot. >>> ------------------- >>> Number of nodes: 2 >>> Topology hash: 0xF195C9FC >>> Local: CFECD114-271B-45C6-86D5-8A1026F52040, xxx.xxx.xxx.2, Linux i386 2.6.26-2-686, USERNAME, Java(TM) 2 Runtime Environment, Standard Edition 1.5.0_21-b01 >>> Remote: F3837CD5-95FF-41E2-B27A-ABDA24AC8478, xxx.xxx.xxx.100, Linux i386 2.6.24.4, root, Java(TM) 2 Runtime Environment, Standard Edition 1.5.0_21-b01 >>> Total number of CPUs: 2 [17:47:29,124][WARN ][main][GridUpdateNotifier] Update status is not available. [17:47:29,583][INFO ][main][GridKernal%null] >>> --------------------------------------------------- >>> GridGain ver. 2.1.1-26022009 STARTED OK in 44188ms. >>> --------------------------------------------------- >>> OS name: Linux 2.6.26-2-686 i386, 1 CPU(s) >>> OS user: USERNAME >>> VM information: Java(TM) 2 Runtime Environment, Standard Edition 1.5.0_21-b01 Sun Microsystems Inc. Java HotSpot(TM) Client VM 1.5.0_21-b01 >>> VM name: 18075@HOSTNAME >>> Optional grid name: null >>> Local node ID: CFECD114-271B-45C6-86D5-8A1026F52040 >>> Local node physical address: xxx.xxx.xxx.2, eth0 >>> GridGain documentation: http://www.gridgain.org/product.html [17:47:32,766][INFO ][main][GridDeploymentLocalStore] Task locally deployed: LocalDeploymentClass [undeployed=false, usage=0, super=GridDeploymentClass [cls=class grid.RSAChallengeTask, depMode=ISOLATED, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, clsLdrId=1595d814-f921-4302-8c06-df3c8239e50f, userVer=0, seqNum=1, alias=grid.RSAChallengeTask, local=true]] 0:jobRange is from 3 to 5613867 1:jobRange is from 5613868 to 11227732 2:jobRange is from 11227733 to 16841597 3:jobRange is from 16841598 to 22455462 4:jobRange is from 22455463 to 28069327 5:jobRange is from 28069328 to 33683192 6:jobRange is from 33683193 to 39297057 7:jobRange is from 39297058 to 44910922 8:jobRange is from 44910923 to 50524787 9:jobRange is from 50524788 to 56138652 10:jobRange is from 56138653 to 56138659 [17:47:58,095][INFO ][gridgain-#20%null%][GridJobWorker] Cancelling job: GridTaskSessionImpl [taskName=grid.RSAChallengeTask, userVer=0, taskClsName=grid.RSAChallengeTask, sesId=d2713008-d087-4d29-b699-c30a78ecf5d6, jobId=50c2e576-df93-4faf-9d7e-8bfce41faa62, endTime=9223372036854775807, taskNodeId=cfecd114-271b-45c6-86d5-8a1026f52040, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, closed=false, topSpi=null, cpSpi=null, failSpi=null, loadSpi=null, seqNum=1] [17:47:58,105][INFO ][gridgain-#21%null%][GridJobWorker] Cancelling job: GridTaskSessionImpl [taskName=grid.RSAChallengeTask, userVer=0, taskClsName=grid.RSAChallengeTask, sesId=d2713008-d087-4d29-b699-c30a78ecf5d6, jobId=d6f8d57a-ed64-4fa7-ae86-9ce840f65a81, endTime=9223372036854775807, taskNodeId=cfecd114-271b-45c6-86d5-8a1026f52040, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, closed=false, topSpi=null, cpSpi=null, failSpi=null, loadSpi=null, seqNum=1] [17:47:58,187][INFO ][gridgain-#22%null%][GridJobWorker] Cancelling job: GridTaskSessionImpl [taskName=grid.RSAChallengeTask, userVer=0, taskClsName=grid.RSAChallengeTask, sesId=d2713008-d087-4d29-b699-c30a78ecf5d6, jobId=ef8f46d3-09fd-4954-8a8e-96c0b90e8cc0, endTime=9223372036854775807, taskNodeId=cfecd114-271b-45c6-86d5-8a1026f52040, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, closed=false, topSpi=null, cpSpi=null, failSpi=null, loadSpi=null, seqNum=1] [17:47:58,191][INFO ][gridgain-#23%null%][GridJobWorker] Cancelling job: GridTaskSessionImpl [taskName=grid.RSAChallengeTask, userVer=0, taskClsName=grid.RSAChallengeTask, sesId=d2713008-d087-4d29-b699-c30a78ecf5d6, jobId=bb4074d1-f24c-4b3a-b484-c32f194a3637, endTime=9223372036854775807, taskNodeId=cfecd114-271b-45c6-86d5-8a1026f52040, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, closed=false, topSpi=null, cpSpi=null, failSpi=null, loadSpi=null, seqNum=1] [17:47:58,207][INFO ][gridgain-#24%null%][GridJobWorker] Cancelling job: GridTaskSessionImpl [taskName=grid.RSAChallengeTask, userVer=0, taskClsName=grid.RSAChallengeTask, sesId=d2713008-d087-4d29-b699-c30a78ecf5d6, jobId=ae21ee3d-2cfd-43c2-97e7-6d81d9214a47, endTime=9223372036854775807, taskNodeId=cfecd114-271b-45c6-86d5-8a1026f52040, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, closed=false, topSpi=null, cpSpi=null, failSpi=null, loadSpi=null, seqNum=1] [17:49:02,204][INFO ][main][GridMulticastDiscoverySpi] SPI stopped ok. [17:49:02,205][INFO ][main][GridBasicTopologySpi] SPI stopped ok. [17:49:02,206][INFO ][main][GridFifoQueueCollisionSpi] SPI stopped ok. [17:49:02,207][INFO ][main][GridAlwaysFailoverSpi] SPI stopped ok. [17:49:02,207][INFO ][main][GridRoundRobinLoadBalancingSpi] SPI stopped ok. [17:49:02,210][INFO ][main][GridDeploymentLocalStore] Removed undeployed class: LocalDeploymentClass [undeployed=true, usage=0, super=GridDeploymentClass [cls=class grid.RSAChallengeTask, depMode=ISOLATED, clsLdr=sun.misc.Launcher$AppClassLoader@198dfaf, clsLdrId=1595d814-f921-4302-8c06-df3c8239e50f, userVer=0, seqNum=1, alias=grid.RSAChallengeTask, local=true]] [17:49:02,211][INFO ][main][GridLocalDeploymentSpi] SPI stopped ok. [17:49:02,212][INFO ][main][GridMemoryEventStorageSpi] SPI stopped ok. [17:49:02,214][INFO ][main][GridSharedFsCheckpointSpi] SPI stopped ok. [17:49:02,282][INFO ][main][GridTcpCommunicationSpi] SPI stopped ok. [17:49:02,283][INFO ][main][GridJdkLocalMetricsSpi] SPI stopped ok. [17:49:02,286][INFO ][main][GridKernal%null] >>> --------------------------------------------------- >>> GridGain ver. 2.1.1-26022009 STOPPED OK in 65485ms. >>> --------------------------------------------------- >>> Optional instance name: null >>> Grid up time: 00:01:33:161 3151549059914119 = 9992891 * 315379109 + 0 it takes 27214ms
パフォーマンス計測で使ったソース

【Grid版】RSAChallenge.java

package grid;

import java.math.BigInteger;
import org.gridgain.grid.*;

public class RSAChallenge {
	public static void main(String[] args)throws GridException{
		//BigInteger _a = new BigInteger("5674033");
		//BigInteger _b = new BigInteger("9992891");

		//BigInteger _a = new BigInteger("44147").add(new BigInteger("00660").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("9992891");

		BigInteger _a = new BigInteger("19877").add(new BigInteger("04812").multiply(new BigInteger(String.valueOf(256*256))));
		BigInteger _b = new BigInteger("9992891");

		//BigInteger _a = new BigInteger("08531").add(new BigInteger("07907").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("32047").add(new BigInteger("01527").multiply(new BigInteger(String.valueOf(256*256))));

		//BigInteger _a = new BigInteger("63197").add(new BigInteger("22629").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("32047").add(new BigInteger("01527").multiply(new BigInteger(String.valueOf(256*256))));

		//BigInteger _a = new BigInteger("2").pow(32).subtract(new BigInteger("5"));
		//BigInteger _b = new BigInteger("2").pow(31).subtract(new BigInteger("1"));


		BigInteger q = _a.multiply(_b);
		BigInteger a = bigIntegerSqrt(q);
		
		Grid grid = GridFactory.start();
		long t1;
		long t2;
		try {
			  t1 = System.currentTimeMillis();
		a = RSAChallengeLoop.excuteLoop(q, new BigInteger("3"), a);
			  t2 = System.currentTimeMillis();
		}finally {
			GridFactory.stop(grid.getName(), true);
		 }

		if(a!=null){
			BigInteger b = q.divide(a);
			BigInteger c = q.mod(a);

			System.out.println(q + " =");
			System.out.println(a + " * " + b + " + " + c);
			System.out.println();
			System.out.println("it takes "+(t2-t1)+"ms");
			System.out.println();
		}
	}
	public static BigInteger bigIntegerSqrt(BigInteger x) {
		BigInteger b1 = new BigInteger(x.toString()),
				b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1);

		while (b2.compareTo(b1) < 0) {
			b1 = new BigInteger(b2.toString());
			b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1);
		}
		return b1;
	}
}

【Grid版】RSAChallengeLoop.java

package grid;

import java.math.BigInteger;
import org.gridgain.grid.gridify.*;

public final class RSAChallengeLoop {
	private RSAChallengeLoop(){
		
	}

	@Gridify(taskClass = RSAChallengeTask.class)
	public static BigInteger excuteLoop(BigInteger q,BigInteger minRange,BigInteger maxRange){
		BigInteger a = maxRange;
		BigInteger b = q.divide(a);
		BigInteger c = q.mod(a);
		BigInteger TWO = new BigInteger("2");
		if (a.mod(TWO).equals(BigInteger.ZERO)) {
			a = a.subtract(BigInteger.ONE);
			c = c.add(b);
			b = b.add(c.divide(a));
			c = c.mod(a);
		}
		BigInteger ZERO = new BigInteger("0");
		while (!c.equals(ZERO) && a.compareTo(minRange)>=0) {
			a = a.subtract(TWO);
			c = c.add(b).add(b);
			b = b.add(c.divide(a));
			c = c.mod(a);
		}
		if (c.equals(ZERO)){
			return a;
		}else{
			return null;
		}
	}
}

【Grid版】RSAChallengeTask.java

package grid;

import java.math.BigInteger;
import java.util.*;
import org.gridgain.grid.*;
import org.gridgain.grid.gridify.*;

public class RSAChallengeTask extends GridifyTaskSplitAdapter<BigInteger>{

	protected Collection<? extends GridJob> split(int gridSize, GridifyArgument arg) {
		Object[] args = arg.getMethodParameters();
		BigInteger q = (BigInteger)args[0];
		BigInteger taskMinRange = (BigInteger)args[1];
		BigInteger taskMaxRange = (BigInteger)args[2];
		BigInteger numbersPerTask = taskMaxRange.divide(new BigInteger(String.valueOf(((int)(gridSize*5)))));

		List<GridJobAdapter<BigInteger>> jobs = new ArrayList<GridJobAdapter<BigInteger>>(gridSize);

		BigInteger jobMinRange = BigInteger.ZERO;
		BigInteger jobMaxRange = BigInteger.ZERO;
		for (int i = 0; taskMaxRange.compareTo(jobMaxRange) >0 ; i++) {
		System.out.print(i+":");
			jobMinRange = new BigInteger(String.valueOf(i)).multiply(numbersPerTask).add(taskMinRange);
			jobMaxRange = new BigInteger(String.valueOf(i + 1)).multiply(numbersPerTask).add(taskMinRange).subtract(BigInteger.ONE);

			if (taskMaxRange.compareTo(jobMaxRange)<0) {
				jobMaxRange = taskMaxRange;
			}
		System.out.println("jobRange is from "+jobMinRange+" to "+jobMaxRange);

			jobs.add(new GridJobAdapter<BigInteger>(q, jobMinRange, jobMaxRange) {
				public BigInteger execute() throws GridException {
					BigInteger q = getArgument(0);
					BigInteger min = getArgument(1);
					BigInteger max = getArgument(2);

					return RSAChallengeLoop.excuteLoop(q, min, max);
				}
			});
		}

		return jobs;

	}

	public GridJobResultPolicy result(GridJobResult result, List<GridJobResult> received) throws GridException {
		if(result.getData() != null) {
			return GridJobResultPolicy.REDUCE;
		}

		return super.result(result, received);
	}

	public BigInteger reduce(List<GridJobResult> results) {
		for (GridJobResult res : results) {
			if (res.getData() != null) {
				return res.getData();
			}
		}

		return null;
	}

}

【nonGrid版】RSAChallenge.java

package nongrid;

import java.math.BigInteger;

public class RSAChallenge {
	public static void main(String[] args){
		//BigInteger _a = new BigInteger("5674033");
		//BigInteger _b = new BigInteger("9992891");

		//BigInteger _a = new BigInteger("44147").add(new BigInteger("00660").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("9992891");

		BigInteger _a = new BigInteger("19877").add(new BigInteger("04812").multiply(new BigInteger(String.valueOf(256*256))));
		BigInteger _b = new BigInteger("9992891");

		//BigInteger _a = new BigInteger("08531").add(new BigInteger("07907").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("32047").add(new BigInteger("01527").multiply(new BigInteger(String.valueOf(256*256))));

		//BigInteger _a = new BigInteger("63197").add(new BigInteger("22629").multiply(new BigInteger(String.valueOf(256*256))));
		//BigInteger _b = new BigInteger("32047").add(new BigInteger("01527").multiply(new BigInteger(String.valueOf(256*256))));

		//BigInteger _a = new BigInteger("2").pow(32).subtract(new BigInteger("5"));
		//BigInteger _b = new BigInteger("2").pow(31).subtract(new BigInteger("1"));

		BigInteger q = _a.multiply(_b);
		BigInteger a = bigIntegerSqrt(q);
		
		long t1 = System.currentTimeMillis();
		a = RSAChallengeLoop.excuteLoop(q, new BigInteger("3"), a);
		long t2 = System.currentTimeMillis();

		if(a!=null){
			BigInteger b = q.divide(a);
			BigInteger c = q.mod(a);

			System.out.println(q + " =");
			System.out.println(a + " * " + b + " + " + c);
			System.out.println();
			System.out.println("it takes "+(t2-t1)+"ms");
			System.out.println();
		}
	}
	public static BigInteger bigIntegerSqrt(BigInteger x) {
		BigInteger b1 = new BigInteger(x.toString()),
				b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1);

		while (b2.compareTo(b1) < 0) {
			b1 = new BigInteger(b2.toString());
			b2 = (b1.pow(2).add(x)).shiftRight(1).divide(b1);
		}
		return b1;
	}
}

【nonGrid版】RSAChallengeLoop.java

package nongrid;

import java.math.BigInteger;

public final class RSAChallengeLoop {
	private RSAChallengeLoop(){
		
	}

	public static BigInteger excuteLoop(BigInteger q,BigInteger minRange,BigInteger maxRange){
		BigInteger a = maxRange;
		BigInteger b = q.divide(a);
		BigInteger c = q.mod(a);
		BigInteger TWO = new BigInteger("2");
		if (a.mod(TWO).equals(BigInteger.ZERO)) {
			a = a.subtract(BigInteger.ONE);
			c = c.add(b);
			b = b.add(c.divide(a));
			c = c.mod(a);
		}
		BigInteger ZERO = new BigInteger("0");
		while (!c.equals(ZERO) && a.compareTo(minRange)>=0) {
			a = a.subtract(TWO);
			c = c.add(b).add(b);
			b = b.add(c.divide(a));
			c = c.mod(a);
		}
		if (c.equals(ZERO)){
			return a;
		}else{
			return null;
		}
	}
}

この環境を必要としている方へ

母艦を含め、インストール作業なしにこの環境を構築できるカスタムKNOPPIXを作りました。使い方は以下の通りです。

  1. 使っていないPC達をハブで繋ぎます。−※1
  2. カスタムKNOPPIXをCD-Rに焼いて、それの1枚目を使って母艦を起動させます。
  3. 母艦が起動したら1GB程度のswapを割り当てます。−※2
  4. 母艦のCD-Rを2枚目に入れ替えます。
  5. 母艦でコンソールを開き、「su」→「/root/startserver.sh」と実行します。
  6. 計算ノードの電源を入れ、ネットワークから起動させます。−※3

※1…IPアドレスはAPIPAの範囲(169.254.0.0/16)を使用します。
※2…USBメモリを挿してfdiskとかmkswapとかswaponとかします。
※3…BIOSの設定などでNetwork起動の優先順位を上げてやります。

これで計算グリッドの出来上がりです。後はGridGainのサイトを参照しながらJavaプログラムを組んで実行すればいいだけです。なお、プログラムの実行に際して環境変数CLASSPATHjavaコマンドオプション等、つまずき所がいくつかあるので、母艦にあるgjavac.shやgjava.shも使ってみてください。

  • /usr/bin/gjavac.sh…必要なパスを通してからjavacを実行
  • /usr/bin/gjava.sh…必要なパスを通す+AspectJを有効にしてからjavaを実行
ライセンス

1枚目はKNOPPIXをカスタマイズしただけなのでGPLです。2枚目はGridGainとSunJDKを含みます。GridGainはLGPL、SunJDKは調べたけれどもはっきりとはわかりませんでした。再配布のためにCDを2枚に分けたんですが、「1枚にまとめても問題なし」or「2枚に分けても問題あり」な場合はご指摘ください。

ダウンロード

準備中

まとめ

パフォーマンス測定はなかなか期待通りの結果が得られませんでしたが、久々に大学の頃にやっていたPC相手の実験ができて楽しかったです。