STRINGクラスのメリット
STRINGクラスのメリット
たかせです。
普段、なにげなく使用するSTRINGクラス。
CP2から導入されましたが。
一部分を除き4.24以前のバージョンと同様になにも気にせずに使用できます。
当然、内部動作に差異はありますが。
そこで質問です。
STRINGクラスのメリットってどんなものケースがありますか?
よろしくおねがいします。
ちなみに気づいたのですが、デメリットとして、
STRINGクラスのデータを定義すると個数によってEXEのサイズが大きくなるようです。
普段、なにげなく使用するSTRINGクラス。
CP2から導入されましたが。
一部分を除き4.24以前のバージョンと同様になにも気にせずに使用できます。
当然、内部動作に差異はありますが。
そこで質問です。
STRINGクラスのメリットってどんなものケースがありますか?
よろしくおねがいします。
ちなみに気づいたのですが、デメリットとして、
STRINGクラスのデータを定義すると個数によってEXEのサイズが大きくなるようです。
Re: STRINGクラスのメリット
> 普段、なにげなく使用するSTRINGクラス。
やっと最近なにげなく使用するようになりました。
> そこで質問です。
> STRINGクラスのメリットってどんなものケースがありますか?
自分が感じた限りでは、開発する際の負担が軽減されたことでしょうか?
Insert関数やRemove関数などと同じ機能をもった関数を自分で用意して開発してたのが、「公式」という一応の安全を確保された物として提供してくださるので、なんとな~く安心して使えます。
> ちなみに気づいたのですが、デメリットとして、
> STRINGクラスのデータを定義すると個数によってEXEのサイズが大きくなるようです。
こういったことの逆でメリットを挙げるとするならとくに気付かないんですけど...。
とりあえず、文字列操作に関する細かい作業を気にすることなくStringクラスを使うだけで作業が済んでしまうという効率の良さがメリットでしょうか?。
やっと最近なにげなく使用するようになりました。
> そこで質問です。
> STRINGクラスのメリットってどんなものケースがありますか?
自分が感じた限りでは、開発する際の負担が軽減されたことでしょうか?
Insert関数やRemove関数などと同じ機能をもった関数を自分で用意して開発してたのが、「公式」という一応の安全を確保された物として提供してくださるので、なんとな~く安心して使えます。
> ちなみに気づいたのですが、デメリットとして、
> STRINGクラスのデータを定義すると個数によってEXEのサイズが大きくなるようです。
こういったことの逆でメリットを挙げるとするならとくに気付かないんですけど...。
とりあえず、文字列操作に関する細かい作業を気にすることなくStringクラスを使うだけで作業が済んでしまうという効率の良さがメリットでしょうか?。
配列変数定義の上限値について
String型がまだクラスで無かった頃,Stringの配列型の変数を宣言すると,さして大きくない要素数でもコンパイラがエラーになってしまうという現象がありました。
クラスならNewが使えるのでこの問題は回避できるだろうと当時の私は主張していますね。
#汎用性という観点からすればクラス型でなくてもNew/Deleteが使えるほうが良いと私は思いますけどね。
String型がまだクラスで無かった頃,Stringの配列型の変数を宣言すると,さして大きくない要素数でもコンパイラがエラーになってしまうという現象がありました。
クラスならNewが使えるのでこの問題は回避できるだろうと当時の私は主張していますね。
#汎用性という観点からすればクラス型でなくてもNew/Deleteが使えるほうが良いと私は思いますけどね。
やはり、文字列操作が簡単になる等、開発の負担が減るということが一番のメリットだと思います。
あとは、他のライブラリも整ってきたときに、Stringクラスの価値が再確認できると思います。具体的には、Array(配列)クラスなどを使って、Stringクラスの配列を容易に扱えるようになったりすることです。
あとは、他のライブラリも整ってきたときに、Stringクラスの価値が再確認できると思います。具体的には、Array(配列)クラスなどを使って、Stringクラスの配列を容易に扱えるようになったりすることです。
気にされる方はそうかもしれませんが、私は全然気にしていません。今後、ActiveBasicのexeサイズは増加することが予想されますので、気にする方はちょっと残念ですね。しかし、今よりも格段に開発が楽になると思います。ちなみに気づいたのですが、デメリットとして、
STRINGクラスのデータを定義すると個数によってEXEのサイズが大きくなるようです。
コード: 全て選択
Dim A(100000) As String
コンパイルの速度も少し落ちたような気がします。
調べてみると、AB5系では次のようなコードが生成されています。
コード: 全て選択
XOR ECX,ECX
PUSH ECX
PUSH 00h
PUSH 00h
POP EAX
IMUL EAX,EAX,00000001h
ADD dword ptr[ESP],EAX
POP EAX
IMUL EAX,EAX,00000008h
POP ECX
ADD ECX,EAX
LEA EAX,[ECX+C6C724h]
MOV ECX,EAX
PUSH ECX
CALL _A
せめてこう書き直したほうが良いのではないかという案。
コード: 全て選択
PUSH 0C6C724h
CALL _A
自動化のほう、どうなっているんでしょうか山本さん。
追記:実行箇所がどこに飛ばされているかと言う点と、それの最適化案について。
コード: 全て選択
;生成されるコード
_A:
SUB ESP,00000000h
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI
PUSH 01h
CALL _B
PUSH EAX
MOV ECX,dword ptr[EBP+08h]
ADD ECX,00000004h
POP EAX
MOV dword ptr[ECX],EAX
PUSH 00h
MOV ECX,dword ptr[EBP+08h]
POP EAX
MOV dword ptr[ECX],EAX
POP EDI
POP ESI
POP EBX
MOV ESP,EBP
POP EBP
ADD ESP,+00h
RET 0004h
_B:
SUB ESP,00000004h
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI
PUSH 00h
PUSH 04h
MOV EAX,EBP
ADD EAX,00000004h
PUSH EAX
CALL dword ptr[KERNEL32.RtlFillMemory]
PUSH dword ptr[EBP+0Ch]
PUSH 08h
PUSH dword ptr[offset D]
CALL dword ptr[KERNEL32.HeapAlloc]
PUSH EAX
POP EAX
MOV dword ptr[EBP+04h],EAX
JMP _C
_C:
MOV EAX,dword ptr[EBP+04h]
POP EDI
POP ESI
POP EBX
MOV ESP,EBP
POP EBP
ADD ESP,+04h
RET 0004h
コード: 全て選択
;最適化してみたコード
_A:
SUB ESP,00000000h
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI;ここまでは関数の定石なのでここは削らない。ただし残す必要性は薄い。以下略。
;EAX,ECXは_B先でも現在の値が参照されないので初期化はしない。
PUSH 01h
CALL _B
MOV ECX,dword ptr[EBP+08h]
MOV dword ptr[ECX+04h],EAX
XOR EAX,EAX
MOV ECX,dword ptr[EBP+08h]
MOV dword ptr[ECX],EAX
POP EDI;ここから関数の定石なので削らない。
POP ESI
POP EBX
MOV ESP,EBP
POP EBP
ADD ESP,+00h
RET 0004h
_B:
SUB ESP,00000004h
PUSH EBP
MOV EBP,ESP
PUSH EBX
PUSH ESI
PUSH EDI;ここまで関数の定石なので削らない。
PUSH 00h
PUSH 04h
MOV EAX,EBP
ADD EAX,00000004h
PUSH EAX
CALL dword ptr[KERNEL32.RtlFillMemory]
PUSH dword ptr[EBP+0Ch]
PUSH 08h
PUSH dword ptr[offset D]
CALL dword ptr[KERNEL32.HeapAlloc]
MOV dword ptr[EBP+04h],EAX
POP EDI;ここから関数の定石なので削らない。
POP ESI
POP EBX
MOV ESP,EBP
POP EBP
ADD ESP,+04h
RET 0004h
最後に編集したユーザー konisi [ 2007年1月03日(水) 23:16 ], 累計 2 回
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
私は、言語より下の知識がほとんどないに等しいので、あまり偉そうなことは言えないのですが、このサンプルはちょっと特異し過ぎてるのではないでしょうか?一般的に、このようなコードを書くことは少ないと思います。たぶん、大容量データを扱いたい場合、静的ではなく、動的に確保するのが一般的かと思われます。というコードだけでファイルサイズが8.42MBって言うのは少し大きすぎやしないでしょうか^^;コード: 全て選択
Dim A(100000) As String
コード: 全て選択
Dim s As *String
s = New[100000] String
動的に確保すると言う事をあまりやらないkonisiです。
なるほど、そう書けばサイズが小さくなるんですか。ありがとうございます。
なるほど、そう書けばサイズが小さくなるんですか。ありがとうございます。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
実際に見てもらえば分かると思うのですが、String型を静的に10万個確保したファイルのうち、約82%が上に挙げたような機械語でした。
静的確保・動的確保云々以前の問題として、クラスの配列の総当り的初期化で多くの機械語が生成されていると判断します。
ループで初期化するように書き直したほうが良いのでは?
静的確保・動的確保云々以前の問題として、クラスの配列の総当り的初期化で多くの機械語が生成されていると判断します。
ループで初期化するように書き直したほうが良いのでは?
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
私はよくわからないのですが、修正した方がいいのだと思います。しかし、このレベルの最適化は後からでもいいと思うのですが、どうなんでしょうか?山本さん一人だけでは限界がありますから、今優先すべきタスクから消化していくべきだと思います。静的確保・動的確保云々以前の問題として、クラスの配列の総当り的初期化で多くの機械語が生成されていると判断します。
ループするように書き直したほうが良いのでは?
私の感覚だと、最適化のタスクの優先順位はかなり低い方です。ただ、これは人それぞれで変わりますし、バランスも重要です。結局最終的にタスクをこなされるのは山本さんなので、私の主観はあまり関係ないですけどね。あと、この修正に時間を要さないのであれば、こんなこと話すのも無駄でしょうが。
とありますが、先ほどの投稿はこの意も含めての投稿だったというわけです。最初に言ったように、言語より下のことはわかりませんので、後はkonisiさんに任せます。静的確保・動的確保云々以前の問題として
ABで言うと、今現在code1のようになってるからcode2のようにした方がファイルサイズレベルで効率的ではないのか? という話です。
code1
code2
理解してもらえれば嬉しい。
又、すぐにでも書き直せるかと言われれば、newでの初期化がファイルサイズから見ておそらくcode2のようになっているので、それをコピーして少し改変する程度だと思っていますが・・・。
code1
コード: 全て選択
Dim A(100) As Long
A(0)=0
A(1)=0
A(2)=0
' ・・・(省略
A(100)=0
コード: 全て選択
Dim A(100) As Long,i As Long
For i=0 To 100
A(i)=0
Next i
又、すぐにでも書き直せるかと言われれば、newでの初期化がファイルサイズから見ておそらくcode2のようになっているので、それをコピーして少し改変する程度だと思っていますが・・・。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ちょいと横道へ。 [ここをクリックすると内容が表示されます]
順番が逆になってしまったのですが、こちらが本題です。To OverTakerさん [ここをクリックすると内容が表示されます]
というわけで、特異というよりもむしろ当然ですし、そもそも String クラスの優位性という論点からは大きく外れます。
こんな横道レス書いて言うのも何ですが。
僕の静的/動的の使用区分ポリシーは明瞭で、「動的に確保しなければならない場合は動的に確保する」です。・・・当たり前ですね。
言い換えると「静的に確保できる場合は必ず静的に確保する」ことになるわけで、これはたとえデータ構造の大きさが膨大でもコンパイル時にあらかじめサイズが決定される場合は静的に扱うということです。
そうすることでメモリ管理関数による無用なコストを削減できます。
一般に、巨大な配列構造を定義すればその初期データで実行ファイルは膨らみますね。OverTaker さんが書きました:このサンプルはちょっと特異し過ぎてるのではないでしょうか?
というわけで、特異というよりもむしろ当然ですし、そもそも String クラスの優位性という論点からは大きく外れます。
こんな横道レス書いて言うのも何ですが。
んー、そういう方もおられるのですか。OverTaker さんが書きました:たぶん、大容量データを扱いたい場合、静的ではなく、動的に確保するのが一般的かと思われます。
僕の静的/動的の使用区分ポリシーは明瞭で、「動的に確保しなければならない場合は動的に確保する」です。・・・当たり前ですね。
言い換えると「静的に確保できる場合は必ず静的に確保する」ことになるわけで、これはたとえデータ構造の大きさが膨大でもコンパイル時にあらかじめサイズが決定される場合は静的に扱うということです。
そうすることでメモリ管理関数による無用なコストを削減できます。
To イグトランスさん [ここをクリックすると内容が表示されます]
BSS セクションは、たいていの場合ヒープ領域として扱われます。
また、グローバル変数や静的変数の割り当て領域でもあります。
さて、ローカル変数ならどうでしょうか。この方法でうまく解決できるでしょうか。
おや、御明察。ただし、konisiさんの観察によると、内容ではなく処理のようです。イグトランス さんが書きました:変数の初期化内容として丸ごと実行ファイル内に抱え込んでいると考えれば納得がいきます。
そうですね。しかし BSS セクションの用途を少し考えてみてください。イグトランス さんが書きました:このString型配列変数の領域にBSSセクション(実行開始時に0初期化されるメモリ領域で,実行ファイル内に初期化内容は含まれない)を活用してもらえれば,ファイルの大きさは縮むことが予想できます。
BSS セクションは、たいていの場合ヒープ領域として扱われます。
また、グローバル変数や静的変数の割り当て領域でもあります。
さて、ローカル変数ならどうでしょうか。この方法でうまく解決できるでしょうか。
To konisiさん [ここをクリックすると内容が表示されます]
と、大それたフォローを試みる :-D
ちなみに、初期化はコンストラクタが担当します。
ということは、ある意味でコンパイラは初期化に関知しません。
P.S.
初期化に imul 命令が使われている謎
ヒント:ループアンローリング最適化konisi さんが書きました:静的確保・動的確保云々以前の問題として、クラスの配列の総当り的初期化で多くの機械語が生成されていると判断します。
ループで初期化するように書き直したほうが良いのでは?
と、大それたフォローを試みる :-D
というか、new で動的にオブジェクトを生成する(= コンパイル時にオブジェクトの数が決定できない)のに、特定数の初期化ルーチンを並べることは不可能です。konisi さんが書きました:newでの初期化がファイルサイズから見ておそらくcode2のようになっている
ちなみに、初期化はコンストラクタが担当します。
ということは、ある意味でコンパイラは初期化に関知しません。
P.S.
初期化に imul 命令が使われている謎
String クラスのメリットについてはいくつか挙がっておりますが、デメリットについて(サイズ以外に)触れられていないので、僕の思いつく分だけこちらで。
- String オブジェクトをコピーすると、多くの場所で文字列のコピーと生成/解放が発生する [ここをクリックすると内容が表示されます]大きなデータ構造は、その実体をコピーするコストを省くべくポインタでデータをやり取りする慣習が昔からあります。
これが結構強力で、このテクニックを適切に活用できる場面も(文字列処理では特に)非常に多いのですが、String クラスではこれができません。(堅牢性の面からするべきではない。)
変数への代入、引数の値渡し、戻り値の返却など、たいへん身近で気を緩めると見落としがちな場所で高コストな文字列転送が発生しがちです。たとえばこの短いサンプルの中では、それぞれ 4 回のコピー、オブジェクト生成、および解放が発生しています。探してみましょう。 [ここをクリックすると内容が表示されます]コード: 全て選択
Dim s As String, t As String s = "hogehoge" ' 非常にわざとらしいサンプルでごめんなさいです。 ' でも、わざとらしいのにはちゃんとした理由があります。 t = DuplicateString(s) Function DuplicateString(str As String) As String DuplicateString = str End Function
- バッファ用途としての応用性が現状では狭い [ここをクリックすると内容が表示されます]ある関数の出力として文字列を受け取りたいが、事前に結果の長さを知ることができないということは、よくあることです。
たとえば Windows API を使っていれば、避けては通れない問題といっても言い過ぎではないでしょう。
この場合、呼び出す側である程度余裕をもってメモリ領域を確保しておき、関数にはその領域に文字列を格納してもらうという手段が多く採られます。
しかし、この手法には潜在的にバッファオーバーフローの危険性が潜んでいるので、慎重に対処しなければなりません。
こういうしょーもないところにプログラマが気を散らすのはどうかと思います。これを String クラスでカバーしたいのですが、どうにかならないものでしょうか。
String クラスをバッファとして扱えるようにすることが、ライブラリ開発者としての僕の最近のテーマです。
解決策がひらめき次第クラスを改良していきますので、アイデアなどがあれば気軽にお知らせください。
最初の方だけしか逆アセンブルの結果を見てなかったのでimulの存在を消去してしまっていたのですが、よく調べると二つの即値Pushが配列の番号を示しているようで、そこからアドレスを計算するために整数積算が使われている事が判明しました。
Website→http://web1.nazca.co.jp/himajinn13sei/top.html
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
ここ以外の場所では「暇人13世」というHNを主として使用。
に署名を書き換えて欲しいと言われたので暇だしやってみるテスト。
この件に関してですが、実は結構前に大丈夫なんじゃないかなーと、考えていました。というのも、ライブラリが完成してしまえば、そもそも直にAPIで操作することがなくなるのではないかと考えているからです。tak さんが書きました:バッファ用途としての応用性が現状では狭い
ただ、現状を見てまだライブラリが完成していませんし、APIを使おうとすると、やはりこの問題が出てくるので、何か対処できるのであれば、対処したいですね。
横道の話について [ここをクリックすると内容が表示されます]
たしかに、一日経ってみると、かなり横道にそらしていることに気がつきました。すみませんでした。tak さんが書きました:ちょいと横道へ。
ごめんなさい。文章書くのに夢中になっているうちに、いつの間にか自分の考えを一般化していました。OverTaker さんが書きました:たぶん、大容量データを扱いたい場合、静的ではなく、動的に確保するのが一般的かと思われます。