首页
/ LevelDB 开发者指南:从打开数据库到性能调优的完整 API 实践

LevelDB 开发者指南:从打开数据库到性能调优的完整 API 实践

2026-09-05 09:34:23作者:裘旻烁

本文以 LevelDB 官方使用文档 doc/index.md 为主体,系统讲解这个由 Google 团队实现的持久化有序键值存储库的全部核心 API:打开/关闭数据库、Status 错误处理、读写与原子批量更新、同步写与持久化权衡、并发模型、迭代器、快照、Slice、自定义比较器、以及块大小/压缩/缓存/布隆过滤器等性能调优手段。读完后,你可以直接基于当前仓库的头文件(include/leveldb/db.hinclude/leveldb/options.h 等)写出可运行的 LevelDB 应用,并理解每个参数背后的实现依据。

LevelDB 是什么

LevelDB 提供一个持久化的键值存储(persistent key value store)。键和值都是任意字节数组;键按照用户指定的比较器(comparator)在存储中保持有序。当前仓库的版本常量定义在 db.h 中:kMajorVersion = 1kMinorVersion = 23

整个库的公共 API 都位于 include/leveldb/ 目录下:db.h(数据库主接口)、options.h(三组选项结构)、iterator.h(迭代器)、write_batch.h(批量写)、slice.h(字节切片)、status.h(状态码)、cache.hcomparator.henv.hfilter_policy.h 等。

打开数据库

一个 LevelDB 数据库有一个名称,对应文件系统中的一个目录,数据库的全部内容都存储在该目录中。以下示例展示如何打开一个数据库,必要时创建它:

#include <cassert>
#include "leveldb/db.h"

leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
...

这里涉及的两个开关在 Options 中都有定义和注释:

  • create_if_missing(默认 false):若目录不存在则创建数据库;
  • error_if_exists(默认 false):如果你希望在数据库已经存在时报错,在 DB::Open 调用前加上 options.error_if_exists = true;

从源码结构看,打开流程由 DB::Open 的静态接口进入,实现位于 db/db_impl.ccOpen 成功时向 *dbptr 写入堆分配的数据库指针,失败时置空并返回非 OK 的 Status。

独占锁机制:LevelDB 在打开数据库时会向操作系统申请文件锁以防止多进程误用。db_impl.cc 第 300 行附近即调用 env_->LockFile(LockFileName(dbname_), &db_lock_);锁文件名由 filename.cc 生成,就是数据库目录下的 LOCK 文件(dbname + "/LOCK"),它也是目录扫描时识别数据库类型 kDBLockFile 的依据(见 filename_test.cc)。因此同一时刻只有一个进程能打开同一个数据库。

Status 错误处理

上例中的 leveldb::Status 类型会被大多数可能出错的 LevelDB 函数返回。你可以检查结果是否 ok,并打印关联的错误信息:

leveldb::Status s = ...;
if (!s.ok()) cerr << s.ToString() << endl;

status.h 定义了完整的语义:

  • Status::OK() 表示成功,ok() 等价于内部 state_ == nullptr
  • 预置的错误工厂包括 NotFoundCorruptionNotSupportedInvalidArgumentIOError,分别对应内部代码 kNotFound=1kCorruption=2kNotSupported=3kInvalidArgument=4kIOError=5
  • 配套的谓词 IsNotFound()IsCorruption()IsIOError() 等可用于分支判断——例如 Get 在键不存在时返回的就是 Status::IsNotFound() 为真的状态;
  • ToString() 在成功时返回字符串 "OK",失败时返回带前缀的错误描述。

Status 内部以 new[] 出的字符数组承载(state_[0..3] 为消息长度、state_[4] 为代码、state_[5..] 为消息),拷贝构造会深拷贝该状态,可安全跨线程传递 const 方法。

关闭数据库

用完数据库后,直接删除数据库对象即可:

... open the db as described above ...
... do something with db ...
delete db;

注意 DB::NewIterator 的注释要求:返回的迭代器必须在该 DB 删除之前先删除("The returned iterator should be deleted before this db is deleted")。

读与写

数据库提供 PutDeleteGet 三个方法修改/查询数据。以下代码把 key1 下的值迁移到 key2:

std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);

三个接口均为纯虚方法,声明在 DB 中,并各自带有 "consider setting options.sync = true" 的注释提示。Get 的行为细节:命中则把值写入 *value 并返回 OK;键不存在则 *value 保持不变并返回 IsNotFound() 为真的状态。Delete 对不存在的键不算错误。

原子更新(WriteBatch)

