-
Notifications
You must be signed in to change notification settings - Fork 175
ファイル読み込み高速化(行データ読み出しのマルチスレッド対応) #2021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ファイル読み込み高速化(行データ読み出しのマルチスレッド対応) #2021
Conversation
SetTypeByStringForFileの引数nDataLenがオーバーフローしていたため。 関連する処理を含めてintをsize_tに変更する。
@@ -105,11 +103,14 @@ class CFileLoad | |||
/* メンバオブジェクト */ | |||
const SEncodingConfig* m_pEencoding; | |||
|
|||
//! 文字コード自動検出のために読み込む最大サイズ(byte) | |||
static constexpr LONGLONG m_nAutoDetectReadLen = 32768LL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
挙動が変化しないよう旧 gm_nBufSizeDef
と同値としました。
バッファにデータを読み込む | ||
@note エラー時は throw する | ||
*/ | ||
void CFileLoad::Buffering( void ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
メモリマップドファイル化により逐次の読み込みが不要になったため廃止
✅ Build sakura 1.0.4461 completed (commit 0a57687cf0 by @suconbu) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
対応ありがとうございます。基本方針賛同です。
sakura_core/CEol.cpp
Outdated
{ "\x0d\x00", "\x00\x0d", 2U }, | ||
{ "\x85\x00", "\x00\x85", 2U }, | ||
{ "\x28\x20", "\x20\x28", 2U }, | ||
{ "\x29\x20", "\x20\x29", 2U }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘ではないですが、変更する必要の「ある/なし」で言うと「なし」なので微妙です。
エンディアン変換をするための構造体だと思いますが、もう少しスマートな方法でやれないものか、とか思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int -> size_t (符号なし) に変更したので整数リテラルの符号有無を合わせてますが、変えなくてもwarningは出ないようなのでそのままでも良さそうです。
bool StartsWith(const WCHAR* pData, int nLen) const{ return m_nLen<=nLen && 0==wmemcmp(pData,m_szDataW,m_nLen); } | ||
bool StartsWith(const ACHAR* pData, int nLen) const{ return m_nLen<=nLen && m_szDataA[0] != '\0' && 0==memcmp(pData,m_szDataA,m_nLen); } | ||
bool StartsWith(const WCHAR* pData, size_t nLen) const{ return m_nLen<=nLen && 0==wmemcmp(pData,m_szDataW,m_nLen); } | ||
bool StartsWith(const ACHAR* pData, size_t nLen) const{ return m_nLen<=nLen && m_szDataA[0] != '\0' && 0==memcmp(pData,m_szDataA,m_nLen); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘ではないですが、 util/string_ex.h
に似たような定義がたくさんあるので、そちらで集中管理してもいいんじゃないかと思いました。
@@ -33,6 +33,8 @@ | |||
#include "util/window.h" | |||
#include "CSelectLang.h" | |||
#include "String_define.h" | |||
#include <atomic> | |||
#include <future> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘ではないですが、標準c++のヘッダーファイルは StdAfx.h
でまとめてインクルードする感じにできませんかね。
たぶん趣味の領域なのでどちらでもいいです。
sakura_core/CReadManager.cpp
Outdated
@@ -84,7 +86,7 @@ EConvertResult CReadManager::ReadFile_To_CDocLineMgr( | |||
EConvertResult eRet = RESULT_COMPLETE; | |||
|
|||
try{ | |||
CFileLoad cfl(type->m_encoding); | |||
CFileLoad cFileLoad( type->m_encoding ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
何故変数名を変える? のありがち指摘を入れときます。
理由があるなら教えてください。
理由のない変更は(以下省略。)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
戻しマス
sakura_core/CReadManager.cpp
Outdated
pFileInfo->SetBomExist( bBom ); | ||
|
||
/* ファイル時刻の取得 */ | ||
FILETIME FileTime; | ||
if( cfl.GetFileTime( NULL, NULL, &FileTime ) ){ | ||
if( cFileLoad.GetFileTime( NULL, NULL, &FileTime ) ){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
影響が大きいので黙ってましたが、 NULL
を nullptr
で re-define する変更を revert したいと思っています。
理由は「 gdiplus.h
をインクルードしたときエラーになるから」です。
個人的には gdiplus 嫌いなんですけど、ダークモード対応で TortoiseGit のコードの一部を流用しようとしたときに問題になるので、いつか対応入れると思います。
このあたり踏まえて、触る行に含まれる NULL
は nullptr に置換しときたいです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
関数内で混在になると気持ち悪いので今回は NULL のままで行かせてもらおうと思います。
忘れてしまいそうなのでコーディング時のガイドライン(?)的なものをWikiにこしらえるのがよさそうです。
(標準ヘッダは StdAfx.h でまとめてインクルード、の件等と合わせて)
sakura_core/CReadManager.cpp
Outdated
std::vector<std::future<EConvertResult>> vecWorkerFutures; | ||
std::atomic<bool> bCanceled = false; | ||
|
||
size_t nOffsetBegin = cFileLoad.GetNextLineOffset( (size_t)cFileLoad.GetFileSize() ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘じゃないですが (size_t)
に違和感。
キャストが必要なのは縮小変換する場合だけのはず。
cFileLoad.GetFileSize()
の関数名で、返すものがファイルサイズじゃないとすると問題。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
符号有無が変化することと、32bitビルド時には縮小変換になるため明示的にキャストしてます。
(LONGLONG:8バイト符号あり、size_t:8/4バイト符号なし)
※32bitビルド時は縮小変換になってしまいますが、CFileLoad::IsLoadableSize
の判定にて事前にファイルサイズが2GiB未満に制限されているのでオーバーフローの危険なしと判断
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
理屈は分かりました。
~ここから別な話~
・32bitビルドでも問題ないコードであること、を重要視したくないです。
理由: 32bit Windows は 2025年10月14日 にサポート終了となるため。
・話からするとファイルサイズと「次行の開始オフセット」は ULONGLONG とするべきです。
他のエディタと合わせたり、システムサービスの恩恵を受けるために、一度に読み込むテキストの総量は LONG に収まるサイズとすべきですが、「全体サイズが 2GB を超えない」は実装の都合なので、その気になれば制約を回避できる感じにしといたほうがいい気がします。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
他のエディタと合わせたり、システムサービスの恩恵を受けるために、一度に読み込むテキストの総量は LONG に収まるサイズとすべきですが、「全体サイズが 2GB を超えない」は実装の都合なので、その気になれば制約を回避できる感じにしといたほうがいい気がします。
64bitビルドについて言えば、2GBの制限はされず※、また size_t は8バイト符号なし (ULONGLONG と同じ) となるので上記については問題ないと思います。
※
sakura/sakura_core/io/CFileLoad.cpp
Lines 65 to 76 in a8bf87b
ULONGLONG CFileLoad::GetLimitSize() | |
{ | |
#ifdef _WIN64 | |
// 64bit の場合 | |
// 実質上限は設けないこととする (x64 対応しながらここは要検討) | |
return ULLONG_MAX; | |
#else | |
// 32bit の場合 | |
// だいたい 2GB くらいを上限とする (既存コードがそうなっていたのでそれを踏襲) | |
return 0x80000000; | |
#endif | |
} |
//! 元のCDocLineMgrの内容は空になる | ||
void CDocLineMgr::AppendAsMove( CDocLineMgr& other ) | ||
{ | ||
if( other.GetDocLineTop() == nullptr ){ return; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘じゃないです。ほほ趣味ですが、こう書いても同じ意味。
if( other.GetDocLineTop() == nullptr ){ return; } | |
if (!other.GetDocLineTop()) return; // otherが空なら抜ける |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetDocLineTop() がもしbool型なら GetDocLineTop() == false
とせず !GetDocLineTop()
とするところですが、ポインタ型なので比較演算子にしました。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
どっちでもいいみたいです。
誤って代入するのを防止する目的で、
定数を左辺に書く
みたいな文化もあります。
if ( nullptr == xxx() ){
sakura_core/io/CFileLoad.h
Outdated
// LPWSTR m_pszFileName; // ファイル名 | ||
HANDLE m_hFile; // ファイルハンドル | ||
HANDLE m_hFileMapping; // メモリマップドファイルハンドル |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HANDLE m_hFileMapping; // メモリマップドファイルハンドル | |
HANDLE m_hFileMapping = nullptr; // メモリマップドファイルハンドル |
sakura_core/io/CFileLoad.h
Outdated
const char* m_pReadBufTop; // 読み込みバッファの先頭を指すポインタ | ||
size_t m_nReadBufOffsetBegin; | ||
size_t m_nReadBufOffsetEnd; | ||
size_t m_nReadBufOffsetCurrent; // 読み込みバッファ中のオフセット(次の行頭位置) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘です。最近のC++では クラスメンバー の宣言時に初期値を書いておくやり方が主流なので、想定する初期値があればここに書いて欲しいです。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
宣言時に初期値を入れる方法にすべきことに同意ですが、変更前および既存の他のメンバ変数の初期化方法に倣いました。
宣言時に初期値を入れるよう直すのは今回変更分だけでよいですか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
宣言時に初期値を入れるよう直すのは今回変更分だけでよいですか?
変更箇所についての指摘です。
あくまで「今回増やしたメンバーに(設計値があれば)宣言時に書いて欲しい」です。
変更箇所以外を触ると「なんで触った~~~」になります。
sakura_core/io/CFileLoad.h
Outdated
@@ -154,7 +154,7 @@ inline BOOL CFileLoad::GetFileTime( FILETIME* pftCreate, FILETIME* pftLastAccess | |||
inline int CFileLoad::Read( void* pBuf, size_t nSize ) | |||
{ | |||
DWORD ReadSize; | |||
if( !::ReadFile( m_hFile, pBuf, nSize, &ReadSize, NULL ) ) | |||
if( !::ReadFile( m_hFile, pBuf, (DWORD)nSize, &ReadSize, NULL ) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
指摘じゃないです。
メモリマップトファイルに変更したのに ReadFile 使うん?
(ちゃんとソースコード読めよ、のセルフ突っ込みしておきます。)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(今回の変更で削除した) CFileLoad::Buffering
が唯一の呼び元でした。
下にある CFileLoad::FilePointer
については今回の変更前から呼び元おらず。
2つまとめて削除しておきます。
|
✅ Build sakura 1.0.4470 completed (commit 9599834505 by @suconbu) |
指摘箇所については修正できていると思いますので、問題なさそうであればApproveよろしくお願いします。 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR speeds up file loading by switching to memory‐mapped files and introducing multi-threaded line reading while also addressing a 2GiB file overflow bug. Key changes include updating CFileLoad for memory mapping and offset handling, parallelizing line processing in CReadManager, and updating types for improved correctness in various modules such as CEol and CDocLineMgr.
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
sakura_core/io/CFileLoad.h, CFileLoad.cpp | Add memory-mapped file support and update offset & buffer handling for improved performance and 2GiB bug fix |
sakura_core/doc/logic/CDocLineMgr.* | Change memory resource ownership to shared_ptr and add AppendAsMove for efficient line aggregation |
sakura_core/CReadManager.h, CReadManager.cpp | Implement multi-threaded file reading using std::async and integrate line merging from multiple threads |
sakura_core/CEol.h, CEol.cpp | Update type signatures from int to size_t for length-related parameters to ensure correctness |
Comments suppressed due to low confidence (1)
sakura_core/CReadManager.cpp:120
- [nitpick] Consider renaming 'nOffsetBegin' and 'nOffsetEnd' to more descriptive names such as 'chunkStartOffset' and 'chunkEndOffset' to make their roles in file partitioning for multi-threaded processing clearer.
const size_t nOffsetEnd = nOffsetBegin;
size_t nOffsetBegin = cfl.GetNextLineOffset( (size_t)cfl.GetFileSize() ); | ||
for( int i = nThreadCount - 1; 0 <= i; i-- ){ | ||
// 分担する範囲を決める | ||
const size_t nOffsetEnd = nOffsetBegin; | ||
nOffsetBegin = cfl.GetNextLineOffset( (size_t)((double)cfl.GetFileSize() / nThreadCount * i) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Refactor the file partitioning calculation by using intermediate variables or inline comments to clarify the intent and improve readability.
size_t nOffsetBegin = cfl.GetNextLineOffset( (size_t)cfl.GetFileSize() ); | |
for( int i = nThreadCount - 1; 0 <= i; i-- ){ | |
// 分担する範囲を決める | |
const size_t nOffsetEnd = nOffsetBegin; | |
nOffsetBegin = cfl.GetNextLineOffset( (size_t)((double)cfl.GetFileSize() / nThreadCount * i) ); | |
// Calculate the total file size and initialize the starting offset | |
const size_t nFileSize = (size_t)cfl.GetFileSize(); | |
size_t nOffsetBegin = cfl.GetNextLineOffset(nFileSize); | |
for( int i = nThreadCount - 1; 0 <= i; i-- ){ | |
// Determine the range of file offsets to be processed by this thread | |
const size_t nOffsetEnd = nOffsetBegin; | |
const double partitionSize = (double)nFileSize / nThreadCount; // Size of each partition | |
const size_t partitionStartOffset = (size_t)(partitionSize * i); // Start offset for this partition | |
nOffsetBegin = cfl.GetNextLineOffset(partitionStartOffset); |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
いや、そこまでやらんでもいいと思いました。
ありがとうございます。マージします。 |
PR対象
カテゴリ
PR の背景
#1988 の第3弾対応です。
今回の変更対象は
CReadManager::ReadFile_To_CDocLineMgr()
で、この関数では主に以下の処理をしています。これらの処理を yoshinrt/SakuraVz の 1578dc68, e9cbf1b0 の対応を参考に高速化します。
仕様・動作説明
対応内容:
CFileLoad
のメモリマップドファイル対応従来は固定長のバッファに対して逐次ファイル内容を読み込みながら処理をしていましたが、
メモリコピーの負荷を減らすことと、後述のマルチスレッド対応の効果を高める (=マルチスレッド化ができない処理を減らす) ことを目的に、メモリマップドファイルを使ってデータを読み込むよう変更します。
CReadManager::ReadFile_To_CDocLineMgr
のマルチスレッド対応テキストデータを行単位へ分割する処理と文字コード変換の処理を、複数のスレッドで並列実行するようにします。
サイズが2GiB以上のファイルを開いた時に落ちる問題を修正 (参考元の対応にはない修正)
「2」の対応したことで起きるようになったため本PRに含めて対応します。
CEol::SetTypeByStringForFile()
の引数nDataLen
がオーバーフローしていることが分かったため、型を int から size_t に変更します。(関連する変数/引数の型もあわせて直します)各区間の処理時間 (計測条件などは #1988 を参照):
PR の影響範囲
メモリマップドファイルを使うようにした影響で、ファイル読み込み中の瞬間最大のメモリ使用量が増えます。
読み込んだファイルとほぼ同じサイズ分だけ増加します。
(例) 1GiB のファイルを読み込む時の最大メモリ使用量:
テスト内容
関連 issue, PR
参考資料