2008年12月16日火曜日

べき乗 pow() xy

ふとべき乗計算を考えてみた。

double pow( double x, int y ){
return y > 0 ? pow( x, y - 1 ) * x : 1;
}
整数の時は簡単。

実数になると
__declspec(noinline) double pow( double x, double y ){
__asm{
fld y
fld x
fyl2x
fst ST(1)
frndint
fxch ST(1)
fsub ST(0), ST(1)
f2xm1
fld1
faddp ST(1), ST(0)
fscale
}
}
なんかinline assemblerを使ってしまった。うん、何やってるのかさっぱりです。命令とそのときのスタック状態を表にすると
命令ST(0)ST(1)ST(2)
fld yy
fld xxy
fyl2xy×log2x
fst ST(1)y×log2xy×log2x
frndint⌊y×log2x⌋y×log2x
fxch ST(1)y×log2x⌊y×log2x⌋
fsub ST(0), ST(1)y×log2x-⌊y×log2x⌋⌊y×log2x⌋
f2xm12y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
fld112y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
faddp ST(1), ST(0)2y×log2x-⌊y×log2x⌋⌊y×log2x⌋
fscale2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋

ここで2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋ = 2y×log2x-⌊y×log2x⌋+⌊y×log2x⌋ = 2y×log2x = xyです。
はい、さっぱりわかりませんでした。

2008年12月3日水曜日

ファイルの高速コピー

Webサイトのコンテンツを更新するとき、ローカルディスクからWebサーバにアップロード(ファイルのコピー)をしていますが、ファイル数が多くてExplorerじゃとてもとても。
調べたところFastCopyとかコピーツールがありましたが、ふとC#で書いてみたらどうなるんだろう、と。

static void Copy( FileInfo source, FileInfo destination ) {
var src = new FileStream( source.FullName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, true );
var buffer = new byte[ source.Length ];
src.BeginRead( buffer, 0, buffer.Length, rar => {
src.EndRead( rar );
src.Close();
if( destination.Exists )
destination.Attributes &= ~FileAttributes.ReadOnly;
var dst = new FileStream( destination.FullName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true );
dst.BeginWrite( buffer, 0, buffer.Length, war => {
  dst.EndWrite( war );
dst.Close();
}, null );
}, null );
}
static void Copy( DirectoryInfo source, DirectoryInfo destination ) {
if( !destination.Exists )
destination.Create();
foreach( var src in source.GetDirectories() )
Copy( src, new DirectoryInfo( Path.Combine( destination.FullName, src.Name ) ) );
var srcfiles = source.GetFiles();
foreach( var dst in destination.GetFiles() )
if( !srcfiles.Any( src => String.Equals( src.Name, dst.Name, StringComparison.OrdinalIgnoreCase ) ) )
dst.Delete();
foreach( var src in srcfiles )
Copy( src, new FileInfo( Path.Combine( destination.FullName, src.Name ) ) );
}
static void Main( string[] args ) {
Copy( new DirectoryInfo( args[ 0 ] ), new DirectoryInfo( args[ 1 ] ) );
}

まずはお試しで、属性やタイムスタンプなどは保存しませんが。
で、コピー時間ですがFastCopyが740秒なのに対して、C#は200秒…。
何も考えずに並列でファイルオープンしているためハンドル数やバッファサイズなど問題点はいろいろあるとは思いますが、この所要時間の差はいったい…?!
ちなみにこのコード、途中に1回だけ30秒間ストールが発生しています。この問題を解決すれば更に30秒短くなるはず…。

2008年11月29日土曜日

PtrToStructureArray(): 配列のマーシャリング

Marshal.PtrToStructure()を使うと、メモリの内容を元に構造体などを復元することができます。これでC言語などとデータをやりとりできるようになります。
構造体の配列に関してもMarshalAs属性を使用すれば扱えますが、この場合、コンパイル時にサイズを決定した固定長配列に限られます。
そこで、実行時にサイズを決定できるPtrToStructureArray()を作ってみました。どれほど使い道があるやら。

