试图模拟恒定的字节速率。 与time.sleep结果混淆

上下文

我在我的计算机上使用Windows 7(播放器)和我的大学计算机上的Linux(debian)(stream式传输),我使用ssh来控制它。
我试图通过阅读一个wave文件模拟一个麦克风的恒定字节速率,就好像有人在说话一样。 问题是字节率低于目标。

select32KB / s速率,捕获时间为0.020秒。
我使用time.sleep来实现模拟麦克风,以产生每个0.020秒的每个数据块。 但是所得到的速率是大约27KB / s,而不是32KB / s

问题

我决定在linux机器上testing多less准确的time.sleep,通过阅读这个问题来使用想法。

我做了两种testing。 1)忙碌的睡眠2)正常的睡眠

平均而言,从我得到的样本中可以看出,Linux机器的睡眠分辨率是4ms。 在窗户上它less于/等于1ms。

问题

  1. 什么可能会限制在Linux机器上的睡眠分辨率?
  2. (在Linux上)为什么忙碌睡眠与time.sleep具有相同的分辨率?
  3. 我怎样才能成功地模拟一个麦克风阅读波形文件?

import time def busy_sleep(t): s=time.time() while time.time() - s < t: pass e=time.time() return es def normal_sleep(t): s=time.time() time.sleep(t) e=time.time() return es def test(fun): f = lambda x: sum(fun(x) for d in range(10))/10 print("0.100:{}".format(f(0.100))) print("0.050:{}".format(f(0.050))) print("0.025:{}".format(f(0.025))) print("0.010:{}".format(f(0.010))) print("0.009:{}".format(f(0.010))) print("0.008:{}".format(f(0.008))) print("0.007:{}".format(f(0.007))) print("0.006:{}".format(f(0.006))) print("0.005:{}".format(f(0.005))) print("0.004:{}".format(f(0.004))) print("0.003:{}".format(f(0.003))) print("0.002:{}".format(f(0.002))) print("0.001:{}".format(f(0.001))) if __name__=="__main__": print("Testing busy_sleep:") test(busy_sleep) print("Testing normal_sleep:") test(normal_sleep) 

结果

 """ Debian Testing busy_sleep: 0.100:0.10223722934722901 0.050:0.051996989250183104 0.025:0.027996940612792967 0.020:0.02207831859588623 0.010:0.011997451782226562 0.009:0.011997222900390625 0.008:0.009998440742492676 0.007:0.007997279167175292 0.006:0.0079974365234375 0.005:0.007997465133666993 0.004:0.005918483734130859 0.003:0.003997836112976074 0.002:0.0039977550506591795 0.001:0.003997611999511719 Testing normal_sleep: 0.100:0.1020797061920166 0.050:0.051999988555908205 0.025:0.028000001907348634 0.020:0.02192000865936279 0.010:0.011999979019165039 0.009:0.012000055313110351 0.008:0.010639991760253906 0.007:0.008000001907348633 0.006:0.00799997329711914 0.005:0.008000059127807617 0.004:0.006159958839416504 0.003:0.004000000953674317 0.002:0.00399998664855957 0.001:0.004000091552734375 $ uname -a Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.57-3+deb7u2 x86_64 GNU/Linux """ """ Windows 7 Testing busy_sleep: 0.100:0.10000572204589844 0.050:0.05000288486480713 0.025:0.0250014066696167 0.010:0.010500597953796388 0.009:0.010500597953796388 0.008:0.008000493049621582 0.007:0.00740041732788086 0.006:0.006400299072265625 0.005:0.005400300025939942 0.004:0.004700303077697754 0.003:0.003200197219848633 0.002:0.002700185775756836 0.001:0.0016000032424926759 Testing normal_sleep: 0.100:0.10000579357147217 0.050:0.0500028133392334 0.025:0.02500150203704834 0.010:0.01000049114227295 0.009:0.0100006103515625 0.008:0.008000493049621582 0.007:0.007000398635864257 0.006:0.006000304222106934 0.005:0.00500030517578125 0.004:0.0040001869201660155 0.003:0.0030002117156982424 0.002:0.0020000934600830078 0.001:0.0010000944137573243 """ 

真实的代码

