[Python]战“疫”期,阿里云云效团队在家高效开发实录( 二 )

  • 随着深度学习的流行 , 用来加速数据科学的新的硬件层出不穷 , 这其中最常见的就是 GPU , 在深度学习前序流程中进行数据处理 , 我们是不是也能用上 GPU 来加速呢?
  • 这几个库的操作都是命令式的(imperative) , 和命令式相对应的就是声明式(declarative) 。 命令式的更关心 how to do , 每一个操作都会立即得到结果 , 方便对结果进行探索 , 优点是很灵活;缺点则是中间过程可能占用大量内存 , 不能及时释放 , 而且每个操作之间就被割裂了 , 没有办法做算子融合来提升性能;那相对应的声明式就刚好相反 , 它更关心 what to do , 它只关心结果是什么 , 中间怎么做并没有这么关心 , 典型的声明式像 SQL、TensorFlow 1.x , 声明式可以等用户真正需要结果的时候才去执行 , 也就是 lazy evaluation , 这中间过程就可以做大量的优化 , 因此性能上也会有更好的表现 , 缺点自然也就是命令式的优点 , 它不够灵活 , 调试起来比较困难 。
  • 为了解决这几个问题 , Mars 被我们开发出来 , Mars 在 MaxCompute 团队内部诞生 , 它的主要目标就是让 Numpy、pandas 和 scikit-learn 等数据科学的库能够并行和分布式执行 , 充分利用多核和新的硬件 。
    Mars 的开发过程中 , 我们核心关注的几点包括:
    1. 我们希望 Mars 足够简单 , 只要会用 Numpy、pandas 或 scikit-learn 就会用 Mars 。
    2. 避免重复造轮子 , 我们希望能利用到这些库已有的成果 , 只需要能让他们被调度到多核/多机上即可 。
    3. 声明式和命令式兼得 , 用户可以在这两者之间自由选择 , 灵活度和性能兼而有之 。
    4. 足够健壮 , 生产可用 , 能应付各种 failover 的情况 。
    当然这些是我们的目标 , 也是我们一直努力的方向 。
    Mars tensor:Numpy 的并行和分布式加速器 上面说过 , 我们的目标之一是 , 只要会用 Numpy 等数据科学包 , 就会用 Mars 。 我们直接来看代码 , 还是以蒙特卡洛为例 。 变成 Mars 的代码是什么样子呢?
    import mars.tensor as mtN = 10 ** 10data = http://news.hoteastday.com/a/mt.random.uniform(-1, 1, size=(N, 2))inside = (mt.sqrt((data ** 2).sum(axis=1)) < 1).sum()pi = (4 * inside / N).execute()print('pi: %.5f' % pi) 可以看到 , 区别就只有两处:import numpy as np 变成 import mars.tensor as mt, 后续的 np. 都变成 mt. ;pi 在打印之前调用了一下 .execute() 方法 。
    也就是默认情况下 , Mars 会按照声明式的方式 , 代码本身移植的代价极低 , 而在真正需要一个数据的时候 , 通过 .execute() 去触发执行 。 这样能最大限度得优化性能 , 以及减少中间过程内存消耗 。
    这里 , 我们还将数据的规模扩大了 1000 倍 , 来到了 100 亿个点 。 之前 1/1000 的数据量的时候 , 在我的笔记本上需要 757ms;而现在数据扩大一千倍 , 光 data 就需要 150G 的内存 , 这用 Numpy 本身根本无法完成 。 而使用 Mars , 计算时间只需要 3min 44s , 而峰值内存只需要 1G 左右 。 假设我们认为内存无限大 , Numpy 需要的时间也就是之前的 1000 倍 , 大概是 12min 多 , 可以看到 Mars 充分利用了多核的能力 , 并且通过声明式的方式 , 极大减少了中间内存占用 。
    前面说到 , 我们试图让声明式和命令式兼得 , 而使用命令式的风格 , 只需要在代码的开始配置一个选项即可 。
    import mars.tensor as mtfrom mars.config import optionsoptions.eager_mode = True# 打开 eager mode 后 , 每一次调用都会立即执行 , 行为和 Numpy 就完全一致N = 10 ** 7data = http://news.hoteastday.com/a/mt.random.uniform(-1, 1, size=(N, 2))inside = (mt.linalg.norm(data, axis=1)


    推荐阅读