いろきゅうの(元)はてなダイアリー

はてなダイアリーから移行中…

Java版だけは取れた

数値取っておいてなんなんですが、ちょっと気になることはあるので、適正な値かどうかはちょっと微妙故にこっそり公開。(ぉ

あ、ベンチ系に詳しい人はソースコードに突っ込みよろしくお願いします。^^;
# ちょっと自分も気になっているところがあるけれども…


ちなみに、なんで Java かっていうと、

  • Linux では Cレベルでの Thread の作り方が分からなかったから。 fork 何それおいしいの?
  • VC と GCC じゃ、Cソースコードの最適化度合いが違う -> メインルーチンぐらいは asm でコードを書かなきゃいけない -> めんどい (ぉ

というわけで、Javaりました。 なもんで、以下目安で。^^;

環境1 - メインマシン

CPU Athlon 64 x2 4400+
Mem 6GB - DDR2 800 DualChannel
OS WinXP x64 SP2
Java 1.6.0_07 32bit版

環境2 - サブマシン

CPU Athlon 1500+
Mem 1.5GB - DDR 400
OS WinXP SP3
Java 1.6.0_07 32bit版

環境3 - 現サーバー機

CPU Pentium3 850 x 2
Mem 640MB - PC133(?)
OS CentOS 5
Java 1.6.0_07

環境4 - ATOM

CPU ATOM 330
Mem 2GB - DDR2 533
OS WinXP x64 SP2
Java 1.6.0_07 32bit版

テスト内容

以下の(悲惨な)Javaのコードを各環境で実行。 コマンドライン引数より1つ数値を与えることにより、その数値分のスレッドが生成されます。

で、最後のスレッドが終了するまでの時間を計測しました。

import java.util.Date;

public class Bench extends Thread
{
	static final int TIMES      = 0x8000;
	static final int ARRAY_SIZE = 100000;

	int m_nRand;
	int m_nResult;
	int m_nID;

	public Bench(int nID)
	{
		m_nID   = nID;
		m_nRand = (int)(Math.random() * 0x1000);
	}

	private int rand()
	{	// えせ乱数
		return m_nRand = ((m_nRand * 0x654321) | (m_nRand << 16) ^
			(m_nRand >> 7)) & 0x7fffffff;
	}

	public void run()
	{
		Date dateStart = new Date();
		System.out.println("thread " + m_nID + " start...");
		int[] anArray = new int[ARRAY_SIZE];
		for(int n = 0; n < TIMES; ++n)
		{
			for(int a = 0; a < ARRAY_SIZE; ++a)
			{
				int r1 = rand();
				int r2 = rand();
				int r3 = rand();
				int r4 = rand();
				
				// 適当に演算
				anArray[a] += ((r1 + r2 * r4) | (r3 >> (r4 & 0x06))) ^ r1;
				anArray[a] &= 0x7fffffff;
			}
		}
		
		// 何の結果かわからないけど、結果算出
		for(int i = 0; i < ARRAY_SIZE; ++i)
		{
			m_nResult += anArray[i];
		}
		Date dataEnd = new Date();
		System.out.println("thread " + m_nID + " end / " +
			(dataEnd.getTime() - dateStart.getTime()) + "ms");
	}
	
	public static void main(String[] str)
	{
		int nThreadCount = Integer.parseInt(str[0]);

		Bench[] anTests = new Bench[nThreadCount];
		
		for(int i = 0; i < nThreadCount; ++i)
			anTests[i] = new Bench(i);
			
		Date pcStart = new Date();
		for(int i = 0; i < nThreadCount; ++i)
			anTests[i].start();
		for(int i = 0; i < nThreadCount; ++i)
		{
			try
			{
				anTests[i].join();		// コレはひどい
			}
			catch(InterruptedException e)
			{}
		}
		Date pcEnd  = new Date();
		
		System.out.println((pcEnd.getTime() - pcStart.getTime()) + "ms");
	}
};


http://ir9.jp/hd08/1006_00.png

Dual Core / Dual CPU は、モロに特性が出てて面白いなぁ とまず思った俺。(ぉ

コアが2つあるわけですから、スレッド2つまでであればパフォーマンス落ちないということがグラフからはっきりわかりますね。 (ちょっとあがってる気もするけれど、JavaVM が勝手に "大人の事情" な何かをやってるとする:ぉ)

で、下2つのパフォーマンスが良かったCPU(Athlon 4400+ と ATOMね)だけを抜き出してみます。


http://ir9.jp/hd08/1006_01.png

ATOM すげー。超すげー。 一番処理速度が速いじゃないですか! なんでかスレッドが増える度の処理速度が安定していませんけれども(ぉ)、それでも大変早い。

そして4スレッド目から5スレッド目に一気に右肩上がりな所をみると、ハイパースレッディングってのはまぁ効果があるとみて良いのかもしれません。 一時期「いみねぇよアレ」とか言われてましたけれどもそんなことはないのかも。(2スレッド目から3スレッド目にかけて極端な右肩上がり *ではない* のが謎ですけども…)

そして2スレッドまでの様子を見る限り、コア単位の処理速度ってのは「Athlon 64 XP 4400+」とほぼ同じなのかも。 8ワット でココまで処理できるって凄いですなー。Athlon 64 XP 4400+ は、確か 65ワット ぐらい消費するはずなのに…(ぉ




ところで。

冒頭でも解説したとおり、Athlon 4400+ は、ウチのメインマシンなんですよね。 新入りの ATOM 君の方がパフォーマンス高いんですよね。

ね。

ね。

ね。

…どうしようかしら。(ぉ orz

と言ったものの

あのソースコードで凄い気になってることがあるんです。

1万個のintの配列作ってるじゃないですか。すなわち40KBの配列。 これが8スレッドになると、合計320KBも食うことになるわけですけど、これ CPU のキャッシュに乗るの? みたいな。
Pen3 でも L2 で 512KB ぐらいは乗っていたような気がするので、大丈夫だとは思うんですが…… もしあふれていたら、このベンチマーク「メモリバスベンチ」になりかねない…と。 キャッシュから外れるんだ。メモリにアクセスするしかないじゃない。

というわけで、ちょっとこのベンチは間違ったかなぁ…と(ぉ ^^;

そもそも、JavaVM上で走らせている以上、んなこと気にするレベルじゃねーだろ話もあったり無かったりも……


うーん、もうちょっとベンチの内容を再考するかしら……