计算头脑:串行、并行、分布式云计算、GPU

打印 上一主题 下一主题

主题 503|帖子 503|积分 1509

弁言

分布式云计算、GPU 批计算以及串行和并行计算是现代计算架构中的紧张概念。它们各自代表了不同的计算模式和架构计划,为解决各种规模和复杂度的题目提供了不同的解决方案。下面将从实现思路的角度从浅入深,一起深入挖掘这些概念的精髓,探讨它们的特点与实用场景。

串行计算

串行计算是指计算任务按顺序依次执行,后续任务必须等待前一个任务完成。常用语小规模计算任务、线性题目和算法,如某些排序算法和根本数学运算。


  • 简朴易懂:代码逻辑清晰,易于编写和调试。
  • 占用资源少:对内存和存储的需求较低。
  • 执行效率低:对于大型数据集或复杂计算任务,效率较低。
题目示例

题目:从一个包含 N 个元素的未排序序列中找到最大的 K 个元素。


  • 排序法:将序列进行排序,然后选择最后 K 个元素。时间复杂度:                                        O                            (                            N                            log                            ⁡                            N                            )                                  O(N \log{N})                     O(NlogN),取决于排序算法。
  • 最小堆法:使用最小堆存储 K 个最大元素,遍历序列中的每个元素:如果当前元素比堆顶元素大,则替换堆顶元素,并保持堆的巨细为 K。时间复杂度:                                        O                            (                            N                            log                            ⁡                            K                            )                                  O(N \log{K})                     O(NlogK)。
  • 快速选择(Quickselect):基于快速排序的思想,通过划分快速找到第 K 大元素,并在此根本上找到 K 个最大元素。时间复杂度:O(N) 均匀情况下,但最坏情况是 O(N^2)。
题目求解

针对 K 可能相对较小的场景,采用 最小堆法 是一种高效且直观的选择。它在处置惩罚大数组时的性能体现相较于简朴的排序法有明显的提升,尤其是当 K 较小于 N 时。
下面是使用最小堆法来探求列表中最大 K 个元素的实现代码。
  1. import heapq
  2. def find_k_largest_elements(arr, K):
  3.     # 使用最小堆来存储最大的 K 个元素
  4.     return heapq.nlargest(K, arr)
  5. # 测试示例
  6. if __name__ == "__main__":
  7.     sequence = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
  8.     K = 3
  9.     largest_k_elements = find_k_largest_elements(sequence, K)
  10.     print(f"The largest {K} elements are: {largest_k_elements}")
  11. # The largest 3 elements are: [9, 6, 5]
复制代码


  • heapq.nlargest(K, arr):使用 Python 的 heapq 模块的 nlargest 方法,可以高效地找到最大的 K 个元素,该方法内部使用了最小堆的原理,有效性得到包管。
  • 通过调整 sequence 和 K 的值来验证结果。
这个实现有效且高效,尤其适合用于需要从大数组中提取最大元素的场景。
并行计算

并行计算是指在同一时间内同时执行多个计算任务。通常将任务划分为子任务,在多核处置惩罚器或集群中并行处置惩罚。实用于大规模计算任务,如科学计算、图像处置惩罚、数据分析、大数据处置惩罚和机器学习模型训练。


  • 提高效率:通过多线程或多处置惩罚器并行执行任务,明显提高计算效率。
  • 复杂性增长:需要对任务进行适当的划分和协调,增长了编程和调试的复杂性。
  • 实用范围广:适合于复杂计算、图像处置惩罚、物理模仿等任务。
题目示例

我们拿上文串行计算中的类似题目来演示并行计算。要并行处置惩罚探求一个未排序的序列中最大 K 个元素的任务,可以将整个序列分割为多个子序列,分别处置惩罚这些子序列以找到局部的最大数,然后合并这些结果。这个方法在大数据集上可以或许有效使用多核处置惩罚器的计算能力。
求解思路

  • 分割任务:将数组分成多个部门(子数组),每个部门由一个独立的线程或历程处置惩罚。
  • 找到子数组中的最大元素:每个子数组并行找到其最大的 K 个元素。
  • 合并结果:将全部子数组的结果合并,最终使用最小堆选取这些结果中的 K 个最大值。
题目求解

