From 98d5794f20f9c5aece5de4eba76d3c7550772820 Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Sat, 15 Mar 2008 00:29:17 +0000 Subject: [PATCH] Increased gorgonsub's ASS write speed with large files 3.5x by getting rid of wxString conversion and concatenation routines and writing my own. Originally committed to SVN as r2059. --- aegilib/include/aegilib/utils.h | 12 +++ aegilib/src/formats/format_ass_dialogue.cpp | 101 ++++++++++++++++---- aegilib/src/text_file_reader.cpp | 43 +++++---- aegilib/src/text_file_reader.h | 2 +- aegilib/src/time.cpp | 41 ++++++-- aegilib/src/utils.cpp | 30 ++++++ aegilib/test/src/main.cpp | 5 +- aegilib/test/test.vcproj | 2 +- 8 files changed, 188 insertions(+), 48 deletions(-) diff --git a/aegilib/include/aegilib/utils.h b/aegilib/include/aegilib/utils.h index 0dfa2f640..f7e45fd58 100644 --- a/aegilib/include/aegilib/utils.h +++ b/aegilib/include/aegilib/utils.h @@ -80,4 +80,16 @@ namespace Gorgonsub { String IntegerToString(int value); String PrettySize(int bytes); + // Fast string write + inline void WriteText(wxChar *&dst,const wxChar *src,size_t len,size_t &pos) { + memcpy(dst,src,len*sizeof(wxChar)); + dst += len; + pos += len; + } + inline void WriteChar(wxChar *&dst,const wxChar &src,size_t &pos) { + *dst = src; + dst++; + pos++; + } + void WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos); }; diff --git a/aegilib/src/formats/format_ass_dialogue.cpp b/aegilib/src/formats/format_ass_dialogue.cpp index 3aad5777b..290f35b36 100644 --- a/aegilib/src/formats/format_ass_dialogue.cpp +++ b/aegilib/src/formats/format_ass_dialogue.cpp @@ -140,31 +140,92 @@ bool DialogueASS::Parse(wxString rawData, int version) // Serialize String DialogueASS::ToText(int version) const { - // Prepare - wxString final = _T(""); + // Old, slow code + if (false) { + // Prepare + wxString final; - // Write comment or dialogue - if (isComment) final = _T("Comment: "); - else final = _T("Dialogue: "); + // Write comment or dialogue + if (isComment) final = _T("Comment: "); + else final = _T("Dialogue: "); - // Write layer or marked - if (version >= 1) final += wxString::Format(_T("%01i,"),layer); - else final += _T("Marked=0,"); + // Write layer or marked + if (version >= 1) final += wxString::Format(_T("%01i,"),layer); + else final += _T("Marked=0,"); - // Write times, style and actor - final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(","); + // Write times, style and actor + final += start.GetString(2,1) + _T(",") + end.GetString(2,1) + _T(",") + style + _T(",") + actor + _T(","); - // Write margins - if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]); - else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]); + // Write margins + if (version <= 1) final += wxString::Format(_T("%04i,%04i,%04i,"),margin[0],margin[1],margin[2]); + else final += wxString::Format(_T("%04i,%04i,%04i,%04i,"),margin[0],margin[1],margin[2],margin[3]); - // Write effect and text - final += effect + _T(",") + text; + // Write effect and text + final += effect + _T(",") + text; - // Make sure that final has no line breaks - final.Replace(_T("\n"),_T("")); - final.Replace(_T("\r"),_T("")); + // Return final + return final; + } - // Return final - return final; + // New, faster code + else { + // Calculate size + size_t size = 9+9+20+12+12; // 9 for "comment: " (+1 for dialogue below), 9 for commas, + // 20 for times, 12 for margins, 12 just to be sure that layer fits + if (!isComment) size++; // Comment->Dialogue + if (version == 0) size += 8; // "Marked=0" + else if (version == 2) size += 5; // Fourth margin + size += style.Length() + actor.Length() + effect.Length() + text.Length(); + + // Allocate string + wxString final; + wxChar *buffer = final.GetWriteBuf(size); + wxChar temp[16]; + + // Write comment/dialogue + size_t pos = 0; + if (isComment) WriteText(buffer,_T("Comment: "),9,pos); + else WriteText(buffer,_T("Dialogue: "),10,pos); + + // Write layer or marked + if (version >= 1) { + WriteNumber(buffer,temp,layer,0,pos); + WriteChar(buffer,_T(','),pos); + } + else WriteText(buffer,_T("Marked=0,"),9,pos); + + // Write times + wxString tempStr = start.GetString(2,1); + WriteText(buffer,&tempStr[0],10,pos); + WriteChar(buffer,_T(','),pos); + tempStr = end.GetString(2,1); + WriteText(buffer,&tempStr[0],10,pos); + WriteChar(buffer,_T(','),pos); + + // Write style and actor + WriteText(buffer,&style[0],style.Length(),pos); + WriteChar(buffer,_T(','),pos); + WriteText(buffer,&actor[0],actor.Length(),pos); + WriteChar(buffer,_T(','),pos); + + // Write margins + size_t marCount = 3; + if (version == 2) marCount++; + for (size_t i=0;iDelete(); } @@ -182,6 +184,7 @@ Gorgonsub::String TextFileReader::ActuallyReadLine() // Checks if there's more to read bool TextFileReader::HasMoreLines() { + if (cache.size()) return true; wxCriticalSectionLocker locker(mutex); return (!file.Eof()); } @@ -217,12 +220,13 @@ String TextFileReader::ReadLineFromFile() // Load into cache if needed String final; - if (cache.size() == 0) { + { wxCriticalSectionLocker locker(mutex); - cache.push_back(ActuallyReadLine()); + if (cache.size() == 0) { + cache.push_back(ActuallyReadLine()); + } } - bool startThread = false; { // Retrieve from cache wxCriticalSectionLocker locker(mutex); @@ -234,16 +238,11 @@ String TextFileReader::ReadLineFromFile() // Start the thread to prefetch more if (cache.size() < 3 && thread == NULL) { thread = new PrefetchThread(this); - startThread = true; + thread->Create(); + thread->Run(); } } - // Starts the thread - if (startThread) { - thread->Create(); - thread->Run(); - } - return final; } @@ -253,21 +252,27 @@ String TextFileReader::ReadLineFromFile() wxThread::ExitCode PrefetchThread::Entry() { // Lock - wxCriticalSectionLocker locker(parent->mutex); - while (parent->cache.size() < 3 && !parent->file.Eof()) { - // So I need to do this for whatever reason + bool run = true; + while (run) { if (TestDestroy()) { parent->thread = NULL; return 0; } - - // Get line - parent->cache.push_back(parent->ActuallyReadLine()); + { + wxCriticalSectionLocker locker(parent->mutex); + if (parent->cache.size() < 6) { + if (!parent->file.Eof()) { + // Get line + parent->cache.push_back(parent->ActuallyReadLine()); + } + else run = false; + } + } + Sleep(50); } // Die - //wxCriticalSectionLocker locker(parent->mutex); + wxCriticalSectionLocker locker(parent->mutex); parent->thread = NULL; - Delete(); return 0; } diff --git a/aegilib/src/text_file_reader.h b/aegilib/src/text_file_reader.h index 2f8ec5049..5c9376e76 100644 --- a/aegilib/src/text_file_reader.h +++ b/aegilib/src/text_file_reader.h @@ -64,7 +64,7 @@ namespace Gorgonsub { String ActuallyReadLine(); public: - TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=false); + TextFileReader(wxInputStream &stream,String encoding=_T(""),bool trim=true,bool prefetch=true); ~TextFileReader(); String ReadLineFromFile(); diff --git a/aegilib/src/time.cpp b/aegilib/src/time.cpp index 6f3234e8e..1dce5096e 100644 --- a/aegilib/src/time.cpp +++ b/aegilib/src/time.cpp @@ -67,14 +67,43 @@ String Time::GetString(int ms_precision,int h_precision) const else if (ms_precision == 1) _ms /= 100; else if (ms_precision == 0) _ms = 0; - // Generate mask string - wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision); + // Old code + if (false) { + // Generate mask string + wxString mask = wxString::Format(_T("%%0%ii:%%0%ii:%%0%ii.%%0%ii"),h_precision,2,2,ms_precision); - // Generate final string - wxString final = wxString::Format(mask,h,min,s,_ms); + // Generate final string + wxString final = wxString::Format(mask,h,min,s,_ms); - // Done - return final; + // Done + return final; + } + + // New code + else { + // Get write buffer + wxString final; + size_t size = 7+h_precision+ms_precision; + size_t pos = 0; + wxChar *buffer = final.GetWriteBuf(size); + wxChar temp[16]; + + // Write time + WriteNumber(buffer,temp,h,h_precision,pos); + WriteChar(buffer,_T(':'),pos); + WriteNumber(buffer,temp,min,2,pos); + WriteChar(buffer,_T(':'),pos); + WriteNumber(buffer,temp,s,2,pos); + WriteChar(buffer,_T('.'),pos); + WriteNumber(buffer,temp,_ms,ms_precision,pos); + + // Write terminator + WriteText(buffer,_T("\0"),1,pos); + + // Restore string's state and return + final.UngetWriteBuf(pos-1); + return final; + } } diff --git a/aegilib/src/utils.cpp b/aegilib/src/utils.cpp index 5ba67535b..7c506aef6 100644 --- a/aegilib/src/utils.cpp +++ b/aegilib/src/utils.cpp @@ -82,3 +82,33 @@ String Gorgonsub::FloatToString(double value) { String Gorgonsub::IntegerToString(int value) { return wxString::Format(_T("%i"),value); } + + +//////////////////////////// +// Fast writing to a string +void Gorgonsub::WriteNumber(wxChar *&dst,wxChar *temp,int number,int pad,size_t &pos) { + // Write number backwards first + int div, value; + size_t len; + for (len=0;true;len++) { + div = number / 10; + value = number - (div*10); + temp[len] = (wxChar) (value + '0'); + if (!div) break; + number = div; + } + len++; + + // Pad with zeroes + pad -= (int)len; + for (int i=0;i