在操作系统相关方面会涉及到时间片这部分的认识和测量 在网上似乎没有找到相关的文章 所以这里写下一份供大家参考
如有误 望指正
code
这里的两个函数返回都是***纳秒级(ns)***的时间
#ifndef __TIME_SLICE_
#define __TIME_SLICE_ 1
#include<iostream>
#include<chrono>
#include<thread>
#include<atomic>
size_t singleThreadSliceMeasure(size_t times);
#ifdef _WIN32
#include<windows.h>
#endif
class semaphore{
// bool hold;
std::atomic_bool hold;
semaphore():semaphore(true){}
semaphore(bool have):hold(have){}
public:
static semaphore& object(){
static semaphore signal;
return signal;
}
// busy wait
static void get(){
bool temp=false;
while(!object().hold.exchange(temp));
}
static void release(){
object().hold=true;
}
};
/**
* @brief return the measured OS time slice
* @return the average time of the time slice in nanosecs
* @warning only support Windows and Linux/Unix
* maybe?
*/
size_t TimeSliceMeasure(size_t times=64,bool sout=false){
const unsigned int threads=std::thread::hardware_concurrency()+1;
size_t sum=0,time=0;
#ifdef _WIN32
/**
* @brief return the measured windows time slice
* @return the average time of the time slice in nanosecs
*/
std::chrono::_V2::system_clock::time_point beg,end;
while(times--){
beg=std::chrono::system_clock::now();
// `Sleep` only guarantees that the thread will sleep for at least that length of time.
Sleep(1);
end=std::chrono::system_clock::now();
sum+=(end-beg).count();
}
time=sum/times;
// #elif __linux__
#else
// race condition maybe
using milli=std::chrono::duration<long double, std::milli>;
int actual=0;
const auto &&second=std::chrono::duration<long double>{1};
const auto &&milsec=std::chrono::duration<long double, std::milli>{1};
auto fn=[&](){
std::chrono::_V2::system_clock::time_point stl=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point beg=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point end=std::chrono::system_clock::now();
while((end-stl)<second){
end=std::chrono::system_clock::now();
if((end-beg)>milsec){
semaphore::get();
sum+=(end-beg).count();
++actual;
semaphore::release();
beg=std::chrono::system_clock::now();
}else{
beg=end;
}
}
};
for(int i=0;i<threads;++i){
std::thread tr(fn);
tr.detach();
}
std::this_thread::sleep_for(std::chrono::duration<long double>{2});
time=sum/actual;
#endif
if(sout){
std::cout<<"Time Slice:"<<time/1000000<<"ms"<<std::endl;
}
return time;
}
size_t singleThreadSliceMeasure(size_t times){
const auto &&milsec=std::chrono::duration<long double, std::milli>{1};
size_t temp=times,sum=0;
std::chrono::_V2::system_clock::time_point beg=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point end=std::chrono::system_clock::now();
while(temp){
end=std::chrono::system_clock::now();
if((end-beg)>milsec){
semaphore::get();
sum+=(end-beg).count();
--temp;
semaphore::release();
// puts("Yep");
beg=std::chrono::system_clock::now();
}else{
beg=end;
}
}
return sum/times;
}
#endif//__TIME_SLICE_
解析
semaphore
class semaphore{
// bool hold;
std::atomic_bool hold;
semaphore():semaphore(true){}
semaphore(bool have):hold(have){}
public:
static semaphore& object(){
static semaphore signal;
return signal;
}
// busy wait
static void get(){
bool temp=false;
while(!object().hold.exchange(temp));
}
static void release(){
object().hold=true;
}
};
信号量用的单例模式 因为代码中为多次及时取一个平均 为了预防 race condition 保证原子性 所以使用了一个
atomic_bool
来表示
当然乱调用release
函数会有问题 这只是个自用的一个小代码 为了简单 直接使用的 busy wait 方式来等待
因为开的线程并不多
同时会访问资源的线程也可以认为只有一个 所以可以忽略这个微秒级或者更低的时间消耗
core
整个算法的核心部分
- 利用
<chrono>
库的纳秒级时钟来计时 - 时间迭代
- 记录当前线程被挂起后再次运行所间隔的长时间段
问题也有 在于该线程被挂起的时间不确定 会有一些数据过大
目前的解决办法是增加线程的存活时长 多获得数据来减小这个误差
std::chrono::_V2::system_clock::time_point stl=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point beg=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point end=std::chrono::system_clock::now();
while((end-stl)<second){
end=std::chrono::system_clock::now();
if((end-beg)>milsec){
semaphore::get();
sum+=(end-beg).count();
++actual;
semaphore::release();
beg=std::chrono::system_clock::now();
}else{
beg=end;
}
}
about Windows
大家会发现 Windows 这块的代码比较简单
这里的简单是因为利用了Windows调度的特性 也就是利用了Sleep
函数
Java似乎也有类似的特性
具体的原因我没有过多的研究 可以参考下方链接
std::chrono::_V2::system_clock::time_point beg,end;
while(times--){
beg=std::chrono::system_clock::now();
Sleep(1);
end=std::chrono::system_clock::now();
sum+=(end-beg).count();
}
the other function
size_t singleThreadSliceMeasure(size_t times){
const auto &&milsec=std::chrono::duration<long double, std::milli>{1};
size_t temp=times,sum=0;
std::chrono::_V2::system_clock::time_point beg=std::chrono::system_clock::now();
std::chrono::_V2::system_clock::time_point end=std::chrono::system_clock::now();
while(temp){
end=std::chrono::system_clock::now();
if((end-beg)>milsec){
sum+=(end-beg).count();
--temp;
beg=std::chrono::system_clock::now();
}else{
beg=end;
}
}
return sum/times;
}
也可以使用单线程的测量方式
不过就是在低负载的情况下比较慢 得到一次结果的时间比较长 需要耐心等待
about sleep_for
std::this_thread::sleep_for(std::chrono::duration<long double>{2});
这是标准库的函数 字面值常量是在C++14标准之后才有的 这里没用所以C++11的标准就能编译了
sleep_for
是会阻塞当前线程 让当前线程进入waiting状态的 所以在开始线程需要多开一个
其他的一些可参考资料