HPC -- 基础理论

2024-04-13 12:04
文章标签 基础理论 hpc

本文主要是介绍HPC -- 基础理论,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

面九坤的时候被会HPC的面试官吊打了,九坤的面试官给了一个评价:能进面的是合格的程序员,会HPC的才是优秀的程序员。
我对于HPC的了解只有https://www.yuque.com/treblez/qksu6c/nqe8ip59cwegl6rk?singleDoc# 《从微架构到向量化–CPU性能优化指北》这么多,还是要深入学习一下。
本文是https://en.algorithmica.org/hpc/和 https://weedge.github.io/perf-book-cn/zh/的阅读笔记。

现代hpc硬件算法

流水冲突 Pipeline Hazard

Pipeline hazard指的是方案指令并行的因素。
image.png
Hazard分为三类:
structural hazard:当两个或多个指令需要CPU的同一部分(例如,执行单元)时,就会发生structural hazard。
data hazard:当必须等待从前一步计算操作数时,就会发生data hazard。
control hazard:当CPU无法判断下一步需要执行哪条指令时,就会发生control hazard。

分支的成本 Cost of Branch

for (int i = 0; i < N; i++)a[i] = rand() % 100;
volatile int s;for (int i = 0; i < N; i++)if (a[i] < 50)s += a[i];

产生的汇编代码如下所示:

    mov  rcx, -4000000jmp  body
counter:add  rcx, 4jz   finished   ; "jump if rcx became zero"
body:mov  edx, dword ptr [rcx + a + 4000000]cmp  edx, 49jg   counteradd  dword ptr [rsp + 12], edxjmp  counter

这里产生的cost有三个:
第一个是每两个cycles一个control hazards,每个cycle 等于19 cpu cycles
第二个是mov edx, dword ptr [rcx + a + 4000000]访存,5 cpu cycles
第三个是命中的时候4 cpu cycles 放到volatile主存

换成变量P:

for (int i = 0; i < N; i++)if (a[i] < P)s += a[i];

这个时候P和每循环周期数的关系如下:
image.png
当P = 50的时候,分支预测失败代价很高,就跟上面分析的一样,14个cycles;当 85-90的时候,分支预测失败带来的消耗小于指令执行的消耗,因此有一个低峰。

for (int i = 0; i < N; i++)a[i] = rand() % 100;std::sort(a, a + n);
volatile int s;for (int i = 0; i < N; i++)if (a[i] < 50)s += a[i];

分支预测器可以检测到比“总是左,然后总是右”或“左-右-左-右”更复杂的模式。如果我们只是将数组的大小N减小到1000(不进行排序),那么分支预测器会记住整个比较序列,基准测试再次在大约4个周期进行测量——事实上,甚至比排序数组的情况略少,因为在前一种情况下,分支预测器需要花费一些时间在“总是是”和“总是否”状态之间切换。

使用谓词也就是cmov替换分支是这里的优化方法:

for (int i = 0; i < N; i++)s += (a[i] < 50 ? a[i] : 0);

Perf book

CPU架构

image.png

BPU

一旦程序达到稳定状态,分支预测单元(BPU)就会引导CPU前端的工作。这就是从BPU到指令缓存的箭头的原因。BPU预测所有分支指令的方向,并根据这个预测引导下一个指令提取。
BPU的核心是一个包含12K条目的分支目标缓冲区(BTB),其中包含有关分支及其目标的信息。这些信息被预测算法使用。每个周期,BPU生成下一个提取地址,并将其传递给CPU前端。

DSB

前端的一个主要性能提升特性是解码流缓冲区(DSB)或_μ_op缓存。其动机是在与L1 I-cache并行工作的单独结构中缓存宏操作到_μ_ops的转换。当BPU生成一个新地址进行提取时,也会检查DSB,以查看_μ_ops的转换是否已经在DSB中可用。频繁发生的宏操作会命中DSB,流水线将避免为32字节的捆绑重复执行昂贵的预解码和解码操作。DSB每个周期可以提供八个_μ_ops,并且最多可以容纳4K个条目。

ROB