public class Marshal{
static ModuleBuilder moduleBuilder;
static ModuleBuilder ModuleBuilder {
get {
if( moduleBuilder == null ) {
var name = new AssemblyName( "Sayuri.Dynamic" );
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly( name, AssemblyBuilderAccess.Run );
moduleBuilder = ab.DefineDynamicModule( name.Name );
}
return moduleBuilder;
}
}
public static T[] PtrToStructureArray<T>( IntPtr ptr, int size ) {
var type = typeof(T);
var typeName = string.Format( "{0}Array{1}", type.Name, size );
var arrayType = ModuleBuilder.GetType( typeName );
if( arrayType == null ){
var tb = ModuleBuilder.DefineType( typeName, TypeAttributes.SequentialLayout );
var fb = tb.DefineField( "array", type.MakeArrayType(), FieldAttributes.Public|FieldAttributes.HasFieldMarshal );
var ctype = typeof( MarshalAsAttribute );
var cb = new CustomAttributeBuilder( ctype.GetConstructor( new[] { typeof( UnmanagedType ) } ),
new object[] { UnmanagedType.ByValArray }, new[] { ctype.GetField( "SizeConst" ) }, new object[] { size } );
fb.SetCustomAttribute( cb );
arrayType = tb.CreateType();
}
var obj = SystemMarshal.PtrToStructure( ptr, arrayType );
var fi = arrayType.GetField( "array" );
return (T[])fi.GetValue( obj );
}
}
やってることはT型とサイズ20が与えられた場合に
[StructLayout(LayoutKind.Sequential)]
class TArray20{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
public T[] array;
}
というクラスをその場で生成して使っています。

2008年11月28日金曜日

IComparable<T>とその派生

以前、Dictionary<TKey, TValue>とIEquatable<T>で実行時エラーもなくハマる問題を書きましたが、今度は類似で実行時エラーでわかる問題がありました。

class Base: IComparable<Base>{
}
class Derived: Base{
}
とした場合、DerivedはIComparable<Base>を実装するため、通常の比較は行えます。しかしIComparable<Derived>は実装していないため、List<Derived>.Sort()などは行えません。

class Base<T>: IComparable<T> where T: Base{
}
class Derived: Base<Derived>{
}
とかやり出すか、素直にIComparableに切り替えるか。

2008年11月4日火曜日

swprintf()とwsprintf()

Cランタイムライブラリにsprintf()があります。同様の機能を持つwsprintf()がUSER32.dllにもあります。実はsprintf()はntdll.dllにもあります。

これらの関係を整理すると

宣言DLLTCHAR.HのルーチンANSI版Unicode版
tchar.hMSVCRT90.dll_stprintf()sprintf()swprintf()
windows.hUSER32.dllwsprintf()wsprintfA()wsprintfW()
-ntdll.dll-sprintf()swprintf()
となります。

ちなみに注意点がいくつか。
swprintf()の第2引数はバッファサイズで書式指定文字列は第3引数にずれています。他のものは第2引数が書式指定文字列です。代わりに他のものと互換のある_swprintf()も第2引数が書式指定文字列になっています。
wsprintf()は指定できる書式に制限があります。例えば浮動小数(%f)がサポートされていません。
ntdll.dllに含まれるsprintf()は使用方法が公開されてなく、サポートされている機能も公開されていません。ただし、全てのプロセスにロードされるDLLなので何かの役に立つかも。

2008年11月3日月曜日

CallNextHookEx()のHHOOK引数

SetWindowsHookEx( WH_CBT, CBTProc, ... )でDLL injectionしています。このCBTProc()のドキュメントによると、正確な型宣言は

LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam );
です。そしてこのhookプロシージャの中ではnCodeが負のときはCallNextHookEx()を呼び更にその戻り値を返すこととあります。

さてCallNextHookEx()のドキュメントによると、正確な型宣言は
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam );
です。ちなみに記述が矛盾していて、CallNextHookEx()の呼び出しはoptionalだが、呼び出すことを強く推奨するそうです。CBTProc()側では負の時呼べばいいのに、こう書かれてしまうと0または正の時にも呼ばざるを得なくなります。仕方がないので呼ぶことにしましょう。

問題はここからです。CallNextHookEx()の第一引数hhk。hookプロシージャは進入した各プロセスで実行されます。にもかかわらずCBTProc()には存在しないパラメータが要求されています。とても面倒くさいですが、SetWindowsHookEx()の戻り値を共有メモリに格納しておき、それを読み出して使っていました。

ところが今日ふと気がついたら、
Windows NT/XP/2003: Ignored.
などと書かれているではありませんか。うーん…今まで共有メモリにわざわざ準備していたhhkは無駄でしたか…。

2008年11月1日土曜日

ウィンドウプロシージャとメンバ関数その2

ウィンドウプロシージャとメンバ関数を書いてみたものの、1つのthreadに複数のWindow Procedureが混在する場合、うまく振り分けできませんね。
どうすればいいんだろう…? まぁ、複数Windowを扱ったことがないからいいけど…。