python - C 扩展中用于 IO 有界线程的 GIL (HDF5)

标签 python python-3.x pandas hdf5 pytables

我有一个每秒获取 250,000 样本的采样应用程序,将它们缓冲在内存中并最终附加到 提供的 HDFStore > Pandas 。总的来说,这很棒。但是,我有一个运行并不断清空数据采集设备 (DAQ) 的线程,它需要定期运行。大约一秒的偏差往往会破坏事物。以下是观察到的时间的极端情况。 Start 表示 DAQ 读取开始,Finish 表示完成,IO 表示 HDF 写入(两者 DAQIO 发生在不同的线程中)。

Start        : 2016-04-07 12:28:22.241303
IO (1)       : 2016-04-07 12:28:22.241303
Finish       : 2016-04-07 12:28:46.573440 (0.16 Hz, 24331.26 ms)
IO Done (1)  : 2016-04-07 12:28:46.573440 (24332.39 ms)

如您所见,执行此写入需要 24 秒(典型写入约为 40 毫秒)。我正在写入的 HDD 没有负载,所以这种延迟不应该是由争用引起的(运行时它的利用率约为 7%)。我已禁用对我的 HDFStore 写入的索引。我的应用程序运行许多其他线程,所有这些线程都打印状态字符串,因此 IO 任务似乎阻塞了所有其他线程。我花了相当多的时间逐步检查代码以找出速度变慢的地方,它总是在 C 扩展提供的方法中,这导致了我的问题..

  1. Python(我使用的是 3.5)能否在 C 扩展中抢占执行? Concurrency: Are Python extensions written in C/C++ affected by the Global Interpreter Lock?似乎表明它不会,除非扩展特别产生。
  2. Pandas 的 HDF5 C 代码是否为 I/O 实现了任何让步?如果是这样,这是否意味着延迟是由于 CPU 受限任务造成的?我已禁用索引。
  3. 关于如何获得一致的计时有什么建议吗?我正在考虑将 HDF5 代码移到另一个进程中。不过,这只会在一定程度上有所帮助,因为无论如何我真的不能容忍大约 20 秒的写入,尤其是当它们不可预测时。

这是一个您可以运行以查看问题的示例:

import pandas as pd
import numpy as np
from timeit import default_timer as timer
import datetime
import random
import threading
import time

def write_samples(store, samples, overwrite):
    frame = pd.DataFrame(samples, dtype='float64')

    if not overwrite:
        store.append("df", frame, format='table', index=False)
    else:
        store.put("df", frame, format='table', index=False)

def begin_io():
    store = pd.HDFStore("D:\\slow\\test" + str(random.randint(0,100)) + ".h5", mode='w', complevel=0)

    counter = 0
    while True:
        data = np.random.rand(50000, 1)
        start_time = timer()
        write_samples(store, data, counter == 0)
        end_time = timer()

        print("IO Done      : %s (%.2f ms, %d)" % (datetime.datetime.now(), (end_time - start_time) * 1000, counter))

        counter += 1

    store.close()

def dummy_thread():
    previous = timer()
    while True:
        now = timer()
        print("Dummy Thread  : %s (%d ms)" % (datetime.datetime.now(), (now - previous) * 1000))
        previous = now
        time.sleep(0.01)


if __name__ == '__main__':
    threading.Thread(target=dummy_thread).start()
    begin_io()

您将获得类似于以下内容的输出:

IO Done      : 2016-04-08 10:51:14.100479 (3.63 ms, 470)
Dummy Thread  : 2016-04-08 10:51:14.101484 (12 ms)
IO Done      : 2016-04-08 10:51:14.104475 (3.01 ms, 471)
Dummy Thread  : 2016-04-08 10:51:14.576640 (475 ms)
IO Done      : 2016-04-08 10:51:14.576640 (472.00 ms, 472)
Dummy Thread  : 2016-04-08 10:51:14.897756 (321 ms)
IO Done      : 2016-04-08 10:51:14.898782 (320.79 ms, 473)
IO Done      : 2016-04-08 10:51:14.901772 (3.29 ms, 474)
IO Done      : 2016-04-08 10:51:14.905773 (2.84 ms, 475)
IO Done      : 2016-04-08 10:51:14.908775 (2.96 ms, 476)
Dummy Thread  : 2016-04-08 10:51:14.909777 (11 ms)

最佳答案

答案是否定的,这些作者没有发布 GIL。请参阅文档 here .我知道您实际上并没有尝试使用多个 线程进行写入,但这应该会提示您。当发生写入时会持有强锁,以防止多次写入。 PyTablesh5py 都将此作为 HDF5 标准的一部分。

可以看看SWMR ,虽然没有直接得到 pandas 的支持。 PyTables 文档 herehere指出解决方案。这些通常涉及有一个单独的进程从队列中拉出数据并写入它。

在任何情况下,这通常都是一种更具可扩展性的模式。

关于python - C 扩展中用于 IO 有界线程的 GIL (HDF5),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36488214/

相关文章:

python - 如何使用 python-pptx 删除未填充的占位符项目

python - 初始化子类中在父类中使用的属性

python - Pandas 有条件创建新的数据框列

python - Pandas:根据列上的条件生成多个条形图

python - 1.6 版的 Django 教程

python - 计算图的顶点对上函数的双重求和值

python - 转置 Pandas 数据框并更改列标题

python-3.x - Sklearn PCA 分解解释_方差_比率_

python-3.x - 停用 pipenv 环境

python - 在同一轴上绘制多个条形图