CPU后端的核心是512条目的重排序缓冲区(ROB)。
该单元在图表中被称为"分配/重命名(Allocate / Rename)"。它有几个作用。首先,它提供寄存器重命名。只有16个通用整数寄存器和32个向量/SIMD体系结构寄存器,但是物理寄存器的数量要多得多。物理寄存器位于称为物理寄存器文件(PRF)的结构中。从体系结构可见寄存器到物理寄存器的映射保存在寄存器别名表(RAT)中。
其次,ROB分配执行资源。当一条指令进入ROB时,将分配一个新条目,并为其分配资源,主要是一个执行单元和目标物理寄存器。ROB每个周期可以分配多达6个_μ_ops。
第三,ROB跟踪推测执行。当一条指令完成其执行时,其状态会更新,并且会保留在那里,直到前面的指令也完成。之所以这样做,是因为指令总是按程序顺序退役。一旦一条指令退役,其ROB条目将被释放,并且指令的结果变得可见。退役阶段比分配阶段更宽:ROB每个周期可以退役8条指令。

RS

“调度器/保留站(Scheduler / Reservation Station)”(RS)是跟踪给定_μ_op的所有资源可用性并在准备就绪时将_μ_op分派到分配端口的结构。当一条指令进入RS时,调度器开始跟踪其数据依赖关系。一旦所有源操作数可用,RS尝试将_μ_op分派到空闲的执行端口。RS的条目比ROB少。它每个周期最多可以分派6个_μ_ops。

内存

优化内存访问

  1. 当数组较小的时候,使用顺序查找替代二分查找
  2. 使用OAS替代SOA
struct S {int a;int b;int c;// other fields
};
S s[N];    // AOS<=>struct S { // SOAint a[N];int b[N];int c[N];// other fields  
};

使用Eytzinger优化二分查找

image.png

优化结构体大小

使用枚举值
struct S {unsigned a;unsigned b;unsigned c;
}; // S is `sizeof(unsigned int) * 3` bytesstruct S {unsigned a:4;unsigned b:2;unsigned c:2;
}; // S is only 1 byte a占用4位,b和c分别占用2位
使用对齐和填充
// Make an aligned array
alignas(16) int16_t a[N];// Objects of struct S are aligned at cache line boundaries
#define CACHELINE_ALIGN alignas(64) 
struct CACHELINE_ALIGN S {//...
};
避免伪共享
struct S {int a; // written by thread Aint b; // written by thread B
};#define CACHELINE_ALIGN alignas(64) 
struct S {int a; // written by thread ACACHELINE_ALIGN int b; // written by thread B
};
调整代码适应缓存大小

显式内存预取

如果请求的内存位置不在缓存中,我们将无论如何遭受缓存未命中,因为我们必须访问 DRAM 并提取数据。但是,如果我们在程序需要数据时将该内存位置引入缓存,那么我们实际上可以将缓存未命中惩罚降为零。
现代 CPU 有两种解决这个问题的机制:硬件预取和 OOO 执行。硬件预取器通过对重复内存访问模式发起预取请求来帮助隐藏内存访问延迟。而 OOO 引擎则向前看 N 条指令,并提前发出加载指令,以允许平滑执行未来将需要此数据的指令。
使用显式并且消除依赖的内存预取:

size_t idx = random_distribution(generator);
for (int i = 0; i < N; ++i) {int x = arr[idx]; idx = random_distribution(generator);// prefetch the element for the next iteration__builtin_prefetch(&arr[idx]);doSomeExtensiveComputation(x);
}

image.png

使用EHP和THP

Linux 用户可以使用三种不同的模式使用大页面:

  • 显式大页面 (EHP)
  • 系统范围透明大页面 (THP)
  • 每个进程透明大页面 (THP)

让我们比较一下这些选项。首先,EHP 预先保留在虚拟内存中,而 THP 则没有。这使得使用 EHP 的软件包更难以交付,因为它们依赖于机器管理员所做的特定配置设置。此外,EHP 静态驻留在内存中,占用宝贵的 DRAM 空间,即使它们未使用。
其次,系统范围的透明大页面非常适合快速实验。无需更改用户代码即可测试在您的应用程序中使用大页面的好处。但是,将软件包运送给客户并要求他们启用系统范围的 THP 是不明智的,因为这可能会对该系统上运行的其他程序产生负面影响。通常,开发人员会识别代码中可以从大页面中受益的分配,并在这些位置使用 madvise 提示(每个进程模式)。
每个进程的 THP 没有上述任何一个缺点,但它还有一个缺点。之前我们讨论过,内核分配 THP 对用户来说是透明的。分配过程可能涉及多个内核进程,这些进程负责在虚拟内存中腾出空间,这可能包括将内存换出到磁盘、碎片化或提升页面。透明大页面的后台维护会产生内核在管理不可避免的碎片和交换问题时产生的非确定性延迟开销。EHP 不受内存碎片化影响,也不能换出到磁盘,因此延迟开销要小得多。
总而言之,THP 更易于使用,但会产生更大的分配延迟开销。这正是 THP 在高频交易和其他超低延迟行业不受欢迎的原因,他们更喜欢使用 EHP。另一方面,虚拟机提供商和数据库往往倾向于使用每个进程的 THP,因为要求额外的系统配置会给他们的用户带来负担。

