From cd128dd76526ed01c9a6c125b7ecddd9e3e08e57 Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Wed, 12 Mar 2025 14:48:46 +0100 Subject: ProgressBar improvements (#305) * changed ProgressBar so it doesn't use printf. printf by default is very slow on Windows due to weird buffering behaviour. During a 2 minute build download I profiled 35 CPU seconds inside printf * changed so ProgressBar uses plain output mode if stdout is not a console/tty --- src/zen/zen.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/zen/zen.h | 1 + 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 0fcf9d871..9d0eab7dc 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,10 @@ ZEN_THIRD_PARTY_INCLUDES_END #include +#ifndef ZEN_PLATFORM_WINDOWS +# include +#endif + ////////////////////////////////////////////////////////////////////////// namespace zen { @@ -261,8 +266,22 @@ ZenCmdBase::ResolveTargetHostSpec(const std::string& InHostSpec) return ResolveTargetHostSpec(InHostSpec, /* out */ Dummy); } +static bool +IsStdoutTty() +{ +#if ZEN_PLATFORM_WINDOWS + static HANDLE hStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode); + return IsConsole; +#else + return isatty(fileno(stdout)); +#endif +} + ProgressBar::ProgressBar(bool PlainProgress, bool ShowDetails) -: m_PlainProgress(PlainProgress) +: m_StdoutIsTty(IsStdoutTty()) +, m_PlainProgress(PlainProgress || !m_StdoutIsTty) , m_ShowDetails(ShowDetails) , m_LastUpdateMS(m_SW.GetElapsedTimeMs() - 10000) { @@ -323,7 +342,27 @@ ProgressBar::UpdateState(const State& NewState, bool DoLinebreak) ETA, NewState.Details.empty() ? "" : fmt::format(". {}", NewState.Details)); std::string::size_type EraseLength = m_LastOutputLength > Output.length() ? (m_LastOutputLength - Output.length()) : 0; - printf("%s%s%s", Output.c_str(), std::string(EraseLength, ' ').c_str(), DoLinebreak ? "\n" : ""); + + ExtendableStringBuilder<128> LineToPrint; + LineToPrint << Output << std::string(EraseLength, ' '); + if (DoLinebreak) + LineToPrint << "\n"; + +#if ZEN_PLATFORM_WINDOWS + static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + + if (m_StdoutIsTty) + { + WriteConsoleA(hStdOut, LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + } + else + { + ::WriteFile(hStdOut, (LPCVOID)LineToPrint.c_str(), (DWORD)LineToPrint.Size(), 0, 0); + } +#else + fwrite(LineToPrint.c_str(), 1, LineToPrint.Size(), stdout); +#endif + m_LastOutputLength = DoLinebreak ? 0 : Output.length(); m_State = NewState; } diff --git a/src/zen/zen.h b/src/zen/zen.h index 835c2b6ac..6765101db 100644 --- a/src/zen/zen.h +++ b/src/zen/zen.h @@ -94,6 +94,7 @@ public: bool HasActiveTask() const; private: + const bool m_StdoutIsTty = true; const bool m_PlainProgress; const bool m_ShowDetails; Stopwatch m_SW; -- cgit v1.2.3