新規記事の投稿を行うことで、非表示にすることが可能です。
2023年03月15日
やっとグラフを描画するJavaScriptが動くようになった
pascal側の case文はcaseの中でbreakすれば外のループが抜けるが、そのまま switchに変換したら ループを抜けてくれない。 caseの外のループ文にラベルを置いて、break ラベルと変換しないといけないようだ。
変換ツールをそういうふうに対応させたが、多重になった時の動作が心配になり、結局出来るだけcase文でbreakはしないように元コードを直す事に。
でグラフ表示がJavaScript側でも出来るようになったので、Lazarus側で作ったZ変換から作った周波数特性数式を入れてみたが
低周波で0dBにならないという事は、数式処理の部分でミスってるようだ。
まだまだ周波数特性表示の完成は遠い
【このカテゴリーの最新記事】
-
no image
-
no image
-
no image
-
no image
-
no image
2023年03月11日
久しぶりに数式処理を書いた
Z変換で周波数特性を表示させるJavaScriptを書くためにPascalで先に書いて動くコードをJavaScriptに直す事にした。
数式処理はGIkoBasicをベースにAddSub/MulDivと再帰下降で書いていたのを手直ししていただけなので
正規表現で分解された要素が引数として与えられて処理する方式となるのは初めてのスタイルだ。
async/await を使えば再帰下降のままでも処理出来そうだが、Pascal側にその機能がないので先にコードが書けない。
四苦八苦してとりあえずpascal側である程度動作するコードは書けた。
Lazarusで正規表現を使う例とかで参考になるかもしれないので現時点でのコードを公開してる=>
z変換で周波数特性を得るjavascript作成奮戦記
これからこの千行付近あるコードをJavaScriptに直すのはちょっと頑張らないといけない
数式処理はGIkoBasicをベースにAddSub/MulDivと再帰下降で書いていたのを手直ししていただけなので
正規表現で分解された要素が引数として与えられて処理する方式となるのは初めてのスタイルだ。
async/await を使えば再帰下降のままでも処理出来そうだが、Pascal側にその機能がないので先にコードが書けない。
四苦八苦してとりあえずpascal側である程度動作するコードは書けた。
Lazarusで正規表現を使う例とかで参考になるかもしれないので現時点でのコードを公開してる=>
z変換で周波数特性を得るjavascript作成奮戦記
これからこの千行付近あるコードをJavaScriptに直すのはちょっと頑張らないといけない
タグ:正規表現
2023年03月05日
LazarusでprintfDebug
Lazarusにはデバッグコンソールがなく GUIアプリだとprintfデバッグが面倒です。
GUI画面が固まる前ならメモ帳でも張り付けて出力出来るのですけどね。
もちろんGUIからでも windows.AllocConsole;でコンソールは開くのですが
その場合 コンソールを手動で閉じてしまうとアプリも落ちてしまいます。
つい邪魔でデバッグ中にコンソールを閉じたつもりでアプリを終了させてデバッグ失敗をなくすために
コンソール窓の右上の[X]を無効にしてしまいます。
それは2か所からの公開ノウハウの組み合わせです。リンクを示しています。
リンク先に感謝を
var MyConsoleWhd: HWND = 0;
MyConsoleBuf:array [0..0] of AnsiChar;
procedure OpenConsole;
var
OldWindowTitle, NewWindowTitle: ansistring;
mHMENU: HMENU;
begin
AllocConsole(); //false=失敗するのは既に開いている時だけ
if MyConsoleWhd = 0 then //初めて呼ばれた時
begin
System.Assign(System.OutPut, 'CONOUT$');
System.ReWrite(System.OutPut);
System.SetTextBuf(System.OutPut,MyConsoleBuf); //バッファがある間は文字が出力されないので
// System.SetTextCodePage(System.OutPut ,GetOEMCP()) ; //これをしても効果ない
//https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/performance/obtain-console-window-handle
NewWindowTitle := format('OpenConsole%15.10f', [now]);
OldWindowTitle := '';
SetLength(OldWindowTitle, 512);
GetConsoleTitleA(@OldWindowTitle[1], 512 - 1); //タイトル文字を保存して
SetConsoleTitleA(pansichar(NewWindowTitle)); //適当なタイトルを付けて
Sleep(40);
MyConsoleWhd := FindWindowA(nil, pansichar(NewWindowTitle)); // 窓ハンドル得る
SetConsoleTitle(pansichar(OldWindowTitle)); //タイトル文字を復元
//https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/896conclosebtn/conclosebtn.html
// [閉じる]ボタンの無効化
if MyConsoleWhd <> 0 then
begin
mHMENU := GetSystemMenu(MyConsoleWhd, False);
RemoveMenu(mHMENU, SC_CLOSE, MF_BYCOMMAND);
end;
end;
end;
コンソールに出力する時、GUIから開いたコンソールではwriteは出来ません。
どうやら stdoutのファイル型 が初期化されていないようです。
上では'CONOUT$'でファイル変数を開くようにしています。(ただしコンソールアプリモードとは挙動が違うようです)
もともとLazarusはUTF8ですからそのままコンソールにwriteで出力すれば文字化けします。
そこで文字化けしない文字列用の出力コマンドの例を
procedure cPutS(s: ansistring);
var
rdSize: DWORD;
begin
OpenConsole; //コンソールのコードページも変更可能だが
// SetConsoleOutputCP(StringCodePage(s));//こうやってもUTF8は文字化けするので
SetCodePage(RawByteString(s),GetOEMCP() ,true); //文字列のコードページを変更する
//せっかく変更しても s+#13#10 と文字列の加算するだけでUTF8に戻るから2行に分ける
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
s := #13#10;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
//rdSizeは見てない。まあ出力出来なかったらどうしろというのか判らないし
end;
のような関数を別に用意してやります
GUI画面が固まる前ならメモ帳でも張り付けて出力出来るのですけどね。
もちろんGUIからでも windows.AllocConsole;でコンソールは開くのですが
その場合 コンソールを手動で閉じてしまうとアプリも落ちてしまいます。
つい邪魔でデバッグ中にコンソールを閉じたつもりでアプリを終了させてデバッグ失敗をなくすために
コンソール窓の右上の[X]を無効にしてしまいます。
それは2か所からの公開ノウハウの組み合わせです。リンクを示しています。
リンク先に感謝を
var MyConsoleWhd: HWND = 0;
MyConsoleBuf:array [0..0] of AnsiChar;
procedure OpenConsole;
var
OldWindowTitle, NewWindowTitle: ansistring;
mHMENU: HMENU;
begin
AllocConsole(); //false=失敗するのは既に開いている時だけ
if MyConsoleWhd = 0 then //初めて呼ばれた時
begin
System.Assign(System.OutPut, 'CONOUT$');
System.ReWrite(System.OutPut);
System.SetTextBuf(System.OutPut,MyConsoleBuf); //バッファがある間は文字が出力されないので
// System.SetTextCodePage(System.OutPut ,GetOEMCP()) ; //これをしても効果ない
//https://learn.microsoft.com/ja-jp/troubleshoot/windows-server/performance/obtain-console-window-handle
NewWindowTitle := format('OpenConsole%15.10f', [now]);
OldWindowTitle := '';
SetLength(OldWindowTitle, 512);
GetConsoleTitleA(@OldWindowTitle[1], 512 - 1); //タイトル文字を保存して
SetConsoleTitleA(pansichar(NewWindowTitle)); //適当なタイトルを付けて
Sleep(40);
MyConsoleWhd := FindWindowA(nil, pansichar(NewWindowTitle)); // 窓ハンドル得る
SetConsoleTitle(pansichar(OldWindowTitle)); //タイトル文字を復元
//https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/896conclosebtn/conclosebtn.html
// [閉じる]ボタンの無効化
if MyConsoleWhd <> 0 then
begin
mHMENU := GetSystemMenu(MyConsoleWhd, False);
RemoveMenu(mHMENU, SC_CLOSE, MF_BYCOMMAND);
end;
end;
end;
コンソールに出力する時、GUIから開いたコンソールではwriteは出来ません。
どうやら stdoutのファイル型 が初期化されていないようです。
上では'CONOUT$'でファイル変数を開くようにしています。(ただしコンソールアプリモードとは挙動が違うようです)
もともとLazarusはUTF8ですからそのままコンソールにwriteで出力すれば文字化けします。
そこで文字化けしない文字列用の出力コマンドの例を
procedure cPutS(s: ansistring);
var
rdSize: DWORD;
begin
OpenConsole; //コンソールのコードページも変更可能だが
// SetConsoleOutputCP(StringCodePage(s));//こうやってもUTF8は文字化けするので
SetCodePage(RawByteString(s),GetOEMCP() ,true); //文字列のコードページを変更する
//せっかく変更しても s+#13#10 と文字列の加算するだけでUTF8に戻るから2行に分ける
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
s := #13#10;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), @s[1], length(s), rdSize, nil);
//rdSizeは見てない。まあ出力出来なかったらどうしろというのか判らないし
end;
のような関数を別に用意してやります
2023年03月04日
JavaScriptを学んでゆこう
音の事を説明しようとすると周波数特性とかのグラフを見せないと伝わらない。
静止画ではなく係数がこう変化したらこうなるというグラフを見せたい。
といってwindowsのexeで作ってもウイルス扱いされてダウンロードしてもらえない。
いっぽうJavaScriptはダウンロードの必要もなく、誰にでも見てもらえます。
上の描画はJavaScriptで描いています。描くだけなら少しの知識で出来ます。
問題はGUI処理。
これを学んでゆきたいと考えています。
あ、上の歯車はサイクロイド曲線で描かれていて、4つの歯車で中央の隙間がどうなるのか見たかったのです。
このページで見えないようなら→
4つの歯車
違う歯数を設定したら噛み合ってないとか ひし形の配置とか、そのうち改善させたいと思っていますが未定
タグ:ロータリーエンジン
2023年03月03日
SIMD命令をDelphiでも動かす
Delphiといっても普段使ってるのはDelphi5です。XEあたりまでは飛び飛びのバージョンでも持っていますが、軽くて小さいexeが作れて使用許諾がオンラインでないので仮想環境でも面倒なく使えますからね。
AVX命令で10倍もの速度が出るのならDelphi5でも使ってみたくなります。
問題はDelphi5のアセンブラにはAVXどころかSSEもない事です。
そこでFPCのObjDump(GCCのと同趣旨のもの)を呼び出して アセンブラ命令を DB命令(バイトデータ)に置き換えて見事に使えてしまいました。
Lazarusで作ったSIMD関数をDelphiで使うのページです
このページには ObjDump.exeを呼び出して取り込むツールのソースが入っているので、
何かコンソールアプリ起動して出力を処理したい人は参考にして下さい。
AVX命令で10倍もの速度が出るのならDelphi5でも使ってみたくなります。
問題はDelphi5のアセンブラにはAVXどころかSSEもない事です。
そこでFPCのObjDump(GCCのと同趣旨のもの)を呼び出して アセンブラ命令を DB命令(バイトデータ)に置き換えて見事に使えてしまいました。
Lazarusで作ったSIMD関数をDelphiで使うのページです
このページには ObjDump.exeを呼び出して取り込むツールのソースが入っているので、
何かコンソールアプリ起動して出力を処理したい人は参考にして下さい。
2023年03月01日
32bitの方が64bitより高速で精度がよい
SIMDでD[i]:=D[i] + coff*S[i]を計算するのはだいたい終わって、
SIMDでΣai*biの計算を作ってる。
それで面白い事に気づいた。
SIMDを使わないLazarusの計算結果は32bitLazarusの方が早いのだ。しかもΣai*biに限れば Lazarusは単精度のままでも非常に精度がよい。
違いが出るのはWin64では浮動小数点にFPU(x87)を使わない事にある。
FPUを使うと変数は単精度でも内部での処理は常に80bitになる。
Win64ではSSE命令で処理するから単精度の積和は精度が悪い。
もっとも遅いのはLazarusのコンパイラがSSEを使っても並列化をしてくれない事にある。
結果、現在作成中のAVXのΣai*biの計算では1桁もの差が出る。
SIMDでΣai*biの計算を作ってる。
それで面白い事に気づいた。
SIMDを使わないLazarusの計算結果は32bitLazarusの方が早いのだ。しかもΣai*biに限れば Lazarusは単精度のままでも非常に精度がよい。
違いが出るのはWin64では浮動小数点にFPU(x87)を使わない事にある。
FPUを使うと変数は単精度でも内部での処理は常に80bitになる。
Win64ではSSE命令で処理するから単精度の積和は精度が悪い。
もっとも遅いのはLazarusのコンパイラがSSEを使っても並列化をしてくれない事にある。
結果、現在作成中のAVXのΣai*biの計算では1桁もの差が出る。
2023年02月27日
LazarusでRDTSCで処理時間を見てみた
SIMD命令で並列化コードを少し書いてみたが、実際にどれくらい速度が出るのか見てみたい。
そこでRDTSC命令を使ったライブラリを書いてみた。
単精度のFOR文でAVXの8並列とSSEの4並列を比較してみたが、AVXの方がSSEより遅い結果が出てしまった。
そこで計算する順番を入れ替えると倍以上も速度がAVXが早いという当然の結果が出た。
どうやら 256個のデータで比較したせいか 命令キャッシュに2回目の呼び出しが収まるせいで2回目の処理時間が短くなるようだ
そこでRDTSC命令を使ったライブラリを書いてみた。
単精度のFOR文でAVXの8並列とSSEの4並列を比較してみたが、AVXの方がSSEより遅い結果が出てしまった。
そこで計算する順番を入れ替えると倍以上も速度がAVXが早いという当然の結果が出た。
どうやら 256個のデータで比較したせいか 命令キャッシュに2回目の呼び出しが収まるせいで2回目の処理時間が短くなるようだ
2023年02月26日
win64のアライン16BYTE規制で思う事
64bitOSだから8byteアラインを強制されるなら仕方ないと思う
でも16byteアラインはどうなんだろう?
「SSEで128bit を高速に処理するには16byteアラインが必要」という主張は判るが
AVXで256bit AVX512で512bitだからそれらも処理するのだろうか?
画像処理とか専用のアプリ内だけで社内ルールでやればよい事のように思う。
そもそも現行のPCのRAMなどのバスは64bitだ。128bitアラインにして高速化するのはCPUのキャッシュ内にデータがある間だけ。キャッシュを超えるデーターを扱うとなれば、SSE命令使おうが無駄な話になる。
メモリを無駄に使えば、それだけキャッシュから外れるまでの余裕が短くなるだけではないか?
さらに内臓メモリの限界もある。それを超えれば仮想記憶でHDD/SSDとスワップするわけで、それが速度を決めてしまう。 だから速度を考えるのならメモリは少しでも節約すべきだろう。
昔 音声認識をするのに8bitの符号で対数的な重みを付け 加算や掛算をテーブル引きで行った事がある。
それでもそこそこの結果が得られた。 当時は掛け算さえ遅かった時代なので速度目的だった。
今ではむしろ遅くなる。
しかし今の人工知能のメモリ消費を考えると 案外8bitの対数的符号は速度面でも有利化もしれない。
メモリ消費が少なければそれだけキャッシュから外れる可能性も、そしてPCのメモリスワップが起きる可能性も小さく出来るのだから。
でも16byteアラインはどうなんだろう?
「SSEで128bit を高速に処理するには16byteアラインが必要」という主張は判るが
AVXで256bit AVX512で512bitだからそれらも処理するのだろうか?
画像処理とか専用のアプリ内だけで社内ルールでやればよい事のように思う。
そもそも現行のPCのRAMなどのバスは64bitだ。128bitアラインにして高速化するのはCPUのキャッシュ内にデータがある間だけ。キャッシュを超えるデーターを扱うとなれば、SSE命令使おうが無駄な話になる。
メモリを無駄に使えば、それだけキャッシュから外れるまでの余裕が短くなるだけではないか?
さらに内臓メモリの限界もある。それを超えれば仮想記憶でHDD/SSDとスワップするわけで、それが速度を決めてしまう。 だから速度を考えるのならメモリは少しでも節約すべきだろう。
昔 音声認識をするのに8bitの符号で対数的な重みを付け 加算や掛算をテーブル引きで行った事がある。
それでもそこそこの結果が得られた。 当時は掛け算さえ遅かった時代なので速度目的だった。
今ではむしろ遅くなる。
しかし今の人工知能のメモリ消費を考えると 案外8bitの対数的符号は速度面でも有利化もしれない。
メモリ消費が少なければそれだけキャッシュから外れる可能性も、そしてPCのメモリスワップが起きる可能性も小さく出来るのだから。
アセンブラを学ぶならLazarusが便利
ネイティブコンパイラで プログラムのデバッグをするとき、アセンブラが読めると便利です。
それが出来ないと、いわゆるPrintfデバッグ(Pascalだとwriteとか formatしてメモ帳に出力するとか)となります
アセンブラが読めるならブレークポイントを仕掛けてコンパイルがどうされたか見たり、レジスタの値を見たりと情報の幅が広がります。
アセンブラを学ぶのならLazarusは良い環境です。
asm文でアセンブラを埋め込め、そのアセンブラ文法もLinuxスタイルですが
Intelスタイルも{$ASMMODE intel} をasmの前に入れるだけで書けます。
アセンブラの文法は殆ど同じになりますが、コメントはPascal式 //より右か {この範囲} か (* この範囲 *)
アセンブラだけで何かプログラムするとなればPICとか1チップマイコンくらいになりますから、高級言語の中に埋め込むという方式で入るのがお勧めの理由です。
私もLazarusでWin64環境を始めたので四苦八苦です=>Lazarusでアセンブラを使う時の注意点
asm文を使わなければ出来ない事は色々あります。
・前回のようにCPUIDを読んで自分のPCのCPUの種類や機能を調べる
・パスカル言語にSAR 演算子が無い事の対策
・64bit以上の多倍長演算(アセンブラならフラグを使えるので)
・X*Y/X のように途中で桁溢れする計算の計算可能範囲を広げる
・コンパイラが使ってくれないSIMD命令の利用
もっとも、最近はスクリプト言語が隆盛なので意味がないのかもしれませんけどね。
それが出来ないと、いわゆるPrintfデバッグ(Pascalだとwriteとか formatしてメモ帳に出力するとか)となります
アセンブラが読めるならブレークポイントを仕掛けてコンパイルがどうされたか見たり、レジスタの値を見たりと情報の幅が広がります。
アセンブラを学ぶのならLazarusは良い環境です。
asm文でアセンブラを埋め込め、そのアセンブラ文法もLinuxスタイルですが
Intelスタイルも{$ASMMODE intel} をasmの前に入れるだけで書けます。
アセンブラの文法は殆ど同じになりますが、コメントはPascal式 //より右か {この範囲} か (* この範囲 *)
アセンブラだけで何かプログラムするとなればPICとか1チップマイコンくらいになりますから、高級言語の中に埋め込むという方式で入るのがお勧めの理由です。
私もLazarusでWin64環境を始めたので四苦八苦です=>Lazarusでアセンブラを使う時の注意点
asm文を使わなければ出来ない事は色々あります。
・前回のようにCPUIDを読んで自分のPCのCPUの種類や機能を調べる
・パスカル言語にSAR 演算子が無い事の対策
・64bit以上の多倍長演算(アセンブラならフラグを使えるので)
・X*Y/X のように途中で桁溢れする計算の計算可能範囲を広げる
・コンパイラが使ってくれないSIMD命令の利用
もっとも、最近はスクリプト言語が隆盛なので意味がないのかもしれませんけどね。
2023年02月25日
UTF8 ShiftJis EUC は先頭バイトだけで文字数が判ると知った
delphiを使ってからLazarusに移行する問題の1つが文字コードです。
(delphiはUTF16文字になってから移行しないままなので)
自作のツール類はShiftJISを想定しています。それらを移植しなけばいけません。
ちょっとしたコントロールも数値入力とかで四則演算出来るように作ったりしてる為です。
それで興味のなかった文字コードを調べていたわけですが
日本語で使われる UTF8 ShiftJis EUC は文字を順に読んで、そのも文字コードから1文字のバイト数が判るようになっていると知りました。
ShiftJis は当然最大2文字なので簡単 EUCは3バイトになる先行コードは1つだけ
そして UTF8も最初のバイトを見れば残り何バイトが必要かが判ります。
文字列処理といっても多くの場合、多バイト文字の中身は関係なく、多バイト文字である事が判れば十分です。
したがって、この3つは統一して処理出来そうです。
というような内容を文字列変換のページに追記しました
(delphiはUTF16文字になってから移行しないままなので)
自作のツール類はShiftJISを想定しています。それらを移植しなけばいけません。
ちょっとしたコントロールも数値入力とかで四則演算出来るように作ったりしてる為です。
それで興味のなかった文字コードを調べていたわけですが
日本語で使われる UTF8 ShiftJis EUC は文字を順に読んで、そのも文字コードから1文字のバイト数が判るようになっていると知りました。
ShiftJis は当然最大2文字なので簡単 EUCは3バイトになる先行コードは1つだけ
そして UTF8も最初のバイトを見れば残り何バイトが必要かが判ります。
文字列処理といっても多くの場合、多バイト文字の中身は関係なく、多バイト文字である事が判れば十分です。
したがって、この3つは統一して処理出来そうです。
ShiftJisは
OEMLeadBytes = [#$81..#$9F, #$E0..#$FC];
if c in OEMLeadBytes then 2byte else 1byte;
EUC-jpは
EUCLeadBytes = [#$8E,#$8F,#$A1..#$FE];
EUCLeadBytes3 = [#$8F]; //先頭文字が8Fなら合計3byte
if c in EUCLeadBytes then begin
if c in EUCLeadBytes3 then 3byte else 2byte;
end else 1byte
UTF8は
UTF8LeadBytes = [#$C2..#$DF, #$E0..#$EF, #$F0..#$F4];
UTF8LeadBytes2 = [#$C2..#$DF];
UTF8LeadBytes3 = [#$E0..#$EF];
UTF8LeadBytes4 = [#$F0..#$F4];
if c in UTF8LeadBytes then begin
if c in UTF8LeadBytes2 then 2byte
else
if c in UTF8LeadBytes3 then 3byte
else
if c in UTF8LeadBytes3 then 4byte;
end;
というような内容を文字列変換のページに追記しました