下面是使用 concurrent.futures.ThreadPoolExecutor 基于上述思路的完备代码:
  1. import heapq
  2. from concurrent.futures import ThreadPoolExecutor
  3. def find_k_largest_in_subarray(subarray, K):
  4.     """找到子数组中的最大 K 个元素"""
  5.     return heapq.nlargest(K, subarray)
  6. def find_k_largest_elements(arr, K, num_threads):
  7.     """找到数组中最大的 K 个元素,通过并行处理子数组实现"""
  8.     # 将数组分为多个子数组
  9.     subarray_size = len(arr) // num_threads
  10.     subarrays = [arr[i * subarray_size:(i + 1) * subarray_size] for i in range(num_threads - 1)]
  11.     subarrays.append(arr[(num_threads - 1) * subarray_size:])  # 最后一个子数组
  12.     # 创建线程池并并行处理子数组
  13.     results = []
  14.     with ThreadPoolExecutor(max_workers=num_threads) as executor:
  15.         futures = {executor.submit(find_k_largest_in_subarray, sub, K): sub for sub in subarrays}
  16.         for future in futures:
  17.             results.append(future.result())
  18.     # 合并所有子数组的结果,并从中找出最大的 K 个元素
  19.     merged_result = []
  20.     for res in results:
  21.         merged_result.extend(res)
  22.     return heapq.nlargest(K, merged_result)
  23. # 测试示例
  24. if __name__ == "__main__":
  25.     sequence = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
  26.     K = 3
  27.     num_threads = 2  # 可以根据 CPU 核心数进行调整
  28.     largest_k_elements = find_k_largest_elements(sequence, K, num_threads)
  29.     print(f"The largest {K} elements are: {largest_k_elements}")
  30. # The largest 3 elements are: [9, 6, 5]
复制代码


  • find_k_largest_in_subarray:处置惩罚每个子数组,找到其中的 K 个最大元素,使用 heapq.nlargest() 实现。
  • find_k_largest_elements

    • 计算每个子数组的巨细并划分;
    • 使用 ThreadPoolExecutor 创建线程池并并行处置惩罚每个子数组;
    • 网络每个线程的结果。

  • 合并结果:使用 heapq.nlargest() 从全部子数组中获取 K 个最大的元素。
在大数据集的情况下,通过任务分割和并行处置惩罚,充实使用多核处置惩罚器的上风,可以提升探求最大 K 个元素的效率。
多线程适合处置惩罚 I/O 麋集型的任务,但在 CPU 麋集型任务中效果有限,因为 Python 的全局表明器锁(GIL)限制了多个线程的并发执行。
分布式云计算

分布式计算是指计算任务分散在多个地理位置分开的计算节点上执行。每个节点可能是计算机、服务器或数据中心,它们通过网络毗连。实用于大规模网络应用、数据存储与处置惩罚,如云计算平台、社交网络、在线游戏,大数据框架(Hadoop、Spark)和分布式数据库等。


  • 扩展性:可以通过增长更多节点扩展计算能力。
  • 容错性:节点失败通常不会导致整个体系崩溃。
  • 复杂性高:需要处置惩罚节点间的通信、数据一致性和负载均衡等题目。
题目示例

假定有一个非常巨大的数组,数组大到在一台服务器下生存不下,需要生存在 1000 台服务器上,如何找到数组的中值?
求解思路

  • 选择枢值:从整个数据集中随机选择一个值 v 作为枢值,将其分发给全部服务器。
  • 分区:每台服务器根据该枢值,将本地存储的数据分为两个部门:一部门包含小于或等于 v 的元素,另一部门包含大于 v 的元素。
  • 计数:每台服务器计算小于或等于 v 的元素数量                                                    m                               i                                            m_i                     mi​ 和大于 v 的元素数量                                                    n                               i                                            n_i                     ni​。
  • 汇总:将全部服务器的计数结果发送到协调服务器,以计算全局的 m (总数小于等于 v 的元素)和 n(总数大于 v 的元素)。
  • 调整枢值:根据 m 和 n 的关系决定下一步的处置惩罚。如果 m 大于总元素的中心值,说明当前中值在 v 的左侧,此时选择右侧的元素进行下一轮计算,否则反之。
  • 迭代:重复以上步调,直到最终确定中值。
以上求解方式是一种简朴的分布式中值算法,实用于分布在不同机器上的大规模数据集的中值查找题目。
题目求解