优化计算

critical path关注延迟,非critical path关注吞吐量。

破除数据流依赖

image.png
使用并行的代码破解数据流依赖
优化前:

struct Particle {                                    │float x; float y; float velocity;                  │
};                                                   ││
class XorShift32 {                                   │uint32_t val;                                      │
public:                                              │XorShift32 (uint32_t seed) : val(seed) {}          │uint32_t gen() {                                   │val ^= (val << 13);                              │val ^= (val >> 17);                              │val ^= (val << 5);                               │return val;                                      │ .loop:}                                                  │   eor    w0, w0, w0, lsl #13
};                                                   │   eor    w0, w0, w0, lsr #17│   eor    w0, w0, w0, lsl #5
static float sine(float x) {                         │   ucvtf  s1, w0const float B = 4 / PI_F;                          │   fmov   s2, w9const float C = -4 / ( PI_F * PI_F);               │   fmul   s2, s1, s2return B * x + C * x * std::abs(x);                │   fmov   s3, w10
}                                                    │   fadd   s3, s2, s3
static float cosine(float x) {                       │   fmov   s4, w11return sine(x + (PI_F / 2));                       │   fmul   s5, s3, s3
}                                                    │   fmov   s6, w12│   fmul   s5, s5, s6
/* Map degrees [0;UINT32_MAX) to radians [0;2*pi)*/  │   fmadd  s3, s3, s4, s5
float DEGREE_TO_RADIAN = (2 * PI_D) / UINT32_MAX;    │   ldp    s6, s4, [x1, #0x4]│   ldr    s5, [x1]
void particleMotion(vector<Particle> &particles,     │   fmadd  s3, s3, s4, s5uint32_t seed) {                 │   fmov   s5, w13XorShift32 rng(seed);                               │   fmul   s5, s1, s5for (int i = 0; i < STEPS; i++)                     │   fmul   s2, s5, s2for (auto &p : particles) {                        │   fmadd  s1, s1, s0, s2uint32_t angle = rng.gen();                       │   fmadd  s1, s1, s4, s6float angle_rad = angle * DEGREE_TO_RADIAN;       │   stp    s3, s1, [x1], #0xcp.x += cosine(angle_rad) * p.velocity;            │   cmp    x1, x16p.y += sine(angle_rad) * p.velocity;              │   b.ne   .loop}                                                  │
}                                                    │

优化后:

void particleMotion(vector<Particle> &particles, uint32_t seed1, uint32_t seed2) {XorShift32 rng1(seed1);XorShift32 rng2(seed2);for (int i = 0; i < STEPS; i++) {for (int j = 0; j + 1 < particles.size(); j += 2) {uint32_t angle1 = rng1.gen();float angle_rad1 = angle1 * DEGREE_TO_RADIAN;particles[j].x += cosine(angle_rad1) * particles[j].velocity;particles[j].y += sine(angle_rad1)   * particles[j].velocity;uint32_t angle2 = rng2.gen();float angle_rad2 = angle2 * DEGREE_TO_RADIAN;particles[j+1].x += cosine(angle_rad2) * particles[j+1].velocity;particles[j+1].y += sine(angle_rad2)   * particles[j+1].velocity;}// remainder (not shown)}
}

提示总是内联

对于 GCC 和 Clang 编译器,可以使用 C++11 的 [[gnu::always_inline]] 属性作为内联 foo 的提示,如下面的代码示例所示。对于较早的 C++ 标准,可以使用 attribute((always_inline))。对于 MSVC 编译器,可以使用 __forceinline 关键字。

循环优化

循环不变代码移动(Loop Invariant Code Motion,LICM)

大多数编译器可以自动进行。

for (int i = 0; i < N; ++i)             for (int i = 0; i < N; ++i) {for (int j = 0; j < N; ++j)    =>       auto temp = c[i];a[j] = b[j] * c[i];                   for (int j = 0; j < N; ++j)a[j] = b[j] * temp;}
循环展开

一定要注意循环展开不能引入依赖!!!

for (int i = 0; i < N; ++i)             for (int i = 0; i+1 < N; i+=2) {a[i] = b[i] * c[i];          =>         a[i]   = b[i]   * c[i];a[i+1] = b[i+1] * c[i+1];}

循环展开是一种众所周知的优化;尽管如此,许多人对此感到困惑,并尝试手动展开循环。我建议没有开发者应该手动展开任何循环。
首先,编译器在这方面做得很好,通常会非常优化地进行循环展开。第二个原因是,由于它们的乱序推测执行引擎(见[@sec:uarch]),处理器具有“嵌入式展开器”。当处理器等待第一次迭代中的长延迟指令完成时(例如加载、除法、微码指令、长依赖链),它会推测性地开始执行第二次迭代的指令,并且只等待循环携带的依赖项。这跨越了多个迭代,有效地在指令重排序缓冲区(ROB)中展开了循环。

循环强度降低

拿gcc测了一下,两个是一样的

for (int i = 0; i < N; ++i)             int j = 0;a[i] = b[i * 10] * c[i];      =>      for (int i = 0; i < N; ++i) {a[i] = b[j] * c[i];j += 10;}
循环取消开关

这个编译器优化不了
image.png

循环优化的高级主题

还有一类循环转换会改变循环的结构,通常会影响到多个嵌套循环。我们将探讨循环交换(Loop Interchange)、循环阻塞(Loop Blocking,也称为平铺或Tiling)、以及循环融合和分布(Loop Fusion and Distribution,也称为Fission)。这些转换的目的是改善内存访问,消除内存带宽和内存延迟的瓶颈。从编译器的角度来看,证明这类转换的合法性并证明它们的性能优势是非常困难的。在这个意义上,开发者处于一个更好的位置,因为他们只需要关心他们特定代码片段中转换的合法性,而不需要关心可能发生的每一种情况。不幸的是,这也意味着我们通常需要手动进行这样的转换。

循环交换

交换嵌套循环的顺序的过程。内循环中使用的累加变量切换到外循环,反之亦然。@lst:Interchange展示了交换i和j嵌套循环的例子。循环交换的主要目的是对多维数组的元素进行顺序内存访问。通过遵循元素在内存中布局的顺序,我们可以提高内存访问的空间局部性,使我们的代码更加友好于缓存。

循环分块

这种转换的思想是将多维执行范围分割成更小的块(块或平铺),以便每个块都能适应CPU缓存。如果一个算法处理大型多维数组并对其元素进行跨步访问,那么缓存利用率很可能很低。每一次这样的访问可能会将未来访问请求的数据推出缓存(缓存驱逐)。通过将算法分割成更小的多维块,我们确保循环中使用的数据在被重用之前一直留在缓存中。
循环阻塞是优化通用矩阵乘法(GEMM)算法的广泛已知方法。它增强了内存访问的缓存重用,并改善了算法的内存带宽和内存延迟。

// linear traversal                     // traverse in 8*8 blocks
for (int i = 0; i < N; i++)             for (int ii = 0; ii < N; ii+=8)for (int j = 0; j < N; j++)    =>      for (int jj = 0; jj < N; jj+=8)a[i][j] += b[j][i];                   for (int i = ii; i < ii+8; i++)for (int j = jj; j < jj+8; j++)a[i][j] += b[j][i];
循环展开和融合
for (int i = 0; i < N; i++)           for (int i = 0; i+1 < N; i+=2)for (int j = 0; j < M; j++)           for (int j = 0; j < M; j++) {diffs += a[i][j] - b[i][j];   =>      diffs1 += a[i][j]   - b[i][j];diffs2 += a[i+1][j] - b[i+1][j];}diffs = diffs1 + diffs2;

循环展开和融合可以在没有跨迭代依赖的情况下执行外循环,换句话说,内循环的两次迭代可以并行执行。此外,如果内循环的内存访问在外部循环索引(本例中的i)上是跨步的,那么这种转换也是有意义的,否则其他转换可能更适用。当内循环的迭代次数较低时,例如小于4,展开和融合特别有用。通过进行转换,我们将更多的独立操作打包到内循环中,从而增加了ILP。
展开和融合转换有时对于外循环向量化非常有用,而在编写本文时,编译器无法自动执行。在内循环的迭代次数对编译器不可见的情况下,它仍然可以向量化原始内循环,希望它执行足够多的迭代以触发向量化代码(下一节将详细介绍向量化)。但如果迭代次数较低,程序将使用慢速的标量版本循环。一旦我们进行展开和融合,我们允许编译器以不同的方式向量化代码:现在将内循环中的独立指令“粘合”在一起(也称为SLP向量化)。

使用polly

向量化

向量化的基础(https://www.yuque.com/treblez/qksu6c/nqe8ip59cwegl6rk?singleDoc#)不再赘述,我们来看不能向量化的情况。

向量化是非法的

https://weedge.github.io/perf-book-cn/zh/chapters/9-Optimizing-Computations/9-4_Vectorization_cn.html#VectIllegal
读后写依赖

void vectorDependence(int *A, int n) {for (int i = 1; i < n; i++)A[i] = A[i-1] * 2;
}

浮点数运算顺序问题(使用-ffast-math)

// a.cpp 无法向量化,因为((a + b) + c) != (a + (b + c))
float calcSum(float* a, unsigned N) {float sum = 0.0f;for (unsigned i = 0; i < N; i++) {sum += a[i];}return sum;
}

优化分支预测

使用查找表替换分支

image.png

用算数替换分支

用谓词替换分支

见https://www.yuque.com/treblez/qksu6c/gihyheqszownoouf

机器代码布局优化

使用likely和unlikely改变默认布局

比如说异常处理,应该用unlikely改变布局达到更好的局部性

基本块对齐

见https://weedge.github.io/perf-book-cn/zh/chapters/11-Machine-Code-Layout-Optimizations/11-4_Basic_Block_Alignment_cn.html#Loop_better

函数拆分

分割冷热代码
image.png

优化ITLB未命中

汇总

image.png

低延时优化

避免页错误

主要是缓存预热:

char *mem = malloc(size);
int pageSize = sysconf(_SC_PAGESIZE)
for (int i = 0; i < size; i += pageSize)
mem[i] = 0;

以及mlock:

#include <malloc.h>
#include <sys/mman.h>
mallopt(M_MMAP_MAX, 0);
mallopt(M_TRIM_THRESHOLD, -1);
mallopt(M_ARENA_MAX, 1);
mlockall(MCL_CURRENT | MCL_FUTURE);
char *mem = malloc(size);
for (int i = 0; i < size; i += sysconf(_SC_PAGESIZE))
mem[i] = 0;
//...
free(mem);

避免TLB驱逐

与基于 MESI 的协议和每个内核的 CPU 缓存(即 L1、L2 和 LLC)不同,硬件本身无法维护核心到核心之间的 TLB 一致性。因此,这项任务必须由内核通过软件来完成。内核通过一种特定的处理器间中断 (IPI) 来实现这个角色,叫做 TLB 驱逐,在 x86 平台上通过 INVLPG 汇编指令实现。
TLB 驱逐是实现多线程应用程序低延迟时最容易忽视的陷阱之一。为什么?因为在多线程应用程序中,进程线程共享虚拟地址空间。因此,内核必须在参与线程运行的内核的 TLB 之间通信特定类型的对该共享地址空间的更新。例如,常用的系统调用,如 munmap(可以禁用 glibc 分配器使用,参见 Section 12.2.1)、mprotect 和 madvise,会影响内核必须在进程的组成线程之间通信的地址空间更改类型。
如何检测多线程应用程序中的 TLB 驱逐?一种简单的方法是检查 /proc/interrupts 中的 TLB 行。一种检测运行时连续 TLB 中断的有用方法是在查看此文件时使用 watch 命令。例如,您可以运行 watch -n5 -d ‘grep TLB/proc/interrupts’, 其中 -n 5 选项每 5 秒刷新视图,而 -d 则突出显示每次刷新输出之间的差异。
image.png

缓慢的浮点运算

最后一级缓存敏感性

https://weedge.github.io/perf-book-cn/zh/chapters/12-Other-Tuning-Areas/12-7_Case_Study_-_LLC_sensitivity_cn.html

这篇关于HPC -- 基础理论的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/900084

相关文章

新一代车载(E/E)架构下的中央计算载体---HPC软件架构简介

老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节能减排。 无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事.而不是让内心的烦躁、焦虑、毁掉你本就不多的热情和定力。 时间不知不觉中,快要来到夏末秋初。一年又过去了一大半,成

流媒体与直播的基础理论(其一)

欢迎诸位来阅读在下的博文~ 在这里,在下会不定期发表一些浅薄的知识和经验,望诸位能与在下多多交流,共同努力 文章目录 一、流媒体简介二、流媒体协议常见的流媒体协议 三、视频直播原理与流程通用的视频直播模型视频直播链路 一、流媒体简介 流媒体是指将一连串的媒体数据压缩后,经过网上分段发送数据,在网上及时传输影音以供观赏的一种技术与过程,此技术使数据得以像流水一样发送;如果不

java中aop基础理论

AOP编程原理 什么是AOP: AOP 是软件设计领域中的面向切面编程,它是面向对象编程(OOP)的一种补充和完善实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。) AOP的应用: 实际项目中通常会将系统分为两大部分:核心关注点和非核心关注点 编程过程中首先要完成

GAMES104:10+11游戏引擎中物理系统的基础理论算法和高级应用-学习笔记

文章目录 概览一,物理对象与形状1.1 对象 Actor1.2 对象形状Actor Shape 二,力与运动2.1 牛顿定律2.2 欧拉法2.2.1 显式欧拉法Explicit (Forward) Euler’s Method2.2.2 隐式欧拉法 Implicit (Backward) Euler’s Method2.2.3 半隐式欧拉法 Semi-implicit Euler’s Met

知识图谱:【知识图谱基础理论(十)】——知识图谱的问题与挑战

信息抽取环节 开放域信息抽取,主要的问题包括实体抽取、关系抽取以及属性抽取。其中多语种、 开放领域的纯文本信息抽取问题是当前面临的重要挑战。 知识融合环节 实体消歧和共指消解的研究成果至今距离实际应用相差很大距离,如何在上下文信息受限条件下,如何准确地将从文本中抽取到的实体正确链接到知识库中对应的实体亟待解决。 知识加工环节 知识加工是知识图谱最具特色的功能,同时也是该领域最大的挑战。突

知识图谱:【知识图谱基础理论(九)】——知识图谱的应用

目前知识图谱技术主要用于智能语义搜索、移动个人助理(如Google Now,Apple Siri等)以及深度问答系统(如IBM Watson,Wolfram Alpha等)。 深度问答应用系统首先在知识图谱的帮助下对用户自然语言提出的问题进行语义分析和语法分析,进而将其转化成结构化形式的查询语言,然后在知识图谱中查询答案。对知识图谱的查询通常采用基于图的查询语言(SPARQL)。基于知识图谱的问

知识图谱:【知识图谱基础理论(八)】——知识更新

从逻辑上看,知识库的更新包括概念层的更新和数据层的更新。 更新的两种方式: 数据驱动下的全面更新增量更新

知识图谱:【知识图谱基础理论(七)】——知识加工

通过信息抽取和知识融合得到的事实表达并不等于知识,需经过知识加工最终获得结构化、网络化的知识体系。主要包括:本体构建,知识推理,质量评估。 本体构建 本体(ontology)是对概念进行建模的规范,是描述客观世界的抽象模型,以形式化方式对概念及其之 间的联系给出明确定义。 知识推理 从知识库中已有的实体关系数据出发,经过计算机推理,建立实体间的新关联,从而拓展和丰富知识网络。是知识图谱构建

HPC集群是什么?百度智能云CHPC给出答案

高性能计算(HPC)集群指运行高性能计算的节点集合,一个 HPC 集群可能包含数十到数千台计算节点。HPC 集群可以提供单节点不能提供的强大计算能力,拥有高性能、弹性扩展、稳定可靠等优点。HPC集群能够提供远超单个计算机的处理能力,因此非常适合需要大量计算资源和数据处理的任务。 本文将简单介绍集群涉及的基本概念和百度智能云 CHPC 的功能。 一. 集群类型 集群可以根据其部署和管理方式

分布式基础理论——CAP理论和BASE理论

文章目录 CAP 理论BASE 理论参考资料 CAP 理论 CAP定理(CAP theorem)指出,在分布式系统中,设计读写操作时只能同时满足以下三个特性中的两个: 一致性(Consistency) : 所有节点访问同一份最新的数据副本。需要强调的是,这里的一致性指的是线性一致性。可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者