Linux slab 分配器剖析

2023-11-18

http://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/

了解 Linux 内存管理的方式

良好的操作系统性能部分依赖于操作系统有效管理资源的能力。在过去,堆内存管理器是实际的规范,但是其性能会受到内存碎片和内存回收需求的影响。现在,Linux® 内核使用了源自于 Solaris 的一种方法,但是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存作为对象按照大小进行分配。本文将探索 slab 分配器背后所采用的思想,并介绍这种方法提供的接口和用法。

动态内存管理

内存管理的目标是提供一种方法,为实现各种目的而在各个用户之间实现内存共享。内存管理方法应该实现以下两个功能:

  • 最小化管理内存所需的时间
  • 最大化用于一般应用的可用内存(最小化管理开销)

内存管理实际上是一种关于权衡的零和游戏。您可以开发一种使用少量内存进行管理的算法,但是要花费更多时间来管理可用内存。也可以开发一个算法来有效地管理内存,但却要使用更多的内存。最终,特定应用程序的需求将促使对这种权衡作出选择。

每个内存管理器都使用了一种基于堆的分配策略。在这种方法中,大块内存(称为 )用来为用户定义的目的提供内存。当用户需要一块内存时,就请求给自己分配一定大小的内存。堆管理器会查看可用内存的情况(使用特定算法)并返回一块内存。搜索过程中使用的一些算法有first-fit(在堆中搜索到的第一个满足请求的内存块)和 best-fit(使用堆中满足请求的最合适的内存块)。当用户使用完内存后,就将内存返回给堆。

这种基于堆的分配策略的根本问题是碎片(fragmentation)。当内存块被分配后,它们会以不同的顺序在不同的时间返回。这样会在堆中留下一些洞,需要花一些时间才能有效地管理空闲内存。这种算法通常具有较高的内存使用效率(分配需要的内存),但是却需要花费更多时间来对堆进行管理。

另外一种方法称为 buddy memory allocation,是一种更快的内存分配技术,它将内存划分为 2 的幂次方个分区,并使用 best-fit 方法来分配内存请求。当用户释放内存时,就会检查 buddy 块,查看其相邻的内存块是否也已经被释放。如果是的话,将合并内存块以最小化内存碎片。这个算法的时间效率更高,但是由于使用 best-fit 方法的缘故,会产生内存浪费。

本文将着重介绍 Linux 内核的内存管理,尤其是 slab 分配提供的机制。

slab 缓存

Linux 所使用的 slab 分配器的基础是 Jeff Bonwick 为 SunOS 操作系统首次引入的一种算法。Jeff 的分配器是围绕对象缓存进行的。在内核中,会为有限的对象集(例如文件描述符和其他常见结构)分配大量内存。Jeff 发现对内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间。因此他的结论是不应该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)即可。后续的内存分配不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。

Linux slab 分配器使用了这种思想和其他一些思想来构建一个在空间和时间上都具有高效性的内存分配器。

图 1 给出了 slab 结构的高层组织结构。在最高层是 cache_chain,这是一个 slab 缓存的链接列表。这对于 best-fit 算法非常有用,可以用来查找最适合所需要的分配大小的缓存(遍历列表)。cache_chain 的每个元素都是一个kmem_cache 结构的引用(称为一个 cache)。它定义了一个要管理的给定大小的对象池。

图 1. slab 分配器的主要结构
图 1. slab 分配器的主要结构

每个缓存都包含了一个 slabs 列表,这是一段连续的内存块(通常都是页面)。存在 3 种 slab:

slabs_full
完全分配的 slab
slabs_partial
部分分配的 slab
slabs_empty
空 slab,或者没有对象被分配

注意 slabs_empty 列表中的 slab 是进行回收(reaping)的主要备选对象。正是通过此过程,slab 所使用的内存被返回给操作系统供其他用户使用。

slab 列表中的每个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分成一个个对象。这些对象是从特定缓存中进行分配和释放的基本元素。注意 slab 是 slab 分配器进行操作的最小分配单位,因此如果需要对 slab 进行扩展,这也就是所扩展的最小值。通常来说,每个 slab 被分配为多个对象。

由于对象是从 slab 中进行分配和释放的,因此单个 slab 可以在 slab 列表之间进行移动。例如,当一个 slab 中的所有对象都被使用完时,就从 slabs_partial 列表中移动到 slabs_full 列表中。当一个 slab 完全被分配并且有对象被释放后,就从slabs_full 列表中移动到 slabs_partial 列表中。当所有对象都被释放之后,就从 slabs_partial 列表移动到 slabs_empty 列表中。

