タグ別アーカイブ: Haxe

Haxe3の新機能まとめ

本日2013/05/25、プログラミング言語Haxeのバージョン3が正式にリリースされました!
Haxe3のダウンロード:http://haxe.org/download

ということで、Haxe3がこれまでのHaxe2.10とどうが違うのかを徹底的に紹介していきます!


新機能

Java, C#ターゲットのサポート

これまで、Flash(AIRも)、JavaScript、C++、PHP、Nekoへの出力がサポートされていましたが、これらに新たにJava、C#が追加されました。

‘シングルクオテーション内での、変数展開

Haxe3から、「’」と「”」の扱いが変わります。

var x = 10;
trace( 'x is $x' )	//x is 10
trace( "x is $x" )	//x is $x

これまでのString.Format()関数の代替です。
「’」内で$変数名や${式}と記述した場合に、その変数や式が文字列内に組み込まれます。

新しいハッシュテーブルのクラス(Map)

クロスプラットフォームの辞書(ハッシュテーブル)のクラスが文法レベルでサポートされました。

var price:Map <String , Int > = [
	"カレー"        => 420,
	"カツカレー"    => 550,
	"ラーメン"      => 380
];

price.set("カレー",400);
trace( price.get( "カレー" ) ); //400

さらに、配列の演算子を使って値を参照することができます。

price["カレー"] += 30;

さらに、enumをカギに使うことも可能です。

配列内包表記

for文や、while文を使った配列の初期化がサポートされました。

//0~20の偶数の配列!
var evenArray = [for (i in 0...21) if (i & 1 == 0) i];

パターンマッチング

switch文を使ったパターンマッチングがサポートされました。
多機能なので、詳しくはhttp://haxe.org/manual/pattern_matchingを見てください。

パッケージをまとめてインポート

import my.pack.*;

のような書き方が出来るようになりました。

bind関数

callback構文の代替です。
より簡単に、関数の引数を束縛することができます。

function add(a:Int, b:Int) { trace( '$a,$b' ); }
var func1 = add.bind(3);
var func2 = add.bind(_, 4);

func1( 2 ); //add(3,2)と同じ
func2( 0 ); //add(0,4)と同じ

抽象型(abstract)

Haxe3では、新たに抽象型(abstract)が加わりました。
これは、JavaやC#の抽象クラスや抽象メソッドとは大きく異なるものです。

Haxeの抽象型は実行時には他の型として実行される型で、変換元となる型、変換先となる型、変換方法などを定義して使用します。
カスタム配列アクセス、演算子オーバーロードなどが使用出来ます。

NekoのIntが31bit整数から32bit整数に

Haxe2.10までのNeko出力では、

var argb = 0x66FFFFFF;

のように、31bitであらわせない数値が含まれているとコンパイルエラーになってました。
Haxe3では上記のコードは正常に動作します。

出力されるコードが綺麗に

Haxeには、未使用の関数や変数を自動で取り除くデッドコード削除という機能があります。
Haxe3では、この強さを3段階から選択することが出来ます。