以下是基于上述算法的 Python 实现。在这个例子中,我们会模仿 1000 台服务器并将数据分块进行计算。
  1. import random
  2. import numpy as np
  3. from concurrent.futures import ThreadPoolExecutor, as_completed
  4. def count_less_equal_and_greater(data, pivot):
  5.     """根据枢值对数据进行分区并计数"""
  6.     less_equal_count = sum(x <= pivot for x in data)
  7.     greater_count = sum(x > pivot for x in data)
  8.     return less_equal_count, greater_count
  9. def distributed_median(data, num_servers=1000):
  10.     # 将数据划分到多个服务器上
  11.     data_chunks = np.array_split(data, num_servers)
  12.     # 初始设定
  13.     n = len(data)
  14.     target_count = n // 2  # 寻找中值的目标索引
  15.     pivot = random.choice(data)  # 随机选择初始枢值
  16.     while True:
  17.         # 使用线程池并行处理每个服务器的计数
  18.         with ThreadPoolExecutor(max_workers=num_servers) as executor:
  19.             futures = {executor.submit(count_less_equal_and_greater, chunk, pivot): chunk for chunk in data_chunks}
  20.             results = [future.result() for future in as_completed(futures)]
  21.         # 汇总结果
  22.         total_less_equal = sum(r[0] for r in results)
  23.         total_greater = sum(r[1] for r in results)
  24.         if total_less_equal > target_count:  # 中值在左侧
  25.             # 选择新的枢值:在小于或等于 pivot 的数中选取
  26.             pivot = random.choice([x for chunk in data_chunks for x in chunk if x <= pivot])
  27.         else:  # 中值在右侧
  28.             # 选择新的枢值:在大于 pivot 的数中选取
  29.             pivot = random.choice([x for chunk in data_chunks for x in chunk if x > pivot])
  30.         # 检查是否找到中值
  31.         if total_less_equal == target_count or total_less_equal == target_count + 1:
  32.             return pivot
  33. # 测试示例
  34. if __name__ == "__main__":
  35.     # 生成一个大的随机数组
  36.     data = np.random.randint(0, 1000000, size=10000000)  # 1000 万个元素
  37.     median = distributed_median(data)
  38.     print(f"The estimated median is: {median}")
复制代码


  • 数据分块:使用 np.array_split 将数据划分为多个部门,以模仿 1000 个服务器的处置惩罚。
  • 计数函数:count_less_equal_and_greater 函数根据当前选择的 pivot 对数据进行分区计数。
  • 分布式中值计算

    • 在循环中,随机选择枢值并在全部服务器上并行执行计数。
    • 汇总全部服务器的计数,根据 m 和 n 的关系调整 pivot。
    • 继续迭代直到找到中值。

  • 最终输出:随机生成一个大数组,调用并输出中值。
在每次迭代时,我们可以考虑如何记着并使用上一轮的统计结果,以淘汰重复计算的次数,提高效率。
通过分布式中值算法,我们可以或许从超大数组中找出中值,纵然数据存储在多个服务器上。这种方法在分布式计算情况下极具实际应用代价,尤其是在处置惩罚大规模数据时。
GPU 批计算

GPU 计算是将图形处置惩罚单元(GPU)用于一般计算任务,特别是在并行计算中具有上风。实用深度学习模型训练和推理、图像和视频处置惩罚、科学模仿和计算等范畴。


  • 高并发处置惩罚能力:GPU 的大量核心能同时处置惩罚数千个线程,适合处置惩罚数据并行的任务,比方矩阵运算和深度学习等计算麋集型任务。
  • 吞吐量:对于大规模数据集,GPU 可以实现明显的吞吐量提升,处置惩罚速度比同一任务在 CPU 上执行时快数十倍。
  • 适合处置惩罚大型数据集:在深度学习、图像处置惩罚等范畴,GPU 批处置惩罚可以或许在较短时间内处置惩罚大量数据,动态调整权重和模型参数。
  • 适合矩阵运算:特别在深度学习、图像处置惩罚和科学计算中,GPU 能极大提高计算效率。
  • 编程复杂性:需要了解特定的编程模型和工具(如 CUDA、OpenCL)来有效使用 GPU。