slab 背后的动机

与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。

回页首

API 函数

现在来看一下能够创建新 slab 缓存、向缓存中增加内存、销毁缓存的应用程序接口(API)以及 slab 中对对象进行分配和释放操作的函数。

第一个步骤是创建 slab 缓存结构,您可以将其静态创建为:

struct struct kmem_cache *my_cachep;

然后其他 slab 缓存函数将使用该引用进行创建、删除、分配等操作。kmem_cache 结构包含了每个中央处理器单元(CPU)的数据、一组可调整的(可以通过 proc 文件系统访问)参数、统计信息和管理 slab 缓存所必须的元素。

kmem_cache_create

内核函数 kmem_cache_create 用来创建一个新缓存。这通常是在内核初始化时执行的,或者在首次加载内核模块时执行。其原型定义如下:

struct kmem_cache *
kmem_cache_create( const char *name, size_t size, size_t align,
                       unsigned long flags;
                       void (*ctor)(void*, struct kmem_cache *, unsigned long),
                       void (*dtor)(void*, struct kmem_cache *, unsigned long));

name 参数定义了缓存名称,proc 文件系统(在 /proc/slabinfo 中)使用它标识这个缓存。 size 参数指定了为这个缓存创建的对象的大小,align 参数定义了每个对象必需的对齐。 flags 参数指定了为缓存启用的选项。这些标志如表 1 所示。

表 1. kmem_cache_create 的部分选项(在 flags 参数中指定)
选项 说明
SLAB_RED_ZONE 在对象头、尾插入标志,用来支持对缓冲区溢出的检查。
SLAB_POISON 使用一种己知模式填充 slab,允许对缓存中的对象进行监视(对象属对象所有,不过可以在外部进行修改)。
SLAB_HWCACHE_ALIGN 指定缓存对象必须与硬件缓存行对齐。

ctordtor 参数定义了一个可选的对象构造器和析构器。构造器和析构器是用户提供的回调函数。当从缓存中分配新对象时,可以通过构造器进行初始化。

在创建缓存之后, kmem_cache_create 函数会返回对它的引用。注意这个函数并没有向缓存分配任何内存。相反,在试图从缓存(最初为空)分配对象时,refill 操作将内存分配给它。当所有对象都被使用掉时,也可以通过相同的操作向缓存添加内存。

kmem_cache_destroy

内核函数 kmem_cache_destroy 用来销毁缓存。这个调用是由内核模块在被卸载时执行的。在调用这个函数时,缓存必须为空。

void kmem_cache_destroy( struct kmem_cache *cachep );

kmem_cache_alloc

要从一个命名的缓存中分配一个对象,可以使用 kmem_cache_alloc 函数。调用者提供了从中分配对象的缓存以及一组标志:

void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );

这个函数从缓存中返回一个对象。注意如果缓存目前为空,那么这个函数就会调用 cache_alloc_refill 向缓存中增加内存。kmem_cache_alloc 的 flags 选项与 kmalloc 的 flags 选项相同。表 2 给出了标志选项的部分列表。

表 2. kmem_cache_alloc 和 kmalloc 内核函数的标志选项
标志 说明
GFP_USER 为用户分配内存(这个调用可能会睡眠)。
GFP_KERNEL 从内核 RAM 中分配内存(这个调用可能会睡眠)。
GFP_ATOMIC 使该调用强制处于非睡眠状态(对中断处理程序非常有用)。
GFP_HIGHUSER 从高端内存中分配内存。

kmem_cache_zalloc

内核函数 kmem_cache_zallockmem_cache_alloc 类似,只不过它对对象执行memset 操作,用来在将对象返回调用者之前对其进行清除操作。

kmem_cache_free

要将一个对象释放回 slab,可以使用 kmem_cache_free。调用者提供了缓存引用和要释放的对象。

void kmem_cache_free( struct kmem_cache *cachep, void *objp );

kmalloc 和 kfree

内核中最常用的内存管理函数是 kmallockfree 函数。这两个函数的原型如下:

void *kmalloc( size_t size, int flags );
void kfree( const void *objp );

