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

时间:2014-10-27 19:32:36

标签: python linux windows sleep

上下文

我在我的计算机(播放器)上使用Windows 7,在我的大学计算机(流光)上使用linux(debian),我使用ssh控制它。
我试图通过读取波形文件来模拟麦克风的恒定字节速率,就像有人在说话一样。问题是字节速率低于目标。

选择32KB / s的速率和0.020秒的捕获时间 我使用time.sleep实现了模拟麦克风,每次产生0.020秒的每个数据块。但获得的速率约为27KB / s,而不是32KB / s

问题

我决定测试linux机器上精确的time.sleep,通过阅读this question使用想法。

我做了两种测试。 1)忙碌睡眠2)正常睡眠

平均而言,从我得到的样本中,它表明linux机器的睡眠分辨率是4ms。在Windows上,它小于/等于1毫秒。

问题

  1. 什么可能限制linux机器上的睡眠分辨率?
  2. (在Linux上)为什么忙碌睡眠与time.sleep具有相同的分辨率?
  3. 如何通过读取波形文件成功模拟麦克风?
  4. 代码

    import time
    
    def busy_sleep(t):
        s=time.time()
        while time.time() - s < t:
            pass
        e=time.time()
        return e-s
    
    def normal_sleep(t):
        s=time.time()
        time.sleep(t)
        e=time.time()
        return e-s
    
    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
    """
    

    真实代码

    导入操作系统     进口浪潮     导入系统     进口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()
    

    修改

    更改了&#34; Real Code&#34; ,增加时间测量,包括睡眠时间 但现在我有双字节率:Producing speed: 63996 how many: 100
    这让我很困惑。我尝试过不同的字节速率,每次最终都是双倍的。

    结论

    感谢@ J.F.Sebastian and his code,我开始了解到:

    • 最好使用截止日期作为时间参考,而不是创建每个循环的新参考
    • 使用截止日期&#34;摊还&#34;时间的不精确。睡眠,在所需的比特率周围振荡一点,但产生正确的(并且更加恒定)平均值。
    • 您只需要使用time.time()一次,这意味着更少的计算不精确。

    结果,我得到一个恒定的32000 B / s有时振荡到31999并且非常罕见到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
    

2 个答案:

答案 0 :(得分:1)

结论

感谢@ J.F.Sebastian and his code,我开始了解到:

  • 最好使用截止日期作为时间参考,而不是创建每个循环的新参考
  • 使用截止日期&#34;摊还&#34;时间的不精确。睡眠,在所需的比特率周围振荡一点,但产生正确的(并且更加恒定)平均值。
  • 您只需要使用time.time()一次,这意味着更少的计算不精确。

结果,我得到一个恒定的32000 B / s有时振荡到31999并且非常罕见到31745
现在我可以听到没有任何延迟或抖动的音乐!

我尝试使用@J.F.Sebastian implentation using only the % operator to sleep the remainder,但KB / s是奇怪的,所以我决定保留截止日期的实现,因为保持添加浮点值会导致不精确。然而,总体结果足以满足我的需求 谢谢大家。

最终守则

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

答案 1 :(得分:0)

正如您可以阅读您所链接的问题一样,无法保证睡眠,并且根据操作系统的不同,它可能会有很大差异。但是如果你想每20毫秒发送一次数据,那么4毫秒的分辨率就足够了。应该没有必要提高你的睡眠()准确度。

在Debian上,输入大约0.02秒后,您的计算机会在您要求的时间内休眠12/10。 10/12 * 32是26.6,所以如果你只收到27 KB / s就有意义了。

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