注意上面"迁移值"的写法有一个隐患:如果进程在 Put(key2) 之后、Delete(key1) 之前崩溃,同一值会残留在多个键下。要避免这类问题,可以使用 WriteBatch 类原子地应用一组更新:

#include "leveldb/write_batch.h"
...
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
  leveldb::WriteBatch batch;
  batch.Delete(key1);
  batch.Put(key2, value);
  s = db->Write(leveldb::WriteOptions(), &batch);
}

WriteBatch 持有一组按加入顺序依次应用的编辑(write_batch.h 头部注释举例:对同一键依次 Put v1 / Delete / Put v2 / Put v3,最终值为 v3)。上面示例中先 DeletePut,是为了在 key1 与 key2 相同时不至于把值整个丢掉。

除原子性收益外,WriteBatch 还能加速批量更新——把大量修改放进同一批次一次 Write。实现上,批次的操作被序列化进 rep_(二进制表示,见 db/write_batch.ccdb/write_batch_internal.h),测试用例 db/write_batch_test.cc 覆盖了顺序语义与 Iterate 回放逻辑。

同步写与持久化权衡

默认情况下,LevelDB 的每次写是异步的:调用返回时只是把数据从进程内存推进了操作系统;从操作系统内存到持久存储的落盘是异步完成的。对某一次写设置 sync 标志可以让写操作"一路推到底"才返回:

leveldb::WriteOptions write_options;
write_options.sync = true;
db->Put(write_options, ...);

WriteOptions 的注释给出了精确的崩溃语义对照:

  • sync == false 时,语义类似 write() 系统调用;进程自身崩溃(机器未重启)不会丢数据,因为更新在写被判定"完成"之前已推入操作系统;但机器崩溃可能丢失最近的少量更新;
  • sync == true 时,语义类似 write() 后跟 fsync()。在 Posix 系统上,这通过调用 fsync(...)fdatasync(...)msync(..., MS_SYNC) 实现(对应 WritableFile::Sync())。

文档明确指出:异步写通常比同步写快一千倍以上,代价是机器崩溃可能丢失最后几条更新。两种缓解方案值得记入实战手册:

  1. 批量重载:向数据库灌入大量数据时,可依赖"崩溃后重跑批量加载"来兜底丢掉的更新;
  2. 混合策略:每第 N 次写使用同步写,崩溃后从上一次同步写完成的位置恢复(同步写可以同时更新一个"断点标记")。

此外,WriteBatch 提供了折中:把多条更新放进同一批次并整体做同步写,一次 sync 的额外开销被批次内的所有写均摊。从源码结构看,db->Writedb/db_impl.cc 中把 w.sync = options.sync 传给写批处理,并在第 1237-1248 行附近对 options.sync 为真时检查落盘错误并置 sync_error。测试侧,db/db_test.ccdata_sync_error_ / delay_data_sync_ 等原子标志模拟同步失败与延迟,验证了上述语义。

并发模型

  • 跨进程:一个数据库同一时刻只允许一个进程打开,LevelDB 通过向操作系统申请锁防止误用(即前述 LOCK 文件锁,见 db/filename.hLockFileName);
  • 进程内:同一个 leveldb::DB 对象可以被多个并发线程安全共享——不同线程可以无外部同步地向同一数据库写入、获取迭代器或调用 Get,LevelDB 内部自动完成所需的同步。DB 的类注释即声明 "A DB is safe for concurrent access from multiple threads without any external synchronization";
  • 其他对象需要自行加锁IteratorWriteBatchSliceStatus 等对象的 const 方法可无同步并发调用,但只要任一线程可能调用非 const 方法(如 Next()batch.Put()),共享该对象的线程必须使用自己的锁协议保护访问。这些约束逐一写在各头文件顶部注释中(如 iterator.hwrite_batch.h)。

迭代

以下示例打印数据库中全部键值对:

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  cout << it->key().ToString() << ": "  << it->value().ToString() << endl;
}
assert(it->status().ok());  // 检查扫描过程中发现的任何错误
delete it;

只处理 [start, limit) 范围内键的变体:

for (it->Seek(start);
   it->Valid() && it->key().ToString() < limit;
   it->Next()) {
  ...
}

也可以反向遍历(注意:反向迭代通常比正向略慢):

for (it->SeekToLast(); it->Valid(); it->Prev()) {
  ...
}

