[C] 延迟限速逻辑
把一个函数放到一个循环中,这个函数被调用的频率在一定程度上反映了程序的速度。
while(任务没有全部完成)
{完成一段任务获得程序执行速度if(程序执行速度 > 程序执行速度的上限)限速
}
1.需要一个标准判断程序执行速度
2.每一次调用函数时都获得程序执行速度并判断可能浪费性能
因此改为
while(任务没有全部完成)
{完成一段任务++检测程序速度的间隔所占的函数调用历史次数if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值){获得程序执行速度if(程序执行速度 > 程序执行速度的上限){限速检测程序速度的间隔所占的函数调用历史次数 = 0}}
}
把计算程序执行速度的具体逻辑代入:
while(任务没有全部完成)
{完成一段任务// 参数初始化if(没有初始化)获取系统时间// 更新参数 ++检测程序速度的间隔所占的函数调用历史次数// 检测程序速度if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值){// 更新参数获取系统时间检测程序速度的间隔所占的时间 = 该次获得的系统时间 - 最近一次获得的系统时间程序执行速度 = 检测程序速度的间隔所占的函数调用历史次数/检测程序速度的间隔所占的时间// 限速主体逻辑if(程序执行速度 > 程序执行速度的上限){限速检测程序速度的间隔所占的函数调用历史次数 = 0}}
}
至此逻辑还是很简单,但是,转换成代码时还需要考虑一些问题
比如,使用这段代码的人怎么确定“程序执行速度的上限”?如果我只能赋“程序执行速度的上限”为一个根据经验摸索出来的数值,那么这个程序的通用性是很差的,不同的人,有不同的经验。因此需要找到一个通用的标准衡量这个上限。
由于我们使用“程序执行速度 = 检测程序速度的间隔所占的函数调用历史次数/检测程序速度的间隔所占的时间”来定义“程序执行速度”,所以可以规定单位时间内程序执行的次数作为上限,把这个规定代入:
while(任务没有全部完成)
{完成一段任务// 参数初始化if(没有初始化)获取系统时间// 更新参数 ++检测程序速度的间隔所占的函数调用历史次数// 检测程序速度if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值){// 更新参数获取系统时间检测程序速度的间隔所占的时间 = 该次获得的系统时间 - 最近一次获得的系统时间程序执行速度 = 检测程序速度的间隔所占的函数调用历史次数/检测程序速度的间隔所占的时间// 限速主体逻辑if(程序执行速度 > 检测程序速度的间隔所占的函数调用理想次数/单位时间){限速检测程序速度的间隔所占的函数调用历史次数 = 0}}
}
这里引入了一个“检测程序速度的间隔所占的函数调用理想次数”,代表了代码使用者期望的“程序执行速度的上限”
再往下看,要真正起到限速的作用,只能让程序等待一段时间。速度和时间是不同的量度,我们还需要使用“等待”具体实现“限速”。
如果需要限速,怎么知道我应该“等待”多久?实际程序执行越快,限速就应该越狠,因此“等待”时间的大小与实际与理想的速度差有关,即
等待时间 = 速度差 * 单位时间
代入:
while(任务没有全部完成)
{完成一段任务// 参数初始化if(没有初始化)获取系统时间// 更新参数 ++检测程序速度的间隔所占的函数调用历史次数// 检测程序速度if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值){// 更新参数获取系统时间检测程序速度的间隔所占的时间 = 该次获得的系统时间 - 最近一次获得的系统时间程序执行速度 = 检测程序速度的间隔所占的函数调用历史次数/检测程序速度的间隔所占的时间// 限速主体逻辑if(程序执行速度 > 检测程序速度的间隔所占的函数调用理想次数/单位时间){等待((程序执行速度 - 检测程序速度的间隔所占的函数调用理想次数/单位时间) * 单位时间)检测程序速度的间隔所占的函数调用历史次数 = 0}}
}
考察这一段代码的时间复杂度,“完成一段任务”不需要我们担心,所以我们唯一可能优化的就是从 if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值) 开始的这一段:它是否在每次循环中都会判定为真?或者说,检测程序速度是否会过于频繁?
现在代码使用者已经清晰地知道自己能够控制的参数的物理意义,但即使如此,代码使用者不一定能够确定好一个合适的“检测延迟的间隔所占的函数调用次数的理想值”,也就是说,如果理想值过小,检测程序速度可能会过于频繁,我们需要尽量避免这个情况。
既然已经开始考虑了理想值的大小,那么就应该开始分类讨论了:
①“检测延迟的间隔所占的函数调用次数的理想值”设置合理
②“检测延迟的间隔所占的函数调用次数的理想值”设置不合理
什么是合理的理想值?就是使得检测程序速度的频率适中的值。什么是检测程序速度的频率适中?就是尽量避免程序“超速”,但是又不能太极端,比如令“检测延迟的间隔所占的函数调用次数的理想值”=1,为了防止“超速”而执行一次任务就检测一次速度。
程序运行速度因机而异,因时而异,但总的来说,可以认为它可以在某一段时间内保持不变,某一时刻,程序运行速度发生变化,接着稳定在另一段时间内,这是一个阶梯状的分段函数,因此我们只需要对分段函数做两步操作,一个是速度平缓时,一个是速度突变时。操作之后,我们希望速度被钳制到一个上限,也就是说,速度平缓时,钳制这个速度到理想值。
速度平缓用什么衡量?速度突变用什么衡量?要是真的用什么统计学的话……emmm,或许也不是不行,但是最简单的来说,只需要判断这一次单位时间“所占的函数调用历史次数”和最近一次单位时间“所占的函数调用历史次数”之间的关系就好了。
while(任务没有全部完成)
{完成一段任务// 参数初始化if(没有初始化)获取系统时间// 更新参数 ++检测程序速度的间隔所占的函数调用历史次数// 检测程序速度if(检测程序速度的间隔所占的函数调用历史次数 >= 检测延迟的间隔所占的函数调用次数的理想值){// 更新参数获取系统时间检测程序速度的间隔所占的时间 = 该次获得的系统时间 - 最近一次获得的系统时间程序执行速度 = 检测程序速度的间隔所占的函数调用历史次数/检测程序速度的间隔所占的时间// 限速主体逻辑if(程序执行速度 > 检测程序速度的间隔所占的函数调用理想次数/单位时间){等待((程序执行速度 - 检测程序速度的间隔所占的函数调用理想次数/单位时间) * 单位时间)检测程序速度的间隔所占的函数调用历史次数 = 0}}// 参数初始化if(没有初始化)最近一次单位时间所占的函数调用历史次数 = 0// 更新参数获取系统时间调整理想值的累计时间间隔 = 该次获得的系统时间 - 最近一次获得的系统时间// 调整理想值主体逻辑if(调整理想值的累计时间间隔 == 一个单位时间){获取这一次单位时间所占的函数调用历史次数if(abs(这一次单位时间所占的函数调用历史次数 - 最近一次单位时间所占的函数调用历史次数) > 速度浮动限度)调整检测程序速度的间隔所占的函数调用理想次数调整理想值的累计时间间隔 = 0}
}
嗯……写累了
以上就是我看到那个限速的代码之后全部的思路了,中文伪代码看着多,写起来容易。但是实际上那个限速的代码更容易,因为他是把限速主体逻辑和调整理想值主体逻辑耦合在一起的
// 更新参数
++检测限速的间隔所占的函数调用历史次数// 检测限速
if(检测限速的间隔所占的函数调用次数的历史值 >= 检测限速的间隔所占的函数调用次数的理想值)
{// 更新参数获取系统时间检测限速所占的时间间隔 = 该次获得的系统时间 - 最近一次获得的系统时间// 限速逻辑主体if(检测限速所占的时间间隔 <= 规定限速时间) // 速度太快,需要限速{//if(检测限速的间隔所占的函数调用次数的历史值 >= 给定的检测限速的间隔所占的函数调用次数的理想值){检测限速的间隔所占的函数调用次数的理想值 = 检测限速的间隔所占的函数调用次数的历史值等待(单位时间 - 检测限速所占的时间间隔)// 重置参数获取系统时间检测限速的间隔所占的函数调用次数的历史值 = 0}}else{检测限速的间隔所占的函数理想调用次数减小 // 减小规则可以参考 检测限速的间隔所占的函数理想调用次数/规定限速时间 = 检测限速的间隔所占的函数调用历史次数/该次检测限速所占的时间间隔// 重置参数检测限速的间隔所占的函数调用次数的历史值 = 0}
}
源代码
/*=============================================================================
#
# Author: DanteZhu - dantezhu@vip.qq.com
#
# QQ: 327775604
#
# Last modified: 2009-12-21 13:33
#
# Filename: timelimit.h
#
# Description: �ٶ�������
#
=============================================================================*/
#ifndef _TIME_LIMIT_H_
#define _TIME_LIMIT_H_
#include <sys/time.h>
class CTimeLimit
{public:CTimeLimit(){/*{{{*/Clear();}/*}}}*//** * @brief ��ʼ��* * @param iMaxCount ����ٶ�* */void Init(int iMaxCount){/*{{{*/m_MaxCount=iMaxCount;m_TrueMaxCount=iMaxCount;}/*}}}*//** * @brief �����ٶ�* * @return 0,1,2,3*/int DetectAndLimit(){/*{{{*/if(!m_Run){m_Run=true;gettimeofday(&m_tpstart,NULL);}++m_CountPerSec;//printf("trueMax[%d]\n",m_TrueMaxCount);if(m_CountPerSec<m_TrueMaxCount){//printf("%d\n",m_CountPerSec);return 0;}int ret=0;gettimeofday(&m_tpend,NULL);int timeuse=1000000*(m_tpend.tv_sec-m_tpstart.tv_sec)+m_tpend.tv_usec-m_tpstart.tv_usec;//��if(timeuse<=1000000 ){if(m_CountPerSec>=m_MaxCount){m_Speed=m_CountPerSec;//��̬����m_TrueMaxCount=m_Speed;usleep(1000000-timeuse);gettimeofday(&m_tpstart,NULL);m_CountPerSec=0;ret = 1;}ret = 2;}else{m_Speed = m_CountPerSec*1000000/timeuse;//��̬����m_TrueMaxCount=m_Speed;gettimeofday(&m_tpstart,NULL);m_CountPerSec=0;ret = 3;}return ret;}/*}}}*//** * @brief ��ȡ��ǰ�ٶ�* * @return �ٶ�*/int Speed(){/*{{{*/return m_Speed;}/*}}}*//** * @brief �����������*/void Clear(){/*{{{*/m_MaxCount=0;m_TrueMaxCount=0;m_CountPerSec=0;m_Speed=0;m_Run=false;}/*}}}*/private:struct timeval m_tpstart,m_tpend;//���õ�����ٶ�int m_MaxCount;//ͨ����̬�����õ�����ʵ��������ٶ�int m_TrueMaxCount;//������int m_CountPerSec;//��ǰ�ٶ�int m_Speed;bool m_Run;
};
#endif
/*
#include <iostream>
#include "timelimit.h"
using namespace std;
int main()
{CTimeLimit timelimit;timelimit.Init(10);while(1){timelimit.DetectAndLimit();printf("%d\n",timelimit.Speed());}
} */
记这个用于限速的函数为 DetectAndLimit
DetectAndLimit 中有三部分,第一部分判断函数是否初始化,没有初始化则使用 sys/time.h 中的 gettimeofday 获得一个系统时间;一部分统计自己被调用的次数,这个调用次数大于某一值时才检测延迟,之后清零,实现“检测程序速度”的时间间隔;最后一部分是检测延迟。
限速主体逻辑:
获得一个系统时间,与上一次获得的系统时间相减,得到一个时间间隔,如果这个时间间隔小于设定的值,那么就 usleep 这个时间间隔和设定时间之间的差,起到了限速的作用
调整理想值主体逻辑:
进入“检测程序速度”的逻辑之后,获得“检测程序速度的间隔所占的时间”,与单位时间相比:
如果“检测程序速度的间隔所占的时间”小于单位时间,继续判断如果“检测程序速度的间隔所占的函数调用历史次数”大于“给定的检测延迟的间隔所占的函数调用次数的理想值”,说明程序执行过快,等待一段时间,并设置“检测程序速度的间隔所占的函数调用理想次数”为该次“检测程序速度的间隔所占的函数调用历史次数”;否则说明程序执行的速度限度之下,直接返回;
如果“检测程序速度的间隔所占的时间”大于单位时间,不判断速度,缩小“检测程序速度的间隔所占的函数调用理想次数”,基于速度稳定的假设,下一次就应该使“检测程序速度的间隔所占的时间”等于单位时间,到那个时候再判断速度
(他应该是从来没有想过要用除法计算程序速度,而是单纯通过分子分母的大小判断整个分数的大小,所以才这么耦合)
参考:http://www.vimer.cn/2009/12/21/xian-su-lei-c-ban/
朋友跟我说我写的太复杂了,可以考虑令牌桶
好吧,确实
嗯,我只是觉得这么写中文伪代码挺爽的hhh
确实,令牌桶的结构更加清晰,我这个网上看的,十几年前的老东西,嗯写,不太好
[C] 延迟限速逻辑相关推荐
- Linux 内核 | 网络流量限速方案大 PK
网络流量限速是一个经久不衰的话题,Linux 内核中已经实现了若干种流量限速的方式. 最简单的方式是通过定期采集速率,在超过指定的速率后直接丢包,但这种方案效果不佳,不能精准地将流量控制在指定的速率. ...
- fiddler网络限速技巧
小弟从接触fiddler到现在 将近二年左右了 由于自己是个卖流量的 所以之前只知道rrr 或者ddd 没有深层次的去了解Http协议 自从从事于工作以来,才有了更加一步的了解 今天我们说的是fid ...
- redis过期监听性能_基于Redis的延迟处理
延迟处理是一个非常常用的一个功能; 例如, 下单成功后,在30分钟内没有支付,自动取消订单; 延迟队列便是延迟处理中最常见的实现方式; 先一起看下JDK中延迟队列是如何实现的. JUC的DelayQu ...
- PostgreSQL 逻辑订阅 - 给业务架构带来了什么希望?
标签 PostgreSQL , 逻辑订阅 , 10.0 , 数据汇聚 , 数据共享 , IDC多活 , 云端线下同步 背景 逻辑订阅是PostgreSQL 10.0的新特性. 具体的原理,使用方法可以 ...
- 扩展RocketMQ 使其支持任意时间精度的消息延迟
前言 本想使用rocketmq的延迟消息特性,但延迟的范围有限,仅支持 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 这18个等 ...
- PgSQL · 应用案例 · 逻辑订阅给业务架构带来了什么?
背景 逻辑订阅是PostgreSQL 10.0的新特性. 具体的原理,使用方法可以参考如下文章. <PostgreSQL 10.0 preview 逻辑订阅 - 原理与最佳实践> < ...
- 深入理解RocketMQ延迟消息
延迟消息是实际开发中一个非常有用的功能,本文第一部分从整体上介绍秒级精度延迟消息的实现思路,在第二部分结合RocketMQ的延迟消息实现,进行细致的讲解,点出关键部分的源码.第三步介绍延迟消息与消息重 ...
- 【笔记】unity逻辑类各种即使方案汇总
迷雾遮罩: 1.使用war3的地图拼接 2.改变网格范围,传到shader中 寻路: 需要高精度的则不适合用Navmesh 1.https://howtorts.github.io/2014/01/0 ...
- pg内功修炼:逻辑复制
目录 什么是逻辑复制 逻辑解析 复制槽 output plugin 几个常见的outputplugin 几个能手动接收解析数据的函数和工具 逻辑解析测试1:观察用2个不同的output plugin解 ...
最新文章
- 加速 PyTorch 模型训练的 9 个技巧
- ffmpeg 基本数据结构和对象: AVPacket、AVPicture、AVFrame
- 提交PR后修改内容并合并commit
- linux飞信机器人的安装fetion
- 「PKUWC2018」随机游走
- 代理模式详解(静态代理和动态代理的区别以及联系)
- lvs+keepalived+nginx+tomcat高可用高性能集群部署
- 单源最短路(SPFA算法)
- 深入浅出的模型压缩:你一定从未见过如此通俗易懂的Slimming操作
- 周育如的音标口诀大全_一年级汉语音标口诀记忆方法
- solr自定义分词器
- Ubuntu18.04自带火狐浏览器设置语言为中文
- LNBP11L_LNB电源和控制电压调节器——科时进商城
- 使用Certbot配置SSL证书【ubuntu系统】
- html制作地球自转,利用CSS3实现地球自转
- 怎么用免费视频引流?如何利用视频网站免费引流?
- 真实的任正非:告诉你一个真实的华为
- 加密世界将迎来以太坊的“黄金十年”
- 400G交换机技术前景和主流产品
- ca证书 csr_ca证书csr crt cer
热门文章
- Python读写文件的七种模式(r,w,x,a,b,t,+)
- 物理cpu和逻辑cpu的区别
- 学习笔记-java代码审计-反序列化
- html如何保存在文档桌面,如何把word文档保存到桌面
- Java工程师入职——配置环境及安装开发工具
- Shell—— 17.IFS
- 论文《Patchmatch-Based Robust Stereo Matching Patchmatch-Based Robust Stereo Matching》学习
- 如何解决aws解绑银行卡问题?
- algodoo中使用的语言 Thyme 学习入门
- 免疫球蛋白IgG偶联CdTe量子点|多肽Exendin-4偶联羧基化量子点QDs-COOH|硒化镉量子点偶联羟基磷灰石