导入os导入波导入sys导入io导入时间

 FORMAT = 8 #get_format_from_width(2) NCHANNELS = 1 FRAMERATE = 16000 # samples per second SAMPWIDTH = 2 # bytes in a sample BYTE_RATE = FRAMERATE*SAMPWIDTH CHUNK_DURATION = 0.020 CHUNK_BYTES = int(CHUNK_DURATION*BYTE_RATE) class StreamSimulator: def __init__(self): wf = wave.open("Kalimba.wav","rb") buf = io.BytesIO() buf.write(wf.readframes(wf.getnframes())) wf.close() buf.seek(0) self.buf = buf self.step = time.time() def delay(self): #delay delta = time.time() - self.step self.step=time.time() delay = CHUNK_DURATION - delta if delay > 0.001: time.sleep(delay) def read(self): buf = self.buf data = buf.read(CHUNK_BYTES) if len(data) == 0: buf.seek(0) data = buf.read(CHUNK_BYTES) self.delay() return data def close(self): self.buf.close() class DynamicPainter: def __init__(self): self.l=0 def paint(self,obj): str1=str(obj) l1=len(str1) bs="\b"*self.l clean=" "*self.l total = bs+clean+bs+str1 sys.stdout.write(total) sys.stdout.flush() self.l=l1 if __name__=="__main__": painter = DynamicPainter() stream = StreamSimulator() produced = 0 how_many = 0 painted = time.time() while True: while time.time()-painted < 1: d = stream.read() produced += len(d) how_many += 1 producing_speed = int(produced/(time.time()-painted)) painter.paint("Producing speed: {} how many: {}".format(producing_speed,how_many)) produced=0 how_many=0 painted = time.time() 

编辑

改变了“实际代码”,增加了包括睡眠时间在内的度量。
但是现在我有了双倍的字节率: Producing speed: 63996 how many: 100
这让我很困惑。 我已经尝试了不同的字节率,并且每次都是双倍的。

结论

感谢@JFSebastian 和他的代码 ,我来了解到:

  • 最好使用最后期限作为时间参考,而不是每个循环创build一个新的参考
  • 使用最后期限“摊销”时间的不精确性。rest时,围绕期望的比特率振荡一下,但是导致了一个正确的(并且更加恒定的)平均值。
  • 你只需要使用time.time()一次,这意味着更less的计算不精确性。

结果,我得到了稳定的32000 B / s,有时翻到31999,很less到31745
现在我可以听到没有任何滞后或抖动的音乐!

最后的代码

 def read(self): buf = self.buf data = buf.read(CHUNK_BYTES) if len(data) == 0: buf.seek(0) data = buf.read(CHUNK_BYTES) self.deadline += CHUNK_DURATION delay = self.deadline - time.time() if delay > 0: time.sleep(delay) return data 

结论

感谢@JFSebastian 和他的代码 ,我来了解到:

  • 最好使用最后期限作为时间参考,而不是每个循环创建一个新的参考
  • 使用最后期限“摊销”时间的不精确性。休息时,围绕期望的比特率振荡一下,但是导致了一个正确的(并且更加恒定的)平均值。
  • 你只需要使用time.time()一次,这意味着更少的计算不精确性。

结果,我得到了稳定的32000 B / s,有时翻到31999,很少到31745
现在我可以听到没有任何滞后或抖动的音乐!

我尝试使用@JFSebastian实现只使用%运算符来休眠其余部分 ,但奇怪的KB / s oscilate,所以我决定保持截止日期的实现,通过保持添加一个浮点值的不确定性。 尽管如此,总体结果足以满足我的需求。
谢谢大家。

最后的代码

 def read(self): self.deadline += 0.020 delay = self.deadline - time.perf_counter() if delay > 0: time.sleep(delay) return self._read() 

正如你可以在你所链接的问题中读到的那样,睡眠不能保证,并且可以根据操作系统的不同而有很大的不同。 但是如果你想每20毫秒发送一次数据,4毫秒的分辨率似乎就足够了。 应该没有必要提高你的睡眠()的准确性。

在Debian上,输入大概需要0.02秒,你的电脑会在12/10的时间内进入睡眠状态。 10/12 * 32是26.6,所以如果你只能得到27 KB / s是有道理的。

而不是睡0.02秒,使用自适应的休眠时间。 测量最后一次迭代所花费的时间(睡眠+发送数据),缩短睡眠时间,使整个迭代花费0.02秒。