这六个核心方法 Valid / SeekToFirst / SeekToLast / Seek / Next / Prev 加上 key()value()status() 构成 Iterator 抽象接口,全部为纯虚函数。两个使用要点来自接口注释:迭代器初始处于无效状态,必须先调用某个 Seek 方法;key()/value() 返回的 Slice 所引用的底层存储只在迭代器下次修改前有效,需要长期保存请先 ToString()

快照(Snapshot)

快照提供对整个键值存储状态的一致只读视图ReadOptions::snapshot 非空时,读操作作用于该指定版本的 DB 状态;为空时,读操作作用于当前状态的隐式快照。

leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();
... apply some updates to db ...
leveldb::Iterator* iter = db->NewIterator(options);
... read using iter to view the state when the snapshot was created ...
delete iter;
db->ReleaseSnapshot(options.snapshot);

创建快照用 DB::GetSnapshot(),快照不再需要时必须DB::ReleaseSnapshot 释放,这允许实现丢弃仅为支持该快照读而保留的状态。Snapshot 是不可变对象,因此可被多线程无同步安全访问。ReadOptions 中三个字段(verify_checksumsfill_cachesnapshot)的语义在 options.h 中有完整注释。

Slice:轻量字节视图

it->key()it->value() 的返回值是 leveldb::Slice 的实例。Slice 是一个简单结构,包含一个长度和一个指向外部字节数组的指针——相比返回 std::string,它省去了拷贝(可能是很大的键/值)的开销。另外,LevelDB 的方法不返回以 null 结尾的 C 风格字符串,因为键和值中允许包含 '\0' 字节。

C++ 字符串和 C 风格字符串可以方便地转为 Slice:

leveldb::Slice s1 = "hello";

std::string str("world");
leveldb::Slice s2 = str;

Slice 也可以转回 C++ 字符串:

std::string str = s1.ToString();
assert(str == std::string("hello"));

生命周期陷阱:Slice 只是指针+长度(见 slice.h 中的 data_size_ 两个成员),调用方必须保证 Slice 使用期间其指向的外部字节数组仍然存活。以下写法有 bug:

leveldb::Slice slice;
if (...) {
  std::string str = ...;
  slice = str;
}
Use(slice);

当 if 语句块结束时 str 被销毁,slice 的底层存储随之消失,Use(slice) 变成悬垂引用。Slice 还提供 compare(三向字节比较)、starts_withremove_prefix 等便捷方法,均直接作用于 data_/size_,无拷贝。

自定义比较器(Comparator)

前面的示例都使用默认的键排序——按字节字典序。打开数据库时也可以提供自定义比较器。假设每个键由两个数组成,按第一个数排序、第一个数相同再按第二个数排:

class TwoPartComparator : public leveldb::Comparator {
 public:
  // Three-way comparison function:
  //   if a < b: negative result
  //   if a > b: positive result
  //   else: zero result
  int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
    int a1, a2, b1, b2;
    ParseKey(a, &a1, &a2);
    ParseKey(b, &b1, &b2);
    if (a1 < b1) return -1;
    if (a1 > b1) return +1;
    if (a2 < b2) return -1;
    if (a2 > b2) return +1;
    return 0;
  }

  // Ignore the following methods for now:
  const char* Name() const { return "TwoPartComparator"; }
  void FindShortestSeparator(std::string*, const leveldb::Slice&) const {}
  void FindShortSuccessor(std::string*) const {}
};

然后使用该比较器创建数据库:

TwoPartComparator cmp;
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
...

向后兼容规则

比较器 Name() 方法的返回值会在数据库创建时被持久化到数据库中,并在之后每次打开数据库时校验。如果名称变了,leveldb::DB::Open 调用会失败。因此,只有在新的键格式与比较函数和现有数据库不兼容、且可以丢弃所有现有数据库内容时,才应该改名。这一点在 Options::comparator 的注释里同样以 REQUIRES 形式强调;名称不一致的校验路径体现在版本描述(MANIFEST 记录)的解析中,db/version_edit.cc 在比较器名不匹配时报出 "comparator name" 错误。

如果计划好,键格式仍可渐进演化:例如在每个键的末尾存储一个版本号(一字节通常够用)。当想切换到新键格式时(比如给 TwoPartComparator 的键增加可选的第三部分):(a) 保持比较器名称不变;(b) 新键递增版本号;(c) 修改比较函数,根据键中的版本号决定如何解释键。

性能调优

性能可以通过修改 options.h 中定义的各类型的默认值来调整。当前仓库中 Options 的完整默认值一览(以源码为准):

