这是一个有趣的优化问题,我现在想几天了:
在系统中,我从慢速IO设备读取数据。我事先不知道我需要多少数据。确切的长度只有在我读完整个包之后才会知道(想想它,因为它有某种结束符号)。读取超出要求的数据不是问题,只是它浪费了IO时间。
两个约束也起作用:读取非常慢。我读的每个字节都要花费。无论我读取的字节数是多少,每个读取请求都具有恒定的设置成本。这使得逐字节读取成本高昂。根据经验:设置成本与读取5个字节大致相同。
我读取的软件包通常介于9到64个字节之间,但很少出现更大或更小的软件包。整个范围将介于1到120个字节之间。
当然我知道我的一些数据:包的大小相同。我可以在这里对三种模式进行分类:
相同大小的读取序列:
A A A A A ...
交替序列:
A B A B A B A B ...
三元组序列:
A B C A B C A B C ...
还存在退化三元组的特殊情况:
A A B A A B A A B ...
(A,B和C表示这里的一些包装尺寸在1到120之间)。
问题:
根据以前包的大小,如何预测下一个读取请求的大小?我需要能够快速适应的东西,使用很少的存储空间(比如低于500字节),并且从计算的角度来看也很快。
哦 - 并且预生成一些表将无法工作,因为读取大小的统计数据可能因我读取的不同设备而有很大差异。
有什么想法吗?
答案 0 :(得分:2)
您需要阅读至少3个包和最多4个包以识别该模式。
根据这个大纲,对3个包大小进行推测读取可能是一个好主意(例如,一次只需3 * 64个字节)。
答案 1 :(得分:1)
我在这里看不到问题..但首先,有几个问题:
1)你可以异步读取输入(例如单独的线程,中断例程等)吗?
2)你有缓冲区的空闲内存吗?
3)如果您已经命令更长时间的读取,您是否能够在读取整个数据包之前获得第一个字节?
如果是这样(我认为在大多数情况下它可以实现),那么你可以只有一个单独的线程以尽可能高的速度读取它们并将它们存储在缓冲区中,当缓冲区变满时停止,这样正常进程可以在该缓冲区上使用同步getc()
。
考虑一个简单的N阶自适应算法,用于M个可能的符号:
int freqs[M][M][M]; // [a][b][c] : occurences of outcome "c" when prev vals were "a" and "b"
int prev[2]; // some history
int predict(){
int prediction = 0;
for (i = 1; i < M; i++)
if (freqs[prev[0]][prev[1]][i] > freqs[prev[0]][prev[1]][prediction])
prediction = i;
return prediction;
};
void add_outcome(int val){
if (freqs[prev[0]][prev[1]][val]++ > DECAY_LIMIT){
for (i = 0; i < M; i++)
freqs[prev[0]][prev[1]][i] >>= 1;
};
pred[0] = pred[1];
pred[1] = val;
};
freqs
必须是订单N+1
的数组,您必须记住N
预先设定的值。必须根据输入的统计信息调整N
和DECAY_LIMIT
。然而,即使它们可以被自适应(例如,如果它产生太多的未命中,那么衰减限制可以缩短)。
最后一个问题是字母表。根据上下文,如果有多个不同的大小,您可以创建与符号的一对一映射。如果更多,则可以使用quantitization来限制符号数。整个算法可以使用指针算术编写,因此N
和M
不会被硬编码。
答案 2 :(得分:1)
由于读取速度很慢,我想你可以为它提供一些CPU功率,这样你就可以尝试对读取的内容做出有根据的猜测。
这基本上是一个预测器,它将具有基于概率的模型。它将生成即将到来的消息大小的预测样本,以及每个消息的成本。然后选择具有最佳预期成本的邮件大小。
然后,当您找到实际的邮件大小时,使用贝叶斯规则更新模型概率,然后再次执行。
也许这听起来很复杂,但如果将概率存储为定点分数,则不必处理浮点数,因此代码可能不多。我会使用类似Metropolis-Hastings算法的东西作为我的基本模拟器和贝叶斯更新框架。 (这只是考虑它的初步尝试。)