–dce no: デッドコード削除を行わない(Haxe2.10のデフォルト)
–dce std: 自分で書いたコードから呼び出していない、標準ライブラリ、外部ライブラリのコードを削除。(新機能、Haxe3のデフォルト)
–dce full: main関数から呼び出していないコードについて削除を行う。(Haxe2.10で–dead-code-eliminationを指定した場合と同じ

デフォルトでのデッドコード削除は強化されたため、基本的にHaxe3から出力されるコードは綺麗になります。

型パラメータ(ジェネリクス)の強化。

構造的部分型による、型パラメータの束縛。

static function test < T: {function clone():T} >( a:T ) {}

enum(列挙型)のコンストラクタに対する、型パラメータの設定。

enum EnumTest{
	 HOGE < T >(t:T);
}

など、型パラメータの機能が強化されました。

マクロ機能の改善

詳しくはhttp://haxe.org/manual/haxe3/features

その他

  • コンストラクタのインライン化が可能に
  • Flashで、swcをそのまま外部ライブラリとして利用可能に
  • traceに複数の値が指定可能に

新しい標準ライブラリ

標準ライブラリの詳細はhttp://haxe.org/apiに載っています。

クロスプラットフォームのZIP圧縮、解凍

パッケージ: haxe.zip.*;
Flash, JavaScript, C++, PHP, Neko, C#, Javaで動くzipライブラリです。

暗号ライブラリの強化

パッケージ: haxe.crypto.*;
もともとあったMd5,Sha1,BaseCodeに加えて、Crc32,Adler32が使えるようになりました。

データ構造の追加

パッケージ: Map, haxe.ds.*;
いままで標準で、
Array < T >(可変長配列),
List < T >(連結リスト),
Hash < T >(文字列が鍵のハッシュ表)をサポートしていたのですが、

Hashが無くなり、

あらたに、
Map < K, V >(汎用なハッシュ表)、
GenericStack < T >(スタック)、
Vector < T >(固定長配列)、
BalancedTree < T >
が加わりました

これらはすべてクロスプラットフォームです。
VectorはいくつかのターゲットではArrayより速く、すべてのターゲットでArrayと同等以上の速度が出ます。

Enumの補助クラス

パッケージ: haxe.EnumTools;

JavaScriptターゲットがHTML5に対応

パッケージ: js.html.*;

HTML5の機能が標準で利用できるようになりました。
svg,webglなどもサポートされます。

その他

  • Array.map、Array.filter

移動したライブラリ

cpp.Sys Sys
php.Sys Sys
neko.Sys Sys
Lib.print Sys.print
neko.db sys.db
php.db sys.db
cpp.net sys.net
php.net sys.net
neko.net sys.net
cpp.io sys.io
php.io sys.io
neko.io sys.io
neko.zip haxe.zip
cpp.Utf8 haxe.Utf8
php.Utf8 haxe.Utf8
neko.Utf8 haxe.Utf8
haxe.BaseCode haxe.crypto.BaseCode
haxe.SHA haxe.crypto.Sha1
haxe.MD5 haxe.crypto.Md5
js.Lib.window js.Browser.window
js.Lib.document js.Browser.document
IntIter IntIterator
tringTools.isEOF StringTools.isEof
haxe.Stack haxe.CallStack
EReg.customReplace EReg.map

新しいメタデータ

@:to, @:from
抽象型(abstract)で使用します。型の変換方法を定義します。
@:arrayAccess
抽象型(abstract)で使用します。カスタム配列アクセスを定義します。
@:op
抽象型(abstract)で使用します。演算子オーバーロードを定義します。
@:publicFields
クラスのデフォルトの可視性をpublicに変更します。
@:noCompletion
コンパイラの補完機能に反映されないようにします。
@:noUsing
usingした際に反映されないようにします
@:font
Flashのみです。フォントの埋め込みを行います。文字コードの範囲指定も可能です。

新しいコンパイラオプション

-dce [std|full|no]
デッドコード削除の強さを(no,std,full)から選択します。
-swf-lib-extern < file >
externのSWFファイルを指定する
-version
バージョンの確認
–help-defines
有効なコンパイラ定義(-D)のリストを表示
–help-metas
コンパイル時に使用されるメタデータを表示

置き換えられたもの

–dead-code-eliminationが-dce fullに
–js-modernがデフォルトに
これにより、JavaScript全体が(function(){“use strict”; })()に囲まれます。コンパイラオプション-D js-classicでこれを回避できます。さらに詳しくは、http://haxe.org/doc/js/modern
extendsとimplementsを同時に指定するときの書き方が変わりました。
haxe2:

... extends haxe.Template, implements InterfaceSyntax2 { }

haxe3:

... extends haxe.Template implements InterfaceSyntax2 { }
プロパティのgetter,setterの書き方が変わりました
詳しくは、http://haxe.org/manual/haxe3/migration#property-accessors

無くなったもの、出来なくなったもの

jQueryライブラリの自動的な埋め込み
Haxe2以前では、標準ライブラリに含まれるJQueryのライブラリを使った場合に出力されるjsにjQueryのコードが埋め込まれていました。
Haxe3では、このjQueryの埋め込みが無くなりました。
対策: コンパイル時に-D embed-jsのオプションを指定する(2013/07/15加筆 コメントで教えてもらいました) または、jQueryのjsファイルをダウンロードしてきてHaxeのjsより前に埋め込む。
Array < Dynamic > に対する型推論
対策: 明示的に型指定を行う
inline変数を参照型の値で初期化
対策: inline関数を使う
Int32
対策: Intを使う
String.format()
対策: ”(シングルクオテーション)を使う。
callback
対策: bind関数を使う
プロパティでget, setを両方指定した際の、デフォルト変数
対策: @:isVarを明示的に指定する
Hash, IntHashクラス
対策: Mapを使う
haxe.Publicインターフェース
対策: @:publicメタデータを使う
haxe.rtti.Genericインターフェース
対策: @:genericメタデータを使う
コンパイラオプション -as3-native

Haxe3での主な変更点は以上のとおりです!あれが足りない、ここが間違ってるなどあればコメントしていただけると嬉しいです。

2013/05/31_訂正:
@:noCompleteって書いてありましたが、正しくは@:noCompletionでした。
3.0RC出たあたりでwikiが変更されてたのを見逃してました。
http://haxe.org/manual/tips_and_tricks?version=15566

2013/08/21_追記:

C#,Javaの正式サポートって書いたんですが、よくよくいろんなところ見返してみたら、betaから、stableになりましたよっていう正式な発表が見つかれませんでした…

2.09リリース時点では、Haxe3.0でC#,Javaをサポートする予定(http://www.silexlabs.org/133720/the-blog/haxe-3-and-4-plans-for-world-domination/)で、2.10リリース時http://haxe.org/doc/release/2.10にも”their final implementation in Haxe 3.0″って書いてあったので、3.0でC#,Javaが正式サポートだと思ってたのですが、3.0公開後の発表ではそこらへんに触れてるものがなかったです

逆に3.0のJava,C#周りの記述でまだbetaだよ記述も無くて、あたらしいgithubレポジトリにはJavaやC#のターゲットのところにもbetaと書かれてないので、このままこっそりbetaの文字が消えてくような気もします。

とはいえ、もしかしたら、次のリリース以降でbetaじゃなくなりましたよって発表があるかもしれません。

ここらへんちゃんと知ってる人がいたら教えてほしいです。

右クリックから一発で素材SWCが生成できるFlashDevelopプラグイン作った

cats

[FlashDevelop Assets Packager Plugin]


前置き

だいぶ昔ですが、Flash開発での素材管理についてこんな話題がありました。

「Flash 開発の議論 – あなたは Embed 派、SWC 派、Load 派? – Togetter」

Flash開発者にとって素材管理は悩ましい問題です。

タイムライン中心でFlashを作る場合は、Adobe Flash Professionalの素材管理機能をそのまま使えばいいので、問題になるのはFlashDevelop, FlashBuilder, FDTなどのエディタを使ってコードをガリガリ書いてFlashの開発をする場合です。

いろんな管理方法がありますが、それぞれにメリット、デメリットがあります。

自分が知っている主な方法は5種類です。

これまでの素材管理方法

Flash Professionalを使わない方法

① 素材1つ1つをLoaderで読み込み (ひとつづつLoad派)
例: 「FlashゲームPG講座 For AS3.0【ファイルの読み込みについて】」

② 素材1つ1つを[Embed()]タグで埋め込み (ひとつづつEmbed派)
例: 「Flash ActionScript3で画像を表示する ≪ Walk on apps.」

○ メリット

  • 有料ソフトウェアが要らない
  • 素材の変更がすぐ反映される

× デメリット

  • 素材一つ一つのパスやコードを書かないといけない。

Flash Professionalを使う方法

③素材をSWC化して、ライブラリとして使う。(SWC派)
例: 「SWCを利用したFlash制作の分業ワークフロー:FlashとFlash Builder連携開発 | デベロッパーセンター」

④素材をSWF化して、Loaderで読み込み。(SWF Load派)
例:「Flex で別 SWF のシンボルをロードする方法あれこれ – 俺の成長日記」

⑤素材をSWF化して、[Embed()]タグで埋め込み。(SWF Embed派)
例:「馬鹿全 – Flex と Flash CS3 の ハイブリッド SWF について」

○ メリット

  • コードの記述量が減らせる
  • フォントの範囲指定とか、ファイルの圧縮とかの管理機能がついてくる

× デメリット

  • Flash Proが高い(CS6は定価で84000円)
  • Flash Proが重い
  • 素材を編集した際の再設定がめんどくさい
  • 複数の開発ツールをまたぐので、敷居が高い

自分がよく使ってるのは③です。ただ、Adobe Flash Professionalが必要というのはかなりのネックになります。素材変更するごとにいちいち設定しなおしてパブリッシュするのメンドクサイですし、Flash Proが入ってないところで編集したい場合もあります。とはいえ、この中では一番楽なのでFlash Pro持ってる人はこの素材管理をしてる人が多いと思います。


…でもさ、Flash Proを使わなくてもさ、Embedの記述なんて自動化できるし、SWCの生成だってFlex SDKで出来るんだよね。この際、FlashDevelopのプラグインでも作っちゃった方が楽なんじゃねーの?

ってことで作りました。

FlashDevelop + Assets Packager Pluginでラクラク素材管理

Assets Packager PluginはFlashDevelop4用のプラグインです。

特徴

  • フォルダを、右クリックですぐ使える
  • [Embed]タグを記述したAS3ファイル、SWC、SWFが一発で生成
  • ディレクトリ構造とファイル名がそのままパッケージ名とクラス名に(普通のAS3ファイルといっしょ!)
  • png、gif、jpgをBitmapDataとして埋め込み
  • otf、ttf、ttc、dfontをFontとして埋め込み
  • mp3をSoundとして埋め込み
  • svg、svgzをSpriteとして埋め込み。(v1.5から)
  • as、mxmlはそのままのコードを埋め込み。(v1.2から)
  • txt、xml、swfなどその他すべてのフォーマットはByteArrayとして埋め込み
  • フォントファイルを、右クリック>>[フォントの設定]で埋め込む文字の指定が可能
  • Haxeでも使用可。生成されたSWFを-swf-libで指定。

2分ちょっとでわかる。Assets Packagerのインストール法と使い方。

インストール法はSourceForgeから.fdzダウンロードしてきて、ダブルクリックするだけです。

基本的な使い方

素材を放りこんだフォルダをFlashDevelopのプロジェクトマネージャー上で右クリック>>[素材をパッケージ]を選択するだけで、libフォルダ内に[Embed]を記述したAS3、SWC、SWFの3種類のファイルが一度に生成されます。

例えば、assetsフォルダ内に以下の4つのファイルを置いてパッケージ化した場合

  • assets/image/Background.png
  • assets/sound/SoundEffect.mp3
  • assets/font/Pixcell.ttf
  • assets/text/HelloWorld.txt

次の4つのクラスが自動で生成されます。

  • assets.image.Background (BitmapData)
  • assets.sound.SoundEffect (Sound)
  • assets.font.Pixcell (Font)
  • assets.text.HelloWorld (ByteArray)

これらを利用するためにどのようなコードを書けばいいかはサンプルコードを用意してあります。

SourceForgeでexample.zipをダウンロードしてください。

基本的には、生成されたAS3ファイルかSWCの参照を追加して各クラスをnewするだけです。それぞれ、BitmapData、Sound、Font、ByteArrayとして使うことが出来ます。

package {
	import assets.text.HelloWorld;
	import assets.font.Pixcell;
	import assets.image.Background;
	import assets.sound.SoundEffect;
	import flash.display.*;
	import flash.events.*;
	import flash.media.Sound;
	import flash.text.*;
	import flash.utils.ByteArray;
	
	[SWF(width=480,height=360)]
	public class EmbedTest extends Sprite {
			
		function EmbedTest() {
			//Font(ttf)
			var font:Font = new Pixcell();
			var tf:TextField = new TextField();
			tf.autoSize = "left";
			tf.embedFonts = true;
			tf.selectable = false;
			tf.defaultTextFormat = new TextFormat( font.fontName, 48 );
			
			//ByteArray(txt)
			tf.text = (new HelloWorld() as ByteArray).toString();
			tf.height = tf.textHeight;
			tf.x = (stage.stageWidth - tf.width) / 2;
			tf.y = (stage.stageHeight - tf.height) / 2;
			addChild( tf );
			
			//BitmapData(png)
			var g:Graphics = graphics;
			g.beginBitmapFill( new Background() );
			g.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
			
			//Sound(mp3)
			stage.addEventListener( 
				MouseEvent.CLICK, 
				function( e:Event ):void { new SoundEffect().play(); }
			);
		}
	}
}

結果:[example.swf]

フォントの設定

さらに、フォントファイル(ttf,otf,dfont)を右クリック>>[フォントの設定]をクリックでフォントの埋め込み範囲やフォント名に関する設定を行えます。

フォント_small

詳細設定

他のプラグインと同じようにツール>>環境設定>>AssetsPackagerから各種設定が行えます。
ここで拡張子の設定、各出力のオン/オフ切り替え、出力フォルダの指定ができます。

ライセンス

MITライセンスです。

更新履歴

2013/05/13 v1.5.0
– 拡張子設定”Sprite Extentions”,”MovieClip Extentions”を追加
– ‘.svg’,’.svgz’をSpriteとして対応
2013/04/13 v1.4.0
– 拡張子設定”Bitmap Extentions”を追加
– 拡張子’ttc’をフォントとして対応
– フォント設定に”太字”,”イタリック”を追加。
2013/03/18 v1.3.0
– 設定にcompcのコンパイラオプションを追加。
– コンパイル前のファイル名チェックを追加。
– ファイル名のマルチバイト文字に対応。
2013/03/16 v1.2.2
– 大文字の拡張子が無視されていたのを修正。
2013/03/15 v1.2.0
– .as,.mxmlのファイルをクラスとして埋め込まれるように設定。
– さらに、詳細設定で無視されるファイルの拡張子が指定可能に。
2013/03/14 v1.1.2
-C:以外のボリュームのプロジェクトで使用した際のエラーを修正。

マージソートで速度比較【C#, C++, Java, PHP vs. Haxe】

 最近はWindowsアプリケーションの制作にC#を使ってるんですが、Haxe/C++ってどうなのよ、C++と比べてどのくらいの速さなのよ?ってことが気になったので速度比較しました。つかったのは、マージソートです。理由は先日のCODE VS 2.0(http://codevs.jp/)で配列操作がボトルネックになってるように見えたからです。

マージソートって何?って人もいると思うのでちょっと紹介。

右上のrandomを押したあと、左上でMargeを選択してみましょう。
このような方法で数字の大小を比較して、順番に並べるのがマージソートです。

速度比較用コード

Haxe

package;

class Main {
    static inline var SIZE = 1000000;
    
	static function main() {
		var start = getTime();
        var arr = new Array<Int>();
		
		//Neko,PHP,Java,C#は初めに領域確保。これをやらないと配列を伸ばすごとに時間がかかる。
		#if (neko || php || java || cs)
		arr[SIZE - 1] = 0;
		#end
		
		for( i in 0...SIZE ){ arr[i] = SIZE - i; }
        margeSort( arr );
        var time:Int = Math.round(getTime() - start);
		trace( time + "ms" );
	}
     
    public static function margeSort( arr:Array<Int> ){
        var l   = arr.length;
        var tmp = new Array<Int>();
		
		#if (neko || php || java || cs)
		tmp[SIZE - 1] = 0;
		#end
		
        var i = 1;
        while( i < l ) {
            var j = 0;
            while( true ){
                var l0 = j + i;
                if( l0 >= l )    break;
                 
                var l1 = l0 + i;
                if( l1 > l ) l1 = l;
                 
                var p0 = j, p1 = l0, val0 = arr[p0], val1 = arr[p1], tp = 0;
                 
                while ( true ){
                    if( val0 <= val1 ){
                        tmp[ tp++ ] = val0;
                        if( ++p0 == l0 ) break;
                        val0 = arr[ p0 ];
                    }else{
                        tmp[ tp++ ] = val1;
                        if( ++p1 == l1 ){
                            var tl = l0 - p0;
                            copyArray( arr, p0, arr, l1 - tl, tl );
                            break;
                        }
                        val1 = arr[ p1 ];
                    }
                }
                copyArray( tmp, 0, arr, j, tp );
                j = l1;
            }
            i <<= 1;
        }
    }
     
    public static inline function copyArray( source:Array<Dynamic>, sourceIndex:Int, target:Array<Dynamic>, targetIndex:Int, length:Int ) {
        var i = length;
        while( --i >= 0 ) target[targetIndex + i] = source[sourceIndex + i];
    }
	
	public static inline function getTime() {
		#if (flash || js || cs)
		return Date.now().getTime();
		#else
		return Sys.time() * 1000;
		#end
	}
}

 関数の頭にinlineと付けるだけでインライン展開できるのがポイント。Haxeから出力されたC++では、copyArrayの関数自体は消えてなくなり、中身のループが呼び出し位置に展開された形になります。

 あと、配列の長さが指定できないのが特徴で、PHP, Neko, C#, Javaに出力する際は注意が必要になります。これらの言語を選択した場合、配列の長さを伸びるごとに配列の長さに比例した時間が取られてしまいます。あらかじめ配列を伸ばしておいてから数値を代入しましょう。

 せっかくなので、JavaScript, AIR, NekoVM, PHP, C++, C#, Javaに出力しました。上のコードでこれらすべての言語に対応してます。

C#

using System;
using System.Diagnostics;

namespace SpeedTest{
	class Program{
		public const int SIZE = 1000000;
		
		public static void Main(string[] args){
			var sw = new Stopwatch();
			sw.Start();
			
			int[] arr = new int[ SIZE ];
			for( int i = 0; i < SIZE; i++ ){
				arr[i] = SIZE - i;
			}
			MargeSort( arr );
			
			Console.WriteLine( sw.ElapsedMilliseconds + "ms" );
			Console.ReadKey(true);
		}
		
		//小さい順にソート
		public static void MargeSort( int[] arr ){
			int 	l 	= arr.Length;
			int[] 	tmp	= new int[l];
			
			for( int i = 1; i < l; i <<= 1 ){
				for( int j = 0, l0, l1; ; j = l1 ){
					l0 = j + i;
					if( l0 >= l )	break;
					
					l1 = l0 + i;
					if( l1 > l )	l1 = l;
					
					int p0 = j, p1 = l0, val0 = arr[p0], val1 = arr[p1], tp = 0;
					
					while( true ){
						if( val0 <= val1 ){
							tmp[ tp++ ] = val0;
							if( ++p0 == l0 ) break;
							val0 = arr[ p0 ];
						}else{
							tmp[ tp++ ] = val1;
							if( ++p1 == l1 ){
								int tl = l0 - p0;
								var targetIndex = l1 - tl;
								var i1 = tl;
								while(--i1 >= 0) {
									arr[targetIndex + i1] = arr[p0 + i1];
								}
								
								break;
							}
							val1 = arr[ p1 ];
						}
					}
					
					{
						var i1 = tp;
						while(--i1 >= 0) {
							arr[j + i1] = tmp[i1];
						}
					}
				}
			}
		}
	}
}

 copyArrayの部分はループに変更しています。普段ならそんなめんどくさいことやらないでArray.Copy()を使ってますが、Array.Copy()使うと50msくらい遅くなります。

C++

#include <iostream>
#include <time.h>

using namespace std;
int SIZE = 1000000;

void margeSort( int* arr, int l ){
    int* 	tmp	= new int[l];

    for( int i = 1; i < l; i <<= 1 ){
        for( int j = 0, l0, l1; ; j = l1 ){
            l0 = j + i;
            if( l0 >= l )	break;

            l1 = l0 + i;
            if( l1 > l )	l1 = l;

            int p0 = j, p1 = l0, val0 = arr[p0], val1 = arr[p1], tp = 0;

            while( true ){
                if( val0 <= val1 ){
                    tmp[ tp++ ] = val0;
                    if( ++p0 == l0 ) break;
                    val0 = arr[ p0 ];
                }else{
                    tmp[ tp++ ] = val1;
                    if( ++p1 == l1 ){
                        int tl = l0 - p0;
                        int targetIndex = l1 - tl;
                        int i1 = tl;
                        while(--i1 >= 0) {
                            arr[targetIndex + i1] = arr[p0 + i1];
                        }
                        break;
                    }
                    val1 = arr[ p1 ];
                }
            }
            {
                int i1 = tp;
                while(--i1 >= 0) {
                    arr[j + i1] = tmp[i1];
                }
            }
        }
    }
}

int main() {
    int start = clock();
    
    int* arr = new int[SIZE];
    for( int i = 0; i < SIZE; i++ ){ arr[i] = SIZE - i; }
    margeSort( arr, SIZE );
    
    std::cout << (clock() - start) << "ms\n";
    return 0;
}

 C++は苦手です。やりようによってはまだまだ高速化できるのかもしれませんが、わからないのでできません。C#のコードをなるべくそのまま移植しました。

Java

public class Main {
    public static final int SIZE = 1000000;
	
    public static void main(String[] args) {
        // TODO code application logic here
        long start = System.currentTimeMillis();
        int[] arr = new int[ SIZE ];
        for( int i = 0; i < SIZE; i++ ){
            arr[i] = SIZE - i;
        }
        margeSort( arr );
        
        System.out.println( (System.currentTimeMillis() - start) + "ms" );
    }

    //小さい順にソート
    public static void margeSort( int[] arr ){
        int 	l 	= arr.length;
        int[] 	tmp	= new int[l];

        for( int i = 1; i < l; i <<= 1 ){
            for( int j = 0, l0, l1; ; j = l1 ){
                l0 = j + i;
                if( l0 >= l )	break;

                l1 = l0 + i;
                if( l1 > l )	l1 = l;

                int p0 = j, p1 = l0, val0 = arr[p0], val1 = arr[p1], tp = 0;

                while( true ){
                    if( val0 <= val1 ){
                        tmp[ tp++ ] = val0;
                        if( ++p0 == l0 ) break;
                        val0 = arr[ p0 ];
                    }else{
                        tmp[ tp++ ] = val1;
                        if( ++p1 == l1 ){
                            int tl = l0 - p0;
                            System.arraycopy( arr, p0, arr, l1 - tl, tl );
                            break;
                        }
                        val1 = arr[ p1 ];
                    }
                }
                System.arraycopy( tmp, 0, arr, j, tp );
            }
        }
    }
}

 C#のコードをほぼそのまま移植しました。こうやって見るとほんとC#にそっくりです。配列のコピーはループよりSystem.arraycopy()のが高速でした。

PHP

<?php
function margeSort($arr) {
	$l = count($arr);
	$tmp = array();
	for ($i = 1; $i < $l; $i <<= 1) {
		$j = 0;
		while(true) {
			$l0 = $j + $i;
			if($l0 >= $l) { break; }
			$l1 = $l0 + $i;
			if($l1 > $l) { $l1 = $l; }
			$p0 = $j; $p1 = $l0; $val0 = $arr[$p0]; $val1 = $arr[$p1]; $tp = 0;
			while(true) {
				if($val0 <= $val1) {
					$tmp[$tp++] = $val0;
					if(++$p0 === $l0) {
						break;
					}
					$val0 = $arr[$p0];
				} else {
					$tmp[$tp++] = $val1;
					if(++$p1 === $l1) {
						$tl = $l0 - $p0;
						$targetIndex = $l1 - $tl;
						$i1 = $tl;
						while(--$i1 >= 0) {
							$arr[$targetIndex + $i1] = $arr[$p0 + $i1];
						}
						break;
					}
					$val1 = $arr[$p1];
				}
			}
			{
				$i1 = $tp;
				while(--$i1 >= 0) {	$arr[$j + $i1] = $tmp[$i1]; }
			}
			$j = $l1;
		}
	}
}

define( "SIZE", 1000000 );
$start = microtime(true) * 1000;
$arr = array();
$_g = 0;
while($_g < SIZE) {
	$i = $_g++;
	$arr[$i] = SIZE - $i;
}
margeSort($arr);
$time = floor(microtime(true) * 1000 - $start);
echo $time."ms";
?>

Haxe/PHPがあまりにもかわいそうな結果だったので、PHPも書きました。こっちのがいくらかマシです。

結果

速い順です。

言語 時間[ms] C++との比較
C++ 37
C# 72 1.95倍
Java 74 2倍
Haxe/C++ 119 3.22倍
Haxe/JS [Node.js] 274 7.41倍
Haxe/C# 501 13.5倍
Haxe/AIR 744 24倍
Haxe/Java 1155 31.2倍
Haxe/Neko 4282 116倍
PHP 19853 537倍
Haxe/PHP 132853 3590倍

 やはりC++が高速。続いて、C#,Javaがほぼ同じ。ちょっと遅れてHaxe/C++。

 Haxe/C++とC#で結構な差があるように見えますが、先述のArray.Copy()でひっくり返りかねない差なので、C#,Java,Haxe/C++は同じくらいのレベルと考えていいと思います。個人的にC++はあまり使いたくないので、速さが必要なプログラムはこの3つのどれかで作ることになるでしょうか。

 Haxeはマージソートくらいだと他言語に簡単に移植できるのがいいですね。ついでなので他の言語への移植もしてみましたが、Haxe/JSはかなり健闘しました。Node.jsって遅いイメージがあったんですが、かなり高速らしいです。

 Haxe/AIRは、まあ普通という感じです。AS3で書いてもこのへんになる気がします。
 
 Haxe/C#、Haxe/Javaはベータ版ということもありまだ洗練されていないように見えます。これからに期待。
 
 Haxe/Nekoも遅いですね。Node.jsの方がはるかに速いです。
 
 PHPが遅いのは当然の結果ですね。C++の500倍遅かろうとPHPは良い言語ですし…。そもそも、PHPで速度が必要なプログラム書くこと自体が間違ってますし…。HTML生成できて、JSON出力できて、データベース操作できて、exec()でコマンド実行出来れば、それだけでPHPは価値があると思いますね。

 Haxe/PHPはさらに遅いです。PHPはただでさえ遅いのに配列に独自のクラスをつかってるので、無駄な関数呼び出しが発生してるようでした。Haxe/PHPはPHPの良い部分も死んでる(HTMLに直接書き込めない、1ページに1プロジェクト必要など)気がするので、自分が使う機会はなさそうです。

 ともあれ、Haxe/C++, Haxe/JS, Haxe/Flash(AIR)はかなり実用的だと思います。これだけできれば、モバイルでも、ブラウザでも、サーバーサイドでも、PCネイティブでも使えるので、プラットフォームと使える言語がマッチしなくて困ることはほぼ無さそうです。

 Haxeに興味を持った人はとりあえず公式サイト(http://haxe.org/?lang=jp)にどうぞ。

使った環境について

Windows 7 Home Premium 64-bit
Intel(R) Core(TM) i5-2430M
メモリ: 4G

Haxe 2.10, cygwin gcc 3.4.4(C++), .NET Framework 4.5(C#) 4.0(Haxe/C#), Visual Studio 2010(Haxe/C++), node.js 0.8.11, AIR 3.4, neko vm 1.8.2, php 5.3.10

Haxe vs TypeScript でベンチマーク対決

TypeScriptって速度はどうなの?速いの?ってことが気になったので、HaXeや素のJavaScriptと比較してみました。

比較にはAOBenchを使いました。

比較に使ったプログラム

HaXe版(HaXe2.10 @yoshihiro503さん移植https://github.com/yoshihiro503/aobench_haxe)
TypeScript版(https://github.com/shohei909/TypeScript_AOBench)
JSX版(JSX0.0.2 @kioku_systemkさん移植 http://kioku.sys-k.net/aobench_jsx/)
JavaScript版(http://kioku.sys-k.net/aobench_jsx/)

HaXe版はデッドコード削除を使用するにあたりちょっとだけ改変してあります。TypeScript版はHaXe版を元に書き変えて移植しました。

結果

HaXe TypeScript JSX(修正前) JSX(修正後) JavaScript
Google Chrome (Windows) [ms] 974 995 968 1125 1076
Firefox (Windows) 1544 3698 3766 1604 1649
Browser (Android) 63158 64636 61327 57338 70425
Safari (iOS) 54876 59792 57831 43200 70800
JavaScriptのファイルサイズ [Byte] 6010 8163 7963 17591 6968

HaXeは安定して高速ですね。ファイルサイズも小さくなっています。以前のHaXeはもうちょっと大きいファイルを出力してたんですが、HaXe2.10で強力なデッドコード削除最適化が実装されて、未使用なコードがJavaScriptファイルに反映されなくする–dead-code-eliminationオプションが強化されました。

一方、TypeScriptで出力されたJavaScriptは直訳っぽい素直なコードで読みやすいんですが、あんまり速くありません。基本的にはインライン展開が無い分の差だと思うんですが、気になるのは2倍以上の差がついたFirefoxの結果です。インライン展開されてるはずのJSXでも遅い結果になっています。
続きを読む