Skip to content

ファイル読み込み高速化(行データ読み出しのマルチスレッド対応) #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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions sakura_core/CEol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ const SEolDefinition g_aEolTable[] = {
struct SEolDefinitionForUniFile{
const char* m_szDataW;
const char* m_szDataWB;
int m_nLen;
size_t m_nLen;

bool StartsWithW(const char* pData, int nLen) const{ return m_nLen<=nLen && 0==memcmp(pData,m_szDataW,m_nLen); }
bool StartsWithWB(const char* pData, int nLen) const{ return m_nLen<=nLen && 0==memcmp(pData,m_szDataWB,m_nLen); }
bool StartsWithW(const char* pData, size_t nLen) const{ return m_nLen<=nLen && 0==memcmp(pData,m_szDataW,m_nLen); }
bool StartsWithWB(const char* pData, size_t nLen) const{ return m_nLen<=nLen && 0==memcmp(pData,m_szDataWB,m_nLen); }
};
static const SEolDefinitionForUniFile g_aEolTable_uni_file[] = {
{ "", "", 0 },
Expand All @@ -75,7 +75,7 @@ static const SEolDefinitionForUniFile g_aEolTable_uni_file[] = {
@return 改行コードの種類。終端子が見つからなかったときはEEolType::noneを返す。
*/
template <class T>
EEolType GetEOLType( const T* pszData, int nDataLen )
EEolType GetEOLType( const T* pszData, size_t nDataLen )
{
for( size_t i = 1; i < EOL_TYPE_NUM; ++i ){
if( g_aEolTable[i].StartsWith(pszData, nDataLen) ){
Expand All @@ -89,7 +89,7 @@ EEolType GetEOLType( const T* pszData, int nDataLen )
ファイルを読み込むときに使用するもの
*/

EEolType _GetEOLType_uni( const char* pszData, int nDataLen )
EEolType _GetEOLType_uni( const char* pszData, size_t nDataLen )
{
for( size_t i = 1; i < EOL_TYPE_NUM; ++i ){
if( g_aEolTable_uni_file[i].StartsWithW(pszData, nDataLen) ){
Expand All @@ -99,7 +99,7 @@ EEolType _GetEOLType_uni( const char* pszData, int nDataLen )
return EEolType::none;
}

EEolType _GetEOLType_unibe( const char* pszData, int nDataLen )
EEolType _GetEOLType_unibe( const char* pszData, size_t nDataLen )
{
for( size_t i = 1; i < EOL_TYPE_NUM; ++i ){
if( g_aEolTable_uni_file[i].StartsWithWB(pszData, nDataLen) ){
Expand Down Expand Up @@ -131,22 +131,22 @@ EEolType _GetEOLType_unibe( const char* pszData, int nDataLen )
return CLogicInt(g_aEolTable[static_cast<size_t>(m_eEolType)].m_nLen);
}

void CEol::SetTypeByString( const wchar_t* pszData, int nDataLen )
void CEol::SetTypeByString( const wchar_t* pszData, size_t nDataLen )
{
SetType( GetEOLType( pszData, nDataLen ) );
}

void CEol::SetTypeByString( const char* pszData, int nDataLen )
void CEol::SetTypeByString( const char* pszData, size_t nDataLen )
{
SetType( GetEOLType( pszData, nDataLen ) );
}

void CEol::SetTypeByStringForFile_uni( const char* pszData, int nDataLen )
void CEol::SetTypeByStringForFile_uni( const char* pszData, size_t nDataLen )
{
SetType( _GetEOLType_uni( pszData, nDataLen ) );
}

void CEol::SetTypeByStringForFile_unibe( const char* pszData, int nDataLen )
void CEol::SetTypeByStringForFile_unibe( const char* pszData, size_t nDataLen )
{
SetType( _GetEOLType_unibe( pszData, nDataLen ) );
}
Expand Down
16 changes: 8 additions & 8 deletions sakura_core/CEol.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ struct SEolDefinition{
const WCHAR* m_szName;
const WCHAR* m_szDataW;
const ACHAR* m_szDataA;
int m_nLen;
size_t m_nLen;

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); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指摘ではないですが、 util/string_ex.h に似たような定義がたくさんあるので、そちらで集中管理してもいいんじゃないかと思いました。

};

constexpr auto EOL_TYPE_NUM = static_cast<size_t>(EEolType::code_max); // 8
Expand Down Expand Up @@ -140,13 +140,13 @@ class CEol {
CEol& operator = ( EEolType t ) noexcept { SetType( t ); return *this; }

//文字列内の行終端子を解析
void SetTypeByString( const wchar_t* pszData, int nDataLen );
void SetTypeByString( const char* pszData, int nDataLen );
void SetTypeByString( const wchar_t* pszData, size_t nDataLen );
void SetTypeByString( const char* pszData, size_t nDataLen );

//設定(ファイル読み込み時に使用)
void SetTypeByStringForFile( const char* pszData, int nDataLen ){ SetTypeByString( pszData, nDataLen ); }
void SetTypeByStringForFile_uni( const char* pszData, int nDataLen );
void SetTypeByStringForFile_unibe( const char* pszData, int nDataLen );
void SetTypeByStringForFile( const char* pszData, size_t nDataLen ){ SetTypeByString( pszData, nDataLen ); }
void SetTypeByStringForFile_uni( const char* pszData, size_t nDataLen );
void SetTypeByStringForFile_unibe( const char* pszData, size_t nDataLen );
};

// グローバル演算子
Expand Down
136 changes: 112 additions & 24 deletions sakura_core/CReadManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "util/window.h"
#include "CSelectLang.h"
#include "String_define.h"
#include <atomic>
#include <future>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指摘ではないですが、標準c++のヘッダーファイルは StdAfx.h でまとめてインクルードする感じにできませんかね。

たぶん趣味の領域なのでどちらでもいいです。


/*!
ファイルを読み込んで格納する(分割読み込みテスト版)
Expand Down Expand Up @@ -84,7 +86,7 @@ EConvertResult CReadManager::ReadFile_To_CDocLineMgr(
EConvertResult eRet = RESULT_COMPLETE;

try{
CFileLoad cfl(type->m_encoding);
CFileLoad cfl( type->m_encoding );

bool bBigFile;
#ifdef _WIN64
Expand All @@ -104,33 +106,64 @@ EConvertResult CReadManager::ReadFile_To_CDocLineMgr(
pFileInfo->SetFileTime( FileTime );
}

// ReadLineはファイルから 文字コード変換された1行を読み出します
// エラー時はthrow CError_FileRead を投げます
CEol cEol;
CNativeW cUnicodeBuffer;
EConvertResult eRead;
constexpr DWORD timeInterval = 33;
ULONGLONG nextTime = GetTickCount64() + timeInterval;
while( RESULT_FAILURE != (eRead = cfl.ReadLine( &cUnicodeBuffer, &cEol )) ){
if(eRead==RESULT_LOSESOME){
eRet = RESULT_LOSESOME;
// 行データ読み込みに使うスレッド数 (メインスレッドを含む)
const int nThreadCount = (std::max)(1, (int)std::thread::hardware_concurrency());

std::vector<CFileLoad> vecThreadFileLoads( nThreadCount );
std::vector<CDocLineMgr> vecThreadDocLineMgrs( nThreadCount );
std::vector<std::future<EConvertResult>> vecWorkerFutures;
std::atomic<bool> bCanceled = false;

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) );

if( nOffsetBegin == nOffsetEnd ){
continue;
}
const wchar_t* pLine = cUnicodeBuffer.GetStringPtr();
int nLineLen = cUnicodeBuffer.GetStringLength();
CDocEditAgent(pcDocLineMgr).AddLineStrX( pLine, nLineLen );
//経過通知
ULONGLONG currTime = GetTickCount64();
if(currTime >= nextTime){
nextTime += timeInterval;
NotifyProgress(cfl.GetPercent());
// 処理中のユーザー操作を可能にする
if( !::BlockingHook( NULL ) ){
throw CAppExitException(); //中断検出
}

vecThreadFileLoads[i].Prepare( cfl, nOffsetBegin, nOffsetEnd );
vecThreadDocLineMgrs[i].SetMemoryResource(pcDocLineMgr->GetMemoryResource());

if( i == 0 ){
// 最後はメインスレッドで処理
eRet = ReadLines( true, vecThreadFileLoads[i], vecThreadDocLineMgrs[i], bCanceled );
}else{
// ワーカースレッドで処理
vecWorkerFutures.push_back(
std::async(
std::launch::async,
&CReadManager::ReadLines,
this,
false,
std::ref(vecThreadFileLoads[i]),
std::ref(vecThreadDocLineMgrs[i]),
std::ref(bCanceled)
)
);
}
}

// ファイルをクローズする
// ワーカースレッド待ち合わせ
for( auto&& future : vecWorkerFutures ){
EConvertResult eRetSub = future.get();
if( eRetSub != RESULT_COMPLETE ){
eRet = eRetSub;
}
}

if( bCanceled.load() ){
// 中断
throw CAppExitException();
}

// 各スレッドの処理結果をpcDocLineMgrに集約
for( int i = 0; i < nThreadCount; i++ ){
pcDocLineMgr->AppendAsMove( vecThreadDocLineMgrs[i] );
}

cfl.FileClose();
}
catch(const CAppExitException&){
Expand Down Expand Up @@ -192,3 +225,58 @@ EConvertResult CReadManager::ReadFile_To_CDocLineMgr(
// CModifyVisitor().ResetAllModifyFlag(pcDocLineMgr, 0);
return eRet;
}

/*!
ファイルから行データを読み込む
@param[in] bMainThread メインスレッドで実行しているかどうか
@param[in] cFileLoad ファイル読み込みクラス
@param[out] cDocLineMgr 読み込んだ行データを格納
@param[in,out] bCanceled 処理中断フラグ
@returns 読み込み処理結果
*/
EConvertResult CReadManager::ReadLines(
bool bMainThread,
CFileLoad& cFileLoad,
CDocLineMgr& cDocLineMgr,
std::atomic<bool>& bCanceled
)
{
CEol cEol;
CNativeW cUnicodeBuffer;
EConvertResult eRead;
constexpr DWORD timeInterval = 33;
auto nextTime = GetTickCount64() + timeInterval;
EConvertResult eRet = RESULT_COMPLETE;

while( RESULT_FAILURE != (eRead = cFileLoad.ReadLine( &cUnicodeBuffer, &cEol )) ){
if( eRead == RESULT_LOSESOME ){
eRet = RESULT_LOSESOME;
}

if( bCanceled.load() ){
break;
}

const wchar_t* pLine = cUnicodeBuffer.GetStringPtr();
const auto nLineLen = cUnicodeBuffer.GetStringLength();
CDocEditAgent(&cDocLineMgr).AddLineStrX( pLine, nLineLen );

if( bMainThread ){
// 経過通知
const auto currTime = GetTickCount64();
if( currTime >= nextTime ){
nextTime += timeInterval;
NotifyProgress( cFileLoad.GetPercent() );
// 処理中のユーザー操作を可能にする
if( !::BlockingHook( NULL ) ){
// 中断検知
bCanceled.store( true );
eRet = RESULT_FAILURE;
break;
}
}
}
}

return eRet;
}
10 changes: 10 additions & 0 deletions sakura_core/CReadManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

#include "doc/CDocListener.h" // CProgressSubject
#include "charset/CCodeBase.h" // EConvertResult
#include "io/CFileLoad.h"
#include <atomic>

class CDocLineMgr;
struct SFileInfo; // doc/CDocFile.h
Expand All @@ -42,5 +44,13 @@ class CReadManager : public CProgressSubject{
const SLoadInfo& sLoadInfo,
SFileInfo* pFileInfo
);

private:
EConvertResult ReadLines(
bool bRunInMainThread,
CFileLoad& cFileLoad,
CDocLineMgr& cDocLineMgr,
std::atomic<bool>& bCanceled
);
};
#endif /* SAKURA_CREADMANAGER_BF5A195D_BEA1_4508_8BC7_DB5316B5B66E_H_ */
24 changes: 24 additions & 0 deletions sakura_core/doc/logic/CDocLineMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,30 @@ void CDocLineMgr::DeleteLine( CDocLine* pcDocLineDel )
}
}

//! 他のCDocLineMgrの内容を末尾に追加する
//! 元のCDocLineMgrの内容は空になる
void CDocLineMgr::AppendAsMove( CDocLineMgr& other )
{
if( other.GetDocLineTop() == nullptr ){ return; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指摘じゃないです。ほほ趣味ですが、こう書いても同じ意味。

Suggested change
if( other.GetDocLineTop() == nullptr ){ return; }
if (!other.GetDocLineTop()) return; // otherが空なら抜ける

Copy link
Member Author

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() とするところですが、ポインタ型なので比較演算子にしました。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どっちでもいいみたいです。

誤って代入するのを防止する目的で、
定数を左辺に書く
みたいな文化もあります。

if ( nullptr == xxx() ){


// 双方向リンクをつなぐ
other.GetDocLineTop()->m_pPrev = m_pDocLineBot;
if( m_pDocLineBot != nullptr ){
m_pDocLineBot->m_pNext = other.GetDocLineTop();
}

// 先頭/末尾を更新
if( m_pDocLineTop == nullptr ){
m_pDocLineTop = other.GetDocLineTop();
}
m_pDocLineBot = other.GetDocLineBottom();

m_nLines += other.GetLineCount();

// 所有権が移ったので空っぽにしておく
other._Init();
}

// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
// 行データへのアクセス //
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
Expand Down
8 changes: 7 additions & 1 deletion sakura_core/doc/logic/CDocLineMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <Windows.h>
#include <memory_resource>
#include <memory>
#include "_main/global.h" // 2002/2/10 aroka
#include "basis/SakuraBasis.h"
#include "util/design_template.h"
Expand Down Expand Up @@ -71,6 +72,11 @@ class CDocLineMgr{
CDocLine* AddNewLine(); //!< 最下部に新しい行を挿入
void DeleteAllLine(); //!< 全ての行を削除する
void DeleteLine( CDocLine* ); //!< 行の削除
void AppendAsMove( CDocLineMgr& other ); //!< 他のCDocLineMgrの内容を末尾に追加する

// メモリリソースのアクセス
std::shared_ptr<std::pmr::memory_resource> GetMemoryResource() const { return m_docLineMemRes; }
void SetMemoryResource(std::shared_ptr<std::pmr::memory_resource> memoryResource) { m_docLineMemRes = memoryResource; }

//デバッグ
void DUMP();
Expand All @@ -92,7 +98,7 @@ class CDocLineMgr{
CDocLine* m_pDocLineTop; //!< 最初の行
CDocLine* m_pDocLineBot; //!< 最後の行(※1行しかない場合はm_pDocLineTopと等しくなる)
CLogicInt m_nLines; //!< 全行数
std::unique_ptr<std::pmr::memory_resource> m_docLineMemRes;
std::shared_ptr<std::pmr::memory_resource> m_docLineMemRes;

public:
//$$ kobake注: 以下、絶対に切り離したい(最低切り離せなくても、変数の意味をコメントで明確に記すべき)変数群
Expand Down
Loading