CPU vs. GPU



  • CPU(中央处置惩罚器):适合处置惩罚复杂的控制逻辑和很多并发任务。通常具有较少的核心(如 4 到 16 个),但每个核心的计算能力非常强,可以或许高效处置惩罚低耽误任务。
  • GPU(图形处置惩罚单元):计划用于高并发计算,具有成百上千个小核心,可以同时处置惩罚大量类似或类似的任务,因此特别适合于批处置惩罚和数据并行计算。
题目示例

以下是求解矩阵乘法的对比示例。我们可以看到在 CPU 和 GPU 上执行矩阵乘法的速度差别。
我们可以使用 NumPy 进行 CPU 的矩阵乘法,使用 CuPy 来进行 GPU 的矩阵乘法。请确保已安装这两个库。如果在运行 GPU 代码时,请确保情况支持 CUDA。
  1. import numpy as np
  2. import cupy as cp
  3. import time
  4. # 矩阵大小
  5. N = 1000
  6. # 创建随机矩阵
  7. A = np.random.rand(N, N).astype(np.float32)
  8. B = np.random.rand(N, N).astype(np.float32)
  9. # CPU 矩阵乘法
  10. start_time = time.time()
  11. C_cpu = np.dot(A, B)
  12. cpu_time = time.time() - start_time
  13. # GPU 矩阵乘法
  14. A_gpu = cp.asarray(A)  # 将 NumPy 数组传到 GPU
  15. B_gpu = cp.asarray(B)
  16. start_time = time.time()
  17. C_gpu = cp.dot(A_gpu, B_gpu)
  18. cp_time = time.time() - start_time
  19. # 从 GPU 获取结果
  20. C_gpu_result = cp.asnumpy(C_gpu)
  21. # 打印结果
  22. print(f"CPU矩阵乘法耗时: {cpu_time:.6f} 秒")
  23. print(f"GPU矩阵乘法耗时: {cp_time:.6f} 秒")
  24. # CPU矩阵乘法耗时: 0.510122 秒
  25. # GPU矩阵乘法耗时: 0.045567 秒
复制代码


  • 矩阵生成:使用 NumPy 创建两个随机矩阵 A 和 B,巨细为 1000x1000。
  • CPU 矩阵乘法:使用 np.dot() 计算 CPU 上的矩阵乘法,记录执行时间。
  • GPU 矩阵乘法:使用 CuPy 将 NumPy 数组转为 GPU 数据。使用 cp.dot() 计算 GPU 上的矩阵乘法,记录时间。
通过这个示例,我们可以看出 GPU 在处置惩罚大规模数据集(比方矩阵乘法)时相较于 CPU 的明显上风。其并行计算能力使得它在处置惩罚数据麋集型任务时具有更高的性能和效率。在深度学习、科学计算和大数据分析等范畴,GPU 批处置惩罚已成为主流。
总结与比力

特性串行计算并行计算分布式计算GPU 计算执行方式顺序同时多节点同时批处置惩罚(同时)复杂性低中高高资源使用效率低高高高(对于并行任务)扩展性低中高中(依赖于装备)实用场景小规模任务大规模任务海量数据深度学习和计算 考虑因素


  • 任务规模与范例:了解任务的规模和性质,选择最合适的计算模式。
  • 资源可用性:考虑可用的计算资源(如 CPU、GPU、集群等)。
  • 开发复杂性:根据开发和维护的复杂水平,选择适合团队的技能栈。
以上是对串行、并行、分布式云计算以及 GPU 计算的探讨。每种计算模式都有其独特之处和实用场景,选择合适的计算方式有助于提高计算效率和低落开发成本。
结语

计算效率是计算机科学中的紧张主题,不同的计算模式和架构计划实用于不同的场景。串行计算实用于小规模任务,而并行计算实用于大规模任务。分布式云计算实用于海量数据处置惩罚,GPU 批处置惩罚实用于深度学习和计算麋集型任务。掌控每一种计算架构与头脑方式是提高计算效率和性能的关键,也是计算机工程师的必备技能。在实际应用中,根据任务的特点和需求,选择合适的计算模式和架构计划。

PS:感谢每一位志同道合者的阅读,欢迎关注、点赞、评论!



  • 上一篇:图论:最大流量题目与 Ford-Fulkerson 算法
  • 专栏:「数智通识」

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

渣渣兔

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表