ページ 1 / 1
ファイルの読み込みが遅いのは?
Posted: 2008年6月13日(金) 16:43
by トモカズ
連続の質問で恐縮です。
以下のコードで実行は可能なのですが、読み取り速度が遅いのです。
ActiveBasicの場合だと、この程度のスピードなのでしょうか?
なお、読取ファイルは6560行、10列です。
(読取ファイルは、
http://homepage3.nifty.com/ae85fcmxs/re ... ab-22b.lzh の中の "table.dat" です)
もちろん、最新のパソコンであれば全然問題の無い速度ですが(~3秒程度で完了)
Pentium3位だと、1分程度待たされてしまいます。
なお、F-BASIC97(マイナーですね)で同じコードでコンパイルすると,F-BASIC版の方が5倍程度速いのです。 (因みにファイル読取以外は遥かにActiveBasicの方が速いのです)
何かコードの書き方が根本的に誤っているような気がいたします。
改善できるのでしょうか?
是非、ご教授の程、お願いいたします。
#N88BASIC
dim x,y
dim HEN%[6560,7] AS Char
dim OKE%[6560,1] AS Char
dim NO' AS Integer
Open "table.dat" For Input As #1
LOCATE 0,0:PRINT "ファイル読み込み中です。しばらくお待ち下さい"
DO
Input #1,NO ':NO =Val(A$) :PRINT NO;'"---";
y=x:x=Int(NO/6560*100):IF x<>y THEN LOCATE 0,1:PRINT x;"%"
Input #1,HEN%(NO,0)
Input #1,HEN%(NO,1)
Input #1,HEN%(NO,2)
Input #1,HEN%(NO,3)
Input #1,HEN%(NO,4)
Input #1,HEN%(NO,5)
Input #1,HEN%(NO,6)
Input #1,HEN%(NO,7)
Input #1,OKE%(NO,0)
Input #1,OKE%(NO,1)
Loop Until Eof(1) = -1
close #1
Posted: 2008年6月13日(金) 19:31
by konisi
InputやPrint等の文字列を扱うN88BASIC命令はString型を引数にとった気がします。
それと多次元配列は掛け算を使って実現しているので、頻繁に使うと多少重くなると思います。
気にならない程度だと思いますが。
Posted: 2008年6月13日(金) 19:56
by ゲスト
PⅢで動かしましたが、遅いですね。
N88系コマンドのファイル操作は、確かに遅いです。
>>改善できるのでしょうか?
はじめにファイルを全て読み込みます。
下記のコードで、ReadDataにファイルの内容が収録されます。
[ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]コード: 全て選択
#N88BASIC
Dim ReadData As BytePtr
Dim FileSize As DWord
Dim hFile As HANDLE
Dim RBytes As Long
hFile=CreateFile("table.dat",GENERIC_READ,0,ByVal 0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
FileSize=GetFileSize(hFile,NULL)
ReadData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,FileSize+1)
ReadFile(hFile,ReadData,FileSize,VarPtr(RBytes),ByVal 0)
CloseHandle(hFile)
この後で、カンマと改行を探しながら、配列にコピーすれば高速になると思います。
速くなりました!!
Posted: 2008年6月14日(土) 09:10
by トモカズ
ゲストさん、ありがとうございました。
劇的に速くなりました!!
実行速度を測定(ストップウォッチにて測定につき、正確ではありませんが)しましたが、以下の通りでした。
Pentium3 67.7秒かかっていたものが1.8秒 (約37倍)
Pentium4 18.4秒かかっていたものが1.5秒 (約12倍)
体感的にはP3もP4も変わりません。改善前は全然速度が違っていたのが不思議です。
もちろんCPU性能の要因の他にもディスク性能等あるのでしょうが、いずれにしても2秒以内なのでこれ未満のパソコンでも耐えられる処理速度になったと思います。
今回のファイル読取はメインプログラムを実行する前に1回だけの処理なので、ReadDat,FileSize,hFile ,RBytes 等の変数は以降使用しないのでメモリから開放したい(した方が良い?)のですが、いかがでしょうか?
ヘルプ見ましたが、"HeapFree"を使うのかなぁ?なんて思いましたが使用方法がわかりません。
度々恐縮ですが、ご教授の程よろしくお願いいたします。
Posted: 2008年6月14日(土) 09:38
by トモカズ
konisiさん、いつも気にかけてくださいましてありがとうございます。
多次元配列の件、実は私もそう思っていたところなのです。
高速化するには(今回のファイル読取とは別件ですが・・)1次元配列に直そうとも思うのですが、配列引数に対して掛算するのと、内部的に(?)掛算をしているのでは、どちらが速いのかなぁなんて考えておりました。
全てを1次元に直すのも結構大変だし、大変な割にはあまり(スピードの)コストダウンにあまりなってない事を想像すると躊躇してしまいます。
間違いなく速くはなるとは思うのですが・・・・
Posted: 2008年6月14日(土) 13:05
by konisi
時間の測定には、GetTickCount関数が使えると思います。
HeapFreeをじかに書くと読みにくいと思うので
コード: 全て選択
Function LoadFileData(FileName As *Byte) As *Byte
Dim ReadData As BytePtr
Dim FileSize As DWord
Dim hFile As HANDLE
Dim RBytes As Long
hFile=CreateFile(FileName,GENERIC_READ,0,ByVal 0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)
FileSize=GetFileSize(hFile,NULL)
ReadData=HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,FileSize+1)
ReadFile(hFile,ReadData,FileSize,VarPtr(RBytes),ByVal 0)
CloseHandle(hFile)
LoadFileData=ReadData
End Function
Sub CloseFileData(Data As *Byte)
HeapFree(GetProcessHeap(),0,Data)
End Sub
Dim ReadData As *Byte
'読み込む
ReadData=LoadFileData("table.dat")
'ここで内容を使う
'最終処理
CloseFileData(ReadData)
のように関数化すれば使いやすいかと思います
ところで自分で掛け算した場合と多次元配列を使った場合の違いについて調べてみたのですが、
出力結果 [ここをクリックすると内容が表示されます] [ここをクリックすると非表示にします]ソースコード
コード: 全て選択
Dim A[99,99] As Byte
Dim B[9999] As Byte
Dim i As Long
Dim j As Long
Dim k As Long
k=A[i,j]
k=B[i*100+j]
をコンパイルして出てきた実行ファイルを調べた結果
コード: 全て選択
k=A[i,j]の部分
XOR ECX,ECX
PUSH ECX
PUSH 0
PUSH j
POP EAX
IMUL EAX,EAX,1
ADD [ESP],EAX
PUSH i
POP EAX
IMUL EAX,EAX,100
ADD [ESP],EAX
POP EAX
IMUL EAX,EAX,1
POP ECX
ADD ECX,EAX
XOR EAX,EAX
MOV AL,[A+ECX]
PUSH EAX
POP EAX
AND EAX,255
PUSH EAX
POP EAX
MOV k,EAX
約46clock
k=B[i*100+j]の部分
XOR ECX,ECX
PUSH ECX
PUSH 0
PUSH i
PUSH 100
POP EBX
POP EAX
SUB ESP,4
IMUL EAX,EBX
MOV [ESP],EAX
PUSH j
POP EBX
POP EAX
SUB ESP,4
ADD EAX,EBX
MOV [ESP],EAX
POP EAX
IMUL EAX,EAX,1
ADD [ESP],EAX
POP EAX
IMUL EAX,EAX,1
POP ECX
ADD ECX,EAX
XOR EAX,EAX
MOV AL,[B+ECX]
PUSH EAX
POP EAX
AND EAX,255
PUSH EAX
POP EAX
MOV k,EAX
約50clock
ちなみに理想的な出力は
k=B[i*100+j]
を翻訳した時に
コード: 全て選択
MOV EAX,i
MOV EDX,j
LEA EAX,[EAX+EAX*4]
XOR ECX,ECX
LEA EAX,[EAX+EAX*4]
LEA EAX,[EDX+EAX*4]
MOV CL,[B+EAX]
MOV k,ECX
で約6clock 等。
どうやら掛け算を使うくらいなら多次元配列を使った方がいいようです
Posted: 2008年6月14日(土) 13:58
by ゲスト
>>しないのでメモリから開放したい(した方が良い?)のですが、いかがでしょうか?
サブルーチンにして、サブルーチン内で宣言すればローカル変数として自動的に開放されます。
こんな感じで、プログラムの最後(別のファイルを作ってプロジェクトに追加してもOK)において、
メインプログラムでは、
と書けばOKです。(Gosubに相当します。)
本当は引数を付けて構造化した方がお勧めですが、追々勉強してください。
Posted: 2008年6月14日(土) 19:01
by ゲスト
konisiさん
時間の測定・・・いい歳のおっさんがパソコンの前でストップウォッチで計測している姿がとても滑稽ですね。
GetTickCount関数ですね。次の機会にはそれを使ってみます。
HeapFreeの件、わざわざサンプルコードの作成までしていただきましてありがとうございます。
また、配列変数につきましても非常に参考になりました。
なるほど!コンパイルした実行ファイルを見るとそうなのですね。
逆アセンブルというヤツでしょうか?
うーん。私が機械語をやっていた時はZ80 & 6809なので雰囲気は理解できますが、(大昔の事なので忘れかけておりますし)難解です。
私の時代(20数年前)ではハンドアセンブルが主流(私だけだったかな?)紙にコードを書いてから、直接16進入力したものです。20年前にはさすがにソフトウェアによるアセンブラを使用しておりました。
ゲストさん
私もWin32プログラミング講座は目は通しているのですが、頭では理解したつもりでも構造体になじんでおりません。ついgosubを使ってしまうのです。
しかし、お陰様で配列のメモリ開放の件、理解出来ました。
sub(や構造化についても)の習慣を付け、少しづつ慣れていくしかなさそうです。
ありがとうございました。
Posted: 2008年6月14日(土) 19:03
by トモカズ
上記自分の名前を入力し忘れました。失礼いたしました