参数 默认值 说明
comparator 字节字典序比较器 定义键的排序
create_if_missing / error_if_exists false / false 创建/存在性检查
paranoid_checks false 内部损坏的激进检查
env Env::Default() 环境抽象
info_log nullptr(写 DB 目录下的日志文件) 内部进度/错误日志
write_buffer_size 4 MB 内存中累积的未排序日志数据量
max_open_files 1000 数据库可使用的打开文件数
block_cache nullptr(内部自动创建 8 MB 缓存) 未压缩块缓存
block_size 4 KB(未压缩) 每块打包的用户数据量
block_restart_interval 16 键增量编码的重启点间隔
max_file_size 2 MB 切换新文件前写入的字节数
compression kSnappyCompression 块压缩算法(另有 kNoCompressionkZstdCompression
zstd_compression_level 1 zstd 压缩级别,支持范围 [-5, 22]
reuse_logs false 实验性:打开时追加复用现有 MANIFEST 和 log 文件
filter_policy nullptr 布隆过滤器等,减少磁盘读

块大小(Block Size)

LevelDB 把相邻的键组织到同一个块中,块是与持久存储之间传输的单位。默认块大小约为 4096 字节的未压缩数据(对应 block_size = 4 * 1024,且该参数可动态修改)。以批量扫描为主的应用可以考虑调大块;以小块点查为主的应用,若性能测量显示有效,可改用更小的块。块小于 1KB 或大于几 MB 意义不大;且压缩对更大的块更有效。

压缩(Compression)

每个块在写入持久存储前被独立压缩。压缩默认开启,因为默认压缩方法非常快,并且会自动对不可压缩数据关闭压缩。仅在基准测试证明有性能收益时才应完全禁用压缩:

leveldb::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....

CompressionType 枚举包含三个值:kNoCompression = 0x0kSnappyCompression = 0x1kZstdCompression = 0x2,注释特别提醒这些值是磁盘持久格式的一部分,不得改动已有条目。kSnappyCompression 的注释还给出量级参考:在 Intel Core 2 2.4GHz 上压缩约 200–500MB/s、解压约 400–800MB/s,显著快于大多数持久存储速度,因此通常不值得切换到 kNoCompression;即便输入不可压缩,Snappy 实现也能高效检测并切换到未压缩模式。

块缓存(Block Cache)

数据库内容以文件集合形式存储于文件系统,每个文件保存一串压缩块。若 options.block_cache 非空,它用于缓存常用的未压缩块内容;为空时 LevelDB 会自动创建并使用一个 8MB 的内部缓存。

#include "leveldb/cache.h"

leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(100 * 1048576);  // 100MB cache
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
... use the db ...
delete db
delete options.block_cache;

注意:缓存存放的是未压缩数据,应按应用层数据规模(不扣除压缩收益)来设定大小。压缩块的缓存交给操作系统缓冲区缓存或客户端自实现的 Env 负责。做批量读时,可以关闭缓存以免批量数据把缓存内容挤出:

leveldb::ReadOptions options;
options.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(options);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  ...
}
delete it;

LRU 缓存的实现位于 util/cache.cc,接口声明在 cache.h

键布局(Key Layout)

磁盘传输与缓存的单位是块,按数据库排序的相邻键通常落在同一块中。因此应用可以通过键布局优化性能:把经常一起访问的键放近,把很少用的键放到键空间的单独区域。文档给出的文件系统例子:要存储 filename -> 权限位、长度、file_block_id 列表file_block_id -> 数据 两类条目。可以给文件名键加前缀 '/',给 file_block_id 键加前缀 '0',这样只扫描元数据时就不必把笨重的文件内容拉进缓存。

过滤器(Filters)

由于 LevelDB 在磁盘上的数据组织方式,一次 Get() 可能涉及多次磁盘读。可选的 FilterPolicy 机制可以大幅减少磁盘读次数:

leveldb::Options options;
options.filter_policy = NewBloomFilterPolicy(10);
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
... use the database ...
delete db;
delete options.filter_policy;

上述代码给数据库关联了一个基于布隆过滤器的策略:每键在内存中保留若干位数据(示例传参 10,即每键 10 位)。该过滤器大约把 Get() 所需的非必要磁盘读减少 100 倍;每键位数越大,减少越多,代价是更多内存。文档建议:工作集装不进内存、且大量随机读的应用应设置过滤器策略。过滤器实现位于 util/bloom.cc,块级组织在 table/filter_block.cc,接口细节见 filter_policy.h

使用自定义比较器时,必须确保过滤器策略与比较器兼容。例如某比较器在比较键时忽略尾部空格,那么 NewBloomFilterPolicy 就不能用于它——应提供同样忽略尾部空格的自定义过滤器策略:

