I have created this code to create a typewriter console effect:
void fastType(const std::string& text, int delay)
{
for (char c : text)
{
std::cout << c;
std::this_thread::sleep_for(std::chrono::nanoseconds(delay));
}
}
void fastType(const std::string& text, int delay = 2);
Example usage:
std::string message = "hello world...";
fastType(message);
In Debug mode, the function seems to have a delay faster than 10 milliseconds, but when I set it to Release mode, it seems to have a delay of around 10 milliseconds and not printing any faster? I have absolutely no clue as to why this could be happening. Also, I am using Visual Studio 2022.
I tried changing it to std::chrono::milliseconds(delay)
and set the delay lower than 10 milliseconds and it didn't become any faster. I tried adding std::cout.flush();
before the line: std::this_thread::sleep_for(std::chrono::nanoseconds(delay));
and it did not do anything (chatgpt said to try that). I am genuinely stumped and have no idea what is causing this issue so any tips on how I could print paragraphs of text quickly with a typewriter effect would be greatly appreciated!
NOTE: I am NOT actually trying to set the delay to 2 nanoseconds but rather experimenting with how to make the delay shorter than 16ms and why this is happening.
This issue actually involves the thread scheduling of the operating system and some of the underlying principles of the code.
Because the Microsoft documentation related to the std::this_thread::sleep_for() function does not explain its underlying implementation, I will use the Sleep() method here:
void Sleep(
[in] DWORD dwMilliseconds
);
This method has a more detailed explanation in the documentation, so we can figure it out why delaytime not cant be less than 16ms.
The operating system has a timer used for scheduling tasks, which often has a resolution typically between 10 to 15 milliseconds. And this is the system clock resolution. The operating system schedules system threads based on this clock cycle. If your thread's sleep time is too short, such as 0.01 ms, then, as mentioned in the documentation, the actual sleep time will be less than the set time. However, if the sleep time is around 3 ms or 5 ms, which is within the same precision but less than the system clock cycle, the thread will wait until the next clock cycle to be executed. That's why the delay time not always like your expect.
In the official documentation, to address this situation, the methods timeBeginPeriod(5);, timeBeginPeriod(), and timeEndPeriod() are provided to adjust the precision of the system clock. The variables in these two methods must be consistent and used immediately before and after the timer.
I also wrote a piece of code regarding testing thread delays for your reference. It can set a delay of less than 16 ms in both release and debug modes. Of course, you can also use your original method, which has been tested and is also effective. The reason for using the Sleep() method here is that its underlying logic can be found in the official documentation. In contrast, the documentation for std::this_thread::sleep_for() does not provide a detailed explanation of its logic, so we can only infer that its logic is similar to that of Sleep().
#include <windows.h>
#include <chrono>
#include <iostream>
#include <thread>
#include <timeapi.h>
#pragma comment(lib, "winmm.lib")
int main()
{
using namespace std::chrono_literals;
std::cout << "Hello waiter\n" << std::flush;
const auto start = std::chrono::high_resolution_clock::now();
timeBeginPeriod(10);
//std::this_thread::sleep_for(6ms) ; // sleep_for() works too.
Sleep(6);
timeEndPeriod(10);
const auto end = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Waited " << elapsed.count() << " ms\n";
}
If you use it, just include the following part:
timeBeginPeriod(10);
//std::this_thread::sleep_for(6ms) ; // sleep_for() works too.
Sleep(6);
timeEndPeriod(10);
The rest is used to monitor the sleep time. I hope my answer can be helpful to you.