注意在 kmalloc 中,惟一两个参数是要分配的对象的大小和一组标志(请参看 表 2 中的部分列表)。但是 kmallockfree 使用了类似于前面定义的函数的 slab 缓存。kmalloc 没有为要从中分配对象的某个 slab 缓存命名,而是循环遍历可用缓存来查找可以满足大小限制的缓存。找到之后,就(使用__kmem_cache_alloc)分配一个对象。要使用 kfree 释放对象,从中分配对象的缓存可以通过调用virt_to_cache 确定。这个函数会返回一个缓存引用,然后在 __cache_free 调用中使用该引用释放对象。

其他函数

slab 缓存 API 还提供了其他一些非常有用的函数。 kmem_cache_size 函数会返回这个缓存所管理的对象的大小。您也可以通过调用kmem_cache_name 来检索给定缓存的名称(在创建缓存时定义)。缓存可以通过释放其中的空闲 slab 进行收缩。这可以通过调用kmem_cache_shrink 实现。注意这个操作(称为回收)是由内核定期自动执行的(通过 kswapd)。

unsigned int kmem_cache_size( struct kmem_cache *cachep );
const char *kmem_cache_name( struct kmem_cache *cachep );
int kmem_cache_shrink( struct kmem_cache *cachep );

回页首

slab 缓存的示例用法

下面的代码片断展示了创建新 slab 缓存、从缓存中分配和释放对象然后销毁缓存的过程。首先,必须要定义一个 kmem_cache 对象,然后对其进行初始化(请参看清单 1)。这个特定的缓存包含 32 字节的对象,并且是硬件缓存对齐的(由标志参数SLAB_HWCACHE_ALIGN 定义)。

清单 1. 创建新 slab 缓存
static struct kmem_cache *my_cachep;

static void init_my_cache( void )
{

   my_cachep = kmem_cache_create( 
                  "my_cache",            /* Name */
                  32,                    /* Object Size */
                  0,                     /* Alignment */
                  SLAB_HWCACHE_ALIGN,    /* Flags */
                  NULL, NULL );          /* Constructor/Deconstructor */

   return;
}

使用所分配的 slab 缓存,您现在可以从中分配一个对象了。清单 2 给出了一个从缓存中分配和释放对象的例子。它还展示了两个其他函数的用法。

清单 2. 分配和释放对象
int slab_test( void )
{
  void *object;

  printk( "Cache name is %s\n", kmem_cache_name( my_cachep ) );
  printk( "Cache object size is %d\n", kmem_cache_size( my_cachep ) );

  object = kmem_cache_alloc( my_cachep, GFP_KERNEL );

  if (object) {

    kmem_cache_free( my_cachep, object );

  }

  return 0;
}

最后,清单 3 演示了 slab 缓存的销毁。调用者必须确保在执行销毁操作过程中,不要从缓存中分配对象。

清单 3. 销毁 slab 缓存
static void remove_my_cache( void )
{

  if (my_cachep) kmem_cache_destroy( my_cachep );

  return;
}

回页首

slab 的 proc 接口

proc 文件系统提供了一种简单的方法来监视系统中所有活动的 slab 缓存。这个文件称为 /proc/slabinfo,它除了提供一些可以从用户空间访问的可调整参数之外,还提供了有关所有 slab 缓存的详细信息。当前版本的 slabinfo 提供了一个标题,这样输出结果就更具可读性。对于系统中的每个 slab 缓存来说,这个文件提供了对象数量、活动对象数量以及对象大小的信息(除了每个 slab 的对象和页面之外)。另外还提供了一组可调整的参数和 slab 数据。

要调优特定的 slab 缓存,可以简单地向 /proc/slabinfo 文件中以字符串的形式回转 slab 缓存名称和 3 个可调整的参数。下面的例子展示了如何增加 limit 和 batchcount 的值,而保留 shared factor 不变(格式为 “cache name limit batchcount shared factor”):

# echo "my_cache 128 64 8" > /proc/slabinfo

limit 字段表示每个 CPU 可以缓存的对象的最大数量。 batchcount 字段是当缓存为空时转换到每个 CPU 缓存中全局缓存对象的最大数量。shared 参数说明了对称多处理器(Symmetric MultiProcessing,SMP)系统的共享行为。

注意您必须具有超级用户的特权才能在 proc 文件系统中为 slab 缓存调优参数。

回页首

SLOB 分配器

