ファイル読込の速度比較          <TOP>


ファイルの読込速度(通常のテキストファイル読込と、ファイルマッピングを使った読込)を比較します。
GetFileTitle パスからファイル名を取得 
GetFileSize ファイルのサイズをバイト単位で取得 
CloseHandle オープンされているオブジェクトハンドルをクローズ 
CreateFile 指定したファイルをオープンし、デバイスハンドルを返す(ファイルサイズ取得用) 
CreateFileLong 指定したファイルをオープンし、デバイスハンドルを返す(マッピング読込用) 
CreateFileMapping ファイルマッピングオブジェクトを作成 
MapViewOfFile ファイルビューをマッピング 
OpenFileMapping 名前付きファイルマッピングオブジェクトをオープン 
UnmapViewOfFile ファイルのマップビューをアンマップ 
GetTickCount システムが起動してからの経過時間を取得 
MoveMemory メモリの指定領域をコピー 
 
Text1にドラッグ&ドロップ、または「ファイルを開く」でテキストファイルを選択します。それぞれの読込ボタンでその処理時間を取得しています。
 
'================================================================
'= ファイル読込の速度比較
'=    (OpenFileMapping.bas)
'================================================================
#include "Windows.bi"

#define FILE_MAP_COPY &H1               '上書きモード
#define FILE_MAP_WRITE &H2              '読み書きモード
#define FILE_MAP_READ &H4               '読み込み専用モード

#define PAGE_READONLY &H2               'コミット済みのページ領域に対する読み取りアクセスを許可
#define PAGE_READWRITE &H4              'コミット済みのページ領域に対する読み取りアクセスと書き込みアクセスの両方を許可
#define PAGE_WRITECOPY &H8              'コピー可能アクセス(hFileは、GENERIC_READ属性を持たなければならない)

#define GENERIC_READ -2147483648        '読み込みモード(&H80000000)
#define GENERIC_WRITE &H40000000        '書き込みモード

#define INVALID_HANDLE_VALUE -1         '見つからない場合

#define OPEN_EXISTING 3                 'ファイルをオープンする(存在しない場合失敗)
#define OPEN_ALWAYS 4                   'ファイルをオープンする(存在しない場合作成)
#define vbCrLf (Chr$(13) & Chr$(10))    'キャリッジリターンとラインフィード(\r\n)

' セキュリティを定義する構造体
Type SECURITY_ATTRIBUTES
    nLength As Long
    lpSecurityDescriptor As Long
    bInheritHandle As Long
End Type
 
' パスからファイル名を取得
Declare Function Api_GetFileTitle% Lib "comdlg32" Alias "GetFileTitleA" (ByVal lpszFile$, ByVal lpszTitle$, ByVal cbBuf%)

' ファイルのサイズをバイト単位で取得
Declare Function Api_GetFileSize& Lib "kernel32" Alias "GetFileSize" (ByVal hFile&, lpFileSizeHigh&)

' オープンされているオブジェクトハンドルをクローズ
Declare Function Api_CloseHandle& Lib "kernel32" Alias "CloseHandle" (ByVal hObject&)

' 指定したファイルをオープンし、デバイスハンドルを返す(ファイルサイズ取得用)
Declare Function Api_CreateFile& Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName$, ByVal dwDesiredAccess&, ByVal dwShareMode&, lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDistribution&, ByVal dwFlagsAttributes&, ByVal hTemplateFile&)

' 指定したファイルをオープンし、デバイスハンドルを返す(マッピング読込用)
Declare Function Api_CreateFileLong& Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName$, ByVal dwDesiredAccess&, ByVal dwShareMode&, ByVal lpSecurityAttributes&, ByVal dwCreationDisposition&, ByVal dwFlagsAndAttributes&, ByVal hTemplateFile&)

' ファイルマッピングオブジェクトを作成
Declare Function Api_CreateFileMapping& Lib "kernel32" Alias "CreateFileMappingA" (ByVal hFile&, ByVal lpFileMappigAttributes&, ByVal flProtect&, ByVal dwMaximumSizeHigh&, ByVal dwMaximumSizeLow&, ByVal lpName$)