class CustomFilterPolicy : public leveldb::FilterPolicy {
 private:
  leveldb::FilterPolicy* builtin_policy_;

 public:
  CustomFilterPolicy() : builtin_policy_(leveldb::NewBloomFilterPolicy(10)) {}
  ~CustomFilterPolicy() { delete builtin_policy_; }

  const char* Name() const { return "IgnoreTrailingSpacesFilter"; }

  void CreateFilter(const leveldb::Slice* keys, int n, std::string* dst) const {
    // Use builtin bloom filter code after removing trailing spaces
    std::vector<leveldb::Slice> trimmed(n);
    for (int i = 0; i < n; i++) {
      trimmed[i] = RemoveTrailingSpaces(keys[i]);
    }
    builtin_policy_->CreateFilter(trimmed.data(), n, dst);
  }
};

高级应用也可以提供不使用布隆过滤器、而以其他方式归纳一组键的过滤器策略。

校验和(Checksums)

LevelDB 为存储在文件系统中的所有数据都关联校验和。有两个独立的开关控制校验和验证的激进程度:

  • ReadOptions::verify_checksums:设为 true 可对某次特定读取从文件系统读出的全部数据强制校验和验证。默认不做这种验证(默认 false,见 ReadOptions);
  • Options::paranoid_checks:打开数据库前设为 true,让实现在检测到内部损坏时尽快报错。错误可能在打开数据库时抛出,也可能在之后的某个操作中抛出,取决于损坏发生在数据库的哪一部分。默认关闭,这样即使持久存储的部分内容损坏,数据库仍可使用。

如果数据库损坏(例如打开 paranoid 检查后无法打开),可以用 leveldb::RepairDB 函数恢复尽可能多的数据。RepairDB 声明在 db.h,实现见 db/repair.cc:其头部注释描述了恢复流程——先把所有 log 文件转成 table,扫描每个 table 计算最小/最大键和最大序列号,然后重建描述文件(log number 置 0、next-file-number 取最大文件号+1、所有 table 文件都放入 level 0)。由于可能丢失部分数据,对重要数据库要谨慎使用。paranoid_checks 与校验相关行为有对应测试覆盖,可参考 db/corruption_test.ccdb/fault_injection_test.cc

近似大小(Approximate Sizes)

GetApproximateSizes 方法可用于获取一个或多个键范围占用的文件系统空间近似字节数:

leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
db->GetApproximateSizes(ranges, 2, sizes);

上述调用把 sizes[0] 设为键范围 [a..c) 占用的近似文件系统字节数,sizes[1] 设为 [x..z) 的近似值。Range 是左闭右开结构(start 包含在范围内,limit 不包含);GetApproximateSizes 的接口注释还提醒:返回的是文件系统空间(压缩十倍的数据只占十分之一),且结果可能不包含最近写入的数据大小。

环境(Environment)

LevelDB 实现发出的所有文件操作(以及其他操作系统调用)都经过一个 leveldb::Env 对象。高级客户端可以提供自己的 Env 实现以获得更细的控制,例如在文件 IO 路径中引入人为延迟来限制 LevelDB 对系统其他活动的影响:

class SlowEnv : public leveldb::Env {
  ... implementation of the Env interface ...
};

SlowEnv env;
leveldb::Options options;
options.env = &env;
Status s = leveldb::DB::Open(options, ...);

默认实现见 util/env_posix.cc(Posix)与 util/env_windows.cc(Windows)。仓库还提供了内存文件系统示例 helpers/memenv/memenv.cc 及其测试 helpers/memenv/memenv_test.cc

移植(Porting)

LevelDB 可以通过为 port/port.h 导出的类型/方法/函数提供平台特定实现来移植到新平台,更多细节见 port/port_example.h。此外,新平台可能还需要新的默认 leveldb::Env 实现,可参考 util/env_posix.cc(原文档写作 leveldb/util/env_posix.h,当前仓库中该实现位于 util/ 下)。

深入实现:相关文档

关于 LevelDB 实现的更多细节,可查阅仓库 doc/ 目录下的以下文档:

  1. 实现笔记——LSM 树整体架构、写入/读取/压缩流程;
  2. 不可变 Table 文件格式——数据文件、块、索引块、过滤块的字节级布局;
  3. 日志文件格式——写日志(WAL)的帧格式与 CRC。

配合 README.md 中的构建与测试说明(CMake 构建、GoogleTest 单元测试),以上材料可以支撑从 API 使用到格式细节的完整学习路径。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384