对于小型的嵌入式系统来说,存在一个 slab 模拟层,名为 SLOB。这个 slab 的替代品在小型嵌入式 Linux 系统中具有优势,但是即使它保存了 512KB 内存,依然存在碎片和难于扩展的问题。在禁用CONFIG_SLAB 时,内核会回到这个 SLOB 分配器中。更多信息请参看 参考资料 一节。

回页首

结束语

slab 缓存分配器的源代码实际上是 Linux 内核中可读性较好的一部分。除了函数调用的间接性之外,源代码也非常直观,总的来说,具有很好的注释。如果您希望了解更多有关 slab 缓存分配器的内容,建议您从源代码开始,因为它是有关这种机制的最新文档。 下面的参考资料 一节提供了介绍 slab 缓存分配器的参考资料,但是不幸的是就目前的 2.6 实现来说,这些文档都已经过时了。

参考资料

学习

获得产品和技术

  • 获取免费的 SEK for Linux,共包含两张 DVD,其中有用于 Linux 的最新 IBM 试用版软件,包括 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。
  • 利用可直接从 developerWorks 的 IBM 软件下载资源中心 下载 IBM 试用版软件,在 Linux 上构建您的下一个开发项目。

讨论


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux slab 分配器剖析 的相关文章

  • ext4 delay allocation之ext4_writepages页回写源码剖析

    概述 ext4 write写入pagecache之后 再合适的时机会回写到磁盘 ext4文件系统中是通过ext4 writepages写入磁盘 本来将在源码角度分析该过程 建议先参照ext4 delay allocation数据结构 ngi
  • 红黑树学习

    红黑树的是一种特殊的二叉搜索树 有如下性质 性质1 节点是红色或黑色 性质2 根是黑色 性质3 每个叶节点是黑色的 性质4 每个红色节点的两个子节点都是黑色 从每个叶子到根的所有路径上不能有两个连续的红色节点 性质5 从任一节点到其每个叶子
  • chinaUnix中的linux源代码学习

    原文地址 http bbs chinaunix net thread 1930079 1 1 html 大家好 内核源码版对本版块的精华帖进行了分类汇总 所有的精华帖分为十大类 各个分类的精华帖相关信息分布在该贴2 11楼 每个分类各占1楼
  • PF_NETLINK应用实例NETLINK_KOBJECT_UEVENT具体实现--udev实现原理

    相对于linux来说 udev还是一个新事物 然而 尽管它03年才出现 尽管它很低调 J 但它无疑已经成为linux下不可或缺的组件了 udev是什么 它是如何实现的 最近研究Linux设备管理时 花了一些时间去研究udev的实现 udev
  • 使用Crash工具分析 Linux dump文件

    前言 Linux 内核 以下简称内核 是一个不与特定进程相关的功能集合 内核的代码很难轻易的在调试器中执行和跟踪 开发者认为 内核如果发生了错误 就不应该继续运 行 因此内核发生错误时 它的行为通常被设定为系统崩溃 机器重启 基于动态存储器
  • KVM内核代码结构

    KVM内核代码结构 因为KVM的源代码已经包含在了Linux的内核树中 因此我们只需直接从www kernel org下载代码即可 内核源码包打开较大 解开后目录结构大概是这个样子 涉及KVM的主要有两个目录 virt和arch x86 k
  • 第十六章PHY -基于Linux3.10

    下载地址 http download csdn net detail shichaog 8620701 16 1 PHY 本章和OSI模型中的物理层和数据链路层关系密切 在嵌入式SOC上 通常集成有ARM核和MAC控制器 以及增加数据传输带
  • linux的dirty page回写磁盘过程中是否允许并发写入更新page?

    概述 众所周知Linux内核write系统调用采用pagecache机制加速写入过程 避免write系统调用长时间block应用进程 用户态进程执行write调用的时候 内核只是将用户态buffer copy到内核的pagecache当中
  • linux内存管理

    原文链接 https blog csdn net wwwlyj123321 article details 128241134 一 内存管理简述 在Linux内核中 RAM会将其中一部分永远分配给内核 用来存放Linux内核源码以及一些静态
  • 虚拟文件系统 (VFS)-基于linux3.10

    引言 虚拟文件系统 VFS VirtualFileSystem 介于具体的文件系统和C库之间 其用提供一个统一的方法来操作文件 目录以及其它对象 其能够很好的抽象具体的文件系统 在linux上具体的文件系统主要分为三类 l 基于非易失性的存
  • bootloader详解

    一 bootloader介绍 bootloader是硬件在加电开机后 除BIOS固化程序外最先运行的软件 负责载入真正的操作系统 可以理解为一个超小型的os 目前在Linux平台中主要有lilo grub等 在Windows平台上主要有nt
  • Linux设备模型4 kobj_attribute

    在上文Linux设备模型 3 Kobject 实例中 我们介绍到如何使用default attribute Default attribute使用很方便 但不够灵活 比如上篇文章在Kobject一节中提到的那个例子 name和val这两个a
  • 第三章 套接字相关数据结构--基于Linux3.10

    本章是对socket通信过程中使用到的比较重要的据结构罗列和意义的阐述 在阅读其它层的代码前 先来看几个重要的数据结构 这几个数据结构贯串四层模型 3 1 socket对应的内核结构体 在用户空间使用socket 函数创建一个套接字 对应的
  • linux内核的 等待队列 使用方法,wait_queue_head_t,进程休眠

    当你在用户空间需要读写一大片数据的时候 这个就用上了 以下来自 http www yuanma org data 2006 1207 article 1916 htm 假设我们在 kernel 里产生一个 buffer user 可以经由
  • 手把手带你实现Linux内核编译步骤及配置

    linux 系统体系结构 linux kernel体系结构 arm有7种工作模式 x86也实现了4个不同级别RING0 RING3 RING0级别最高 这样linux用户代码运行在RING3下 内核运行在RING0 这样系统本身就得到了 充
  • 七种Linux设备驱动模型之——Device

    前言 Linux将所有的设备统一抽象为struct device结构 同时将所有的驱动统一抽象为struct device driver结构 这样设计之后就方便驱动开发工程师编写驱动 只需要将具体的设备包含struct device结构 具
  • 慢慢欣赏linux pud_offset解析

    typedef struct pudval t pud pud t gt typedef u64 pudval t dir表示L0页表索引的指针 指向PUD页表的基地址 define pud offset dir addr pud t va
  • 多线程(1):互斥锁

    leetcode 1114题 按序打印 给你一个类 public class Foo public void first print first public void second print second public void thi
  • Linux内核调度算法 -- CPU时间片如何分配

    内核在微观上 把CPU的运行时间分成许多分 然后安排给各个进程轮流运行 造成宏观上所有的进程仿佛同时在执行 双核CPU 实际上最多只能有两个进程在同时运行 大家在top vmstat命令里看到的正在运行的进程 并不是真的在占有着CPU哈 所
  • 内存文件系统提升磁盘性能瓶颈

    author skate time 2011 08 22 提升磁盘性能瓶颈 linux的内存文件系统 ramdisk ramfs tmpfs ramdisk 是块设备 在使用它们之前必须用选择文件系统将其格式化 并且调整文件系统大小比较麻烦

