日記帳

日記です。

BeanShell を gcj でコンパイル

BeanShell*1Java で書かれた組み込み可能なスクリプト言語です.

BeanShell を含めた Java ベースのスクリプト言語全般に言えることですが,インタープリタ単体で起動するとその度に JVM を起動することになって,起動に非常に時間がかかります.*2そのため PerlRubyPython のようには気楽に使えません.

そんなわけで BeanShell 2.0b4 を gcjコンパイルできるようにしてみました.

gcj でネイティブコードにコンパイルしてしまえば快適に起動します.

ある意味 http://d.hatena.ne.jp/sa-y/20060425 の続きかもしれません.
シリーズ化してみる?

*1:http://www.beanshell.org/

*2:Java アプリケーションに組み込んで使う場合はアプリケーションの起動時間として諦めが付くのですが…

TestSprite.bsh

なんか恒例のやつ.

BeanShell では任意の Java オブジェクトが扱えます.gcjコンパイルしてあるので sdl4gcj*1を使えば当然 SDL も扱えるわけです.

そういうわけで SDL の testsprite.c の BeanShell 版.

#!/usr/bin/bsh-Interpreter
import sdl4gcj.*;
import sdl4gcj.video.*;
import sdl4gcj.event.*;
import sdl4gcj.input.*;

TestSprite()
{
	final int MAX_SPEED = 1;
	final Random random = new java.util.Random();

	Sprite()
	{
		// init size
		int w = super.icon.w;
		int h = super.icon.h;
		int areaW = super.screen.w - w;
		int areaH = super.screen.h - h;

		// init location
		int x = random.nextInt(areaW);
		int y = random.nextInt(areaH);

		// init speed
		int dx, dy;
		while(dx == 0 && dy == 0)
		{
			dx = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
			dy = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
		}

		// for Rect interface
		int getX(){ return x; }
		int getY(){ return y; }
		int getW(){ return w; }
		int getH(){ return h; }

		void move()
		{
			x = x + dx;
			if (x < 0 || x > areaW)
			{
				dx = -dx;
				x += dx;
			}

			y = y + dy;
			if (y < 0 || y > areaH)
			{
				dy = -dy;
				y += dy;
			}
		} 

		void draw()
		{
			super.screen.blitSurface(super.icon, x, y);
		}
		return this;
	}

	int screenWidth = 640;
	int screenHeight = 480;
	int bitsPerPixel = 8;
	int videoFlags = Screen.SDL_SWSURFACE;
	int numberOfSprite = 100;

	Surface screen = null;
	Surface icon = null;

	int bgColor = 0;
	Object[] sprites = null;
	Rect[] rects = null;
	long ticks = 0;
	int frames = 0;
	boolean useFlip = false;

	void run()
	{
		this.initScreen();
		this.initIcon();
		this.initSprites();
		this.printInfo();
		this.mainLoop();
		this.freeIcon();
	}

	double getFPS()
	{
		return ((double)frames * 1000)/ticks;
	}

	void initScreen()
	{
		screen = Screen.setVideoMode(
			screenWidth, screenHeight,
			bitsPerPixel, videoFlags);

		bgColor = screen.mapRGB(Color.BLACK); 

		if ((screen.flags & screen.SDL_DOUBLEBUF) != 0) useFlip = true;
	}

	void initIcon()
	{
		// load icon image and change pixel format
		icon = Surface.loadBMP("icon.bmp");
		icon.setColorKey(Surface.SDL_SRCCOLORKEY|Surface.SDL_RLEACCEL);
		icon.displayFormat();

		// Run a sample blit to trigger blit acceleration 
		screen.blitSurface(icon);
		screen.fillRect(bgColor);
	}

	void freeIcon()
	{
		icon.freeSurface();
		icon = null;
	}

	void initSprites()
	{
		sprites = new Object[numberOfSprite];
		rects = new Rect[numberOfSprite];
		for (int i = numberOfSprite-1;i >= 0;i--)
		{
			sprites[i] = Sprite();
			rects[i] = (Rect)sprites[i];
		}
	}

	void updateSprites()
	{
		for (sprite : sprites)
		{
			sprite.move();
			sprite.draw();
		}
	} 

	void updateScreen()
	{
		if (useFlip)
			screen.flip();
		else
			screen.updateRects(rects);
	}

	void mainLoop()
	{
		boolean done = false;
		EventManager event = new EventManager();
		frames = 0;
		System.gc();
		ticks = SDLSystem.getTicks();
		while (!done)
		{
			frames++;
			while(event.pollEvent() > 0)
			{
				switch (event.type)
				{
					case event.SDL_KEYDOWN:
					case event.SDL_QUIT:
						done = true;
						break;
					default:
						break;
				}
			}

			screen.fillRect(bgColor);
			updateSprites();
			updateScreen();
		}

		ticks = SDLSystem.getTicks() - ticks;
	} 

	void printInfo()
	{
		if (screen != null)
		{
			System.out.println("Screen is at " + screen.getPixelFormat().getBitsPerPixel() + " bits per pixel");
			var flags = screen.getFlags();
			if ((flags & Surface.SDL_HWSURFACE) == Surface.SDL_HWSURFACE)
				System.out.println("Screen is in video memory");
			else
				System.out.println("Screen is in system memory");

			if ((flags & Surface.SDL_DOUBLEBUF) == Surface.SDL_DOUBLEBUF)
				System.out.println("Screen has double-buffering enabled");
		}

		if (icon != null)
		{
			var iconFlags = icon.getFlags();
			if ((iconFlags & Surface.SDL_HWSURFACE) == Surface.SDL_HWSURFACE)
				System.out.println("Sprite is in video memory");
			else
				System.out.println("Sprite is in system memory");

			if ((iconFlags & Surface.SDL_HWACCEL) == Surface.SDL_HWACCEL)
				System.out.println("Sprite blit uses hardware acceleration");

			if ((iconFlags & Surface.SDL_RLEACCEL) == Surface.SDL_RLEACCEL)
				System.out.println("Sprite blit uses RLE acceleration");
		}
	}
	return this;
}

// main
SDLSystem.init(SDLSystem.SDL_INIT_VIDEO);
try
{
	testSprite = TestSprite();
//	testSprite.numberOfSprite = 50;
//	testSprite.videoFlags = Screen.SDL_ANYFORMAT;
//	testSprite.videoFlags = Screen.SDL_DOUBLEBUF|Screen.SDL_FULLSCREEN;
//	testSprite.bitsPerPixel = 16;
	testSprite.run();
	System.out.println(testSprite.getFPS() + " frames per second");
}
catch (e)
{
	System.out.println(e);
	e.printStackTrace();
}
finally
{
	System.out.println("SDLSystem.quit()");
	SDLSystem.quit();
}

なんかすっごいJavaそっくり.とりあえず実行してみる.

% bsh-Interpreter TestSprite.bsh 
Screen is at 32 bits per pixel
Screen is in system memory
Sprite is in system memory
Sprite blit uses RLE acceleration
42.78280978994026 frames per second

C言語版,Java版,JavaScript版と比べると以下のような感じです.

C言語 327.05
Java版(gcj版) 329.95
JavaScript版(インタープリタ) 127.51
JavaScript版(コンパイル) 162.48
BeanShell版 42.78

実行環境は以下の通り.

  • Linux 2.4.31
  • XOrg 6.8.2
  • gcj 3.3.2
  • X は 32 bpp で起動
  • SDL は 32 bpp の SDL_SWSURFACE を Window モードで作成

とりあえず結論,すっごく遅い.