' ファイルビューをマッピング
Declare Function Api_MapViewOfFile& Lib "kernel32" Alias "MapViewOfFile" (ByVal hFileMappingObject&, ByVal dwDesiredAccess&, ByVal dwFileOffsetHigh&, ByVal dwFileOffsetLow&, ByVal dwNumberOfBytesToMap&)

' 名前付きファイルマッピングオブジェクトをオープン
Declare Function Api_OpenFileMapping& Lib "kernel32" Alias "OpenFileMappingA" (ByVal dwDesiredAccess&, ByVal bInheritHandle&, ByVal lpName$)

' ファイルのマップビューをアンマップ
Declare Function Api_UnmapViewOfFile& Lib "kernel32" Alias "UnmapViewOfFile" (ByVal lpBaseAddress&)

' システムが起動してからの経過時間を取得
Declare Function Api_GetTickCount& Lib "kernel32" Alias "GetTickCount" ()

' メモリの指定領域をコピー
Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Dest As Any, ByVal Source&, ByVal length&)

Var Shared Edit1 As Object
Var Shared Text(2) As Object
Var Shared Button(2) As Object

Edit1.Attach GetDlgItem("Edit1") : Edit1.SetFontSize 12
For i = 0 To 2
    Text(i).Attach GetDlgItem("Text" & Trim$(Str$(i + 1))) : Text(i).SetFontSize 12
    Button(i).Attach GetDlgItem("Button" & Trim$(Str$(i + 1))) : Button(i).SetFontSize 12
Next

Var Shared PathName As String
Var Shared FileLength As Long
Var Shared sFile As Long

'================================================================
'= 
'================================================================
Declare Sub MainForm_Start edecl ()
Sub MainForm_Start()
    Edit1.EnableWindow 0
    Button(1).EnableWindow 0
    Button(2).EnableWindow 0
End Sub

'================================================================
'= ファイルマッピングを用いてファイルを高速に読み込む
'================================================================
Declare Function FileReadByFileMapping(hFileName As String) As String
Function FileReadByFileMapping(hFileName As String) As String
    Var hFileMap As Long
    Var hFile As Long
    Var hOpened As Long
    Var nAddress As Long
    Var buffer As String
    Var Buffer2 As String
    Var Ret As Long

    hFile = Api_CreateFileLong(hFileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)
    hFileMap = Api_CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, "test")
    If hFileMap = 0 Then Goto *ReadEnd

    hOpened = Api_OpenFileMapping(FILE_MAP_READ, 0, "test")
    If hOpened = 0 Then Goto *ReadEnd

    nAddress = Api_MapViewOfFile(hOpened, FILE_MAP_READ, 0, 0, 0)
    If nAddress = 0 Then Goto *ReadEnd

    buffer = String$(FileLength, Chr$(0))
    MoveMemory buffer, nAddress, FileLength
    Buffer2 = Left$(buffer, InStr(buffer, Chr$(0)))
    Ret = Api_UnmapViewOfFile(nAddress)

*ReadEnd
    Ret = Api_CloseHandle(hOpened)
    Ret = Api_CloseHandle(hFileMap)
    Ret = Api_CloseHandle(hFile)

    FileReadByFileMapping = Buffer2
End Function

'================================================================
'= ファイルサイズ取得
'================================================================
Declare Sub FileSizeGet edecl ()
Sub FileSizeGet()
    Var hFileName As Long
    Var sa As SECURITY_ATTRIBUTES
    Var FileSizeLow As Long
    Var FileSizeHigh As Long
    Var Ret As Long

    Edit1.SetWindowtext ""
    Text(1).SetWindowtext ""
    Text(2).SetWindowtext ""

    'セキュリティ構造体を初期化
    sa.nLength = Len(sa)

    'ファイルハンドル取得
    hFileName = Api_CreateFile(PathName, GENERIC_READ, 0, sa, OPEN_EXISTING, 0, 0)

    'ファイルハンドルの値が有効(見つかったとき)
    If hFileName <> INVALID_HANDLE_VALUE Then

        'ファイルサイズを取得
        FileSizeLow = Api_GetFileSize(hFileName, FileSizeHigh)

        'ファイルサイズの取得に失敗したときは
        If FileSizeLow = &HFFFFFFFF Then

            '拡張エラー情報を表示
            A% = MessageBox("", "取得に失敗しました。", 0, 2)
            Exit Sub 
        Else

            'ファイルサイズ
            FileLength = FileSizeLow + 1
        End If

        'ファイルをクローズ
        Ret = Api_CloseHandle(hFileName)

    '有効でないとき
    Else

        'エラーを表示
        A% = MessageBox("", "ファイルを開けませんでした。", 0, 2)
    End If
End Sub

'================================================================
'= シェルドロップされたファイル名を取得
'================================================================
Declare Sub Text1_DropFiles edecl (ByVal DF As Long)
Sub Text1_DropFiles(ByVal DF As Long)
    Var CN As Long

    Edit1.EnableWindow -1
    Button(1).EnableWindow -1
    Button(2).EnableWindow -1

    CN = GetDropFileCount(DF)
    PathName = GetDropFileName(DF, 0)
    Text(0).SetWindowText PathName

    'ファイルサイズ取得
    FileSizeGet
    Text(1).SetWindowText format$(FileLength - 1, "###,###,### byte ")
End Sub

'================================================================
'= ファイルを開く
'================================================================
Declare Sub Button1_on edecl ()
Sub Button1_on()
    Var Buffer As String
    Var Ret As Integer

    Edit1.SetWindowText ""
    Text(0).SetWindowText ""
    Text(1).SetWindowText ""
    Text(2).SetWindowText ""

    PathName = WinOpenDlg("ファイルのオープン","*.txt;*.csv;*.bas","テキストファイル(*.txt);csvファイル(*.csv);basファイル(*.bas)", 0)
    If PathName <> Chr$(&H1B) Then
        Edit1.EnableWindow -1
        Button(1).EnableWindow -1
        Button(2).EnableWindow -1

        Buffer = String$(255, 0)
        Ret = Api_GetFileTitle(PathName, Buffer, Len(Buffer))
        Buffer = Left$(Buffer, InStr(1, Buffer, Chr$(0)) - 1)
        Text(0).SetWindowText " " & Buffer

        'ファイルサイズ取得
        FileSizeGet
        Text(1).SetWindowText format$(FileLength - 1, "###,###,### byte ")
    Else
        Edit1.EnableWindow 0
        Button(1).EnableWindow 0
        Button(2).EnableWindow 0
    End If
End Sub

'================================================================
'= ファイルマッピングによる読込
'================================================================
Declare Sub Button2_on edecl ()
Sub Button2_on()
    Var t As Long
    Var buffer As String

    Edit1.SetWindowText ""
    Text(2).SetWindowText ""

    '計測開始
    SetMousePointer 2
    t = Api_GetTickCount

    buffer = FileReadByFileMapping(PathName)

    Edit1.SetWindowText buffer

    '計測終了
    SetMousePointer 0
    Text(2).SetWindowText " 読込表示処理時間:" & Str$((Api_GetTickCount - t) / 1000) & "秒"
End Sub

'================================================================
'= 通常の読込
'================================================================
Declare Sub Button3_on edecl ()
Sub Button3_on()
    Var ff As Integer
    Var t As Long
    Var txt As String
    Var temp As String

    Edit1.SetWindowText ""
    Text(2).SetWindowText ""

    '計測開始
    SetMousePointer 2
    t = Api_GetTickCount

    ff = FreeFile
    Open PathName For Input As #ff
    Do While Not Eof(#ff)
        Line Input #ff, temp
        txt = txt & temp & vbCrLf
    Loop
    Close #ff

    Edit1.SetWindowText txt

    '計測終了
    SetMousePointer 0
    Text(2).SetWindowText " 読込表示処理時間:" & Str$((Api_GetTickCount - t) / 1000) & "秒"
End Sub

'================================================================
'=
'================================================================
While 1
    WaitEvent
Wend
Stop
End