随机推荐

  • 流操作

    StreamReader 与 FileStream 的区别用法 有关StreamReader的内容参考http blog sina com cn s blog 796ffec50100te51 html StreamReader 旨在以一种
  • “互联网+”最可行的路径就是“社群+”

    互联网 最可行的路径就是 社群 36氪的朋友们 2016 04 05 17 04 文章摘要 商业正从物以类聚走向人以群分 编者按 本文作者 卢彦 原文来自微信公众号 互联网思维 ID webthinking 36 氪经授权转载 前不久阿里
  • 【C++】通讯录管理系统

    1 系统功能介绍与展示 2 创建项目 3 菜单功能 代码 封装函数显示该界面 如 void showMenu 在main函数中调用封装好的函数 include
  • jmeter批量上传图片, csv文件参数化——详细讲解

    我们在测试的过程中 需要把图片放到请求中 而且还需要不一样的图片 批量修改图片后缀 我们也可以吧文件生成我们想要的后缀 1 首先创建一个文本txt 在文本中输入 ren jpg png 然后把txt的文件 修改 成bat后缀的 进行双击 在
  • 关于Altium Designer PCB元器件的3D封装

    关于Altium Designer PCB元器件的3D封装 虽然Altium Designer为我们提供了丰富的元件封装库资源 但是 在实际的电路设计中电子元器件技术的不断更新换代 有些特定的元器件封装仍需要我们自行制作 另外 有时根据工程
  • python3 中 dict和list 效率比较

    import time def count time func def int time args kwargs start time time time 程序开始时间 res func args kwargs over time time
  • 给 Ubuntu 操作系统配置静态 IP

    针对 Ubuntu 22 04 3 操作系统的静态 IP 配置 一 查看初始的网络信息 查看网卡名称 ifconfig 查看网关信息 route n 二 编辑网络配置文件 编辑文件 配置文件的名称可能不一样 自己去 etc netplan
  • 手把手教你Modelsim仿真【2020.4版本】

    首先新建一个文件夹 test5 打开Modelsim 依次选择 File gt Change Directory 把目录选择到创建的 test5 文件夹 创建库 依次选择 File gt New gt Library 一般我们选择第三个 库
  • 软文营销如何吸引用户情感意识引起共鸣

    当今互联网营销铺天盖地 自网络开始普及后 网络营销发展速度非常快 然而 我们需要在众多营销中取胜就需要技术创新 不管平台如何变化 有一个要素仍然至关重要 写好广告文案 因为文案内容才是读者了解你的关键所在 今天178软文网小编将分享我们最好
  • 大模型开发:从数据挖掘到智能应用

    在当今的数字化时代 数据的生成和处理已经成为一项重要的战略任务 随着大数据技术的发展 企业 组织以及个人对海量数据的需求越来越大 这也就催生了数据挖掘算法模型开发的快速发展 数据挖掘算法模型开发是一种从大量数据中提取有价值信息的过程 这些信
  • 国服ps4如何修改服务器地址,国服ps4如何修改服务器地址

    国服ps4如何修改服务器地址 内容精选 换一换 本节介绍如何查看云服务器的mac地址 云服务器的mac地址不支持修改 登录Linux云服务器 执行以下命令 查看云服务器的mac地址 ifconfig查看MAC地址ifconfig登录Linu
  • 【3分钟速读】运营到底是干啥的?

    欢迎关注天善智能 我们是专注于商业智能BI 人工智能AI 大数据分析与挖掘领域的垂直社区 学习 问答 求职一站式搞定 作者 陈老师 在咨询行业打拼了10 年 在如何诊断经营问题 建立分析体系 解决专项问题上有超过30个大型项目积累与实战 天
  • 时间序列预测(一)基于Prophet的销售额预测

    时间序列预测 一 基于Prophet的销售额预测 小O 小H 有没有什么方法能快速的预测下未来的销售额啊 小H Facebook曾经开源了一款时间序列预测算法fbprophet 简单又快速 传统的时间序列算法很多 例如AR MA ARIMA
  • 创建对象的几种方式

    一 工厂模式 工厂模式解决了创建相似对象的问题 但却没有解决对象识别的问题 怎样知道一个对象的类型 function createPerson name age job var o new Object o name name o age
  • 走进大数据,感受大数据

    摘要 大数据时代已经到来 现阶段 我国大数据产业发展如何 大数据应用领域在哪 大数据价值在哪里 带着这些疑问 走进大数据 感受大数据带来的神奇魔力 关注作者 需要大数据学习视频资料 其他文章可以找到大师组织 2017年9月8日 腾讯董事会主
  • shapley和树的shap

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 shapley and shap Shapley交互阶的计算 关于树结构Shap的计算 Shapley交互阶的计算 首先一阶的计算公式 二阶 交互值 计算 三阶计算 一般式
  • Qt-窗口消息处理机制及拦截消息的五种方法

    Qt 拦截消息的五种方法 覆写框架类QCoreApplication的notify函数 这个函数将得到整个框架中所有窗口的消息 给框架类安装一个消息过滤器 QCoreApplication gt nativeEventFilter filt
  • java中创建类的数组

    例题 定义一个People类 要求如下 1 成员变量 name height weight 分别表示姓名 身高 cm 和体重 kg 2 构造方法通过参数实现对成员变量的赋初值操作 3 成员方法int check 该方法返回0 1 1 分别表
  • qt 串口粘包_Qt开发串口

    首先 在工程文件里面 QT serialport 在头文件里面 include 1 配置打开串口 QSerialPort myserial new QSerialPort this gt myserial gt setPortName CO
  • Linux slab 分配器剖析

    http www ibm com developerworks cn linux l linux slab allocator 了解 Linux 内存管理的方式 良好的操作系统性能部分依赖于操作系统有效管理资源的能力 在过去 堆内存管理器是