Project Icon

beaver

基于Elixir的MLIR开发工具集

Beaver是一个基于Elixir的MLIR开发工具集,旨在简化开发流程。它充分利用Elixir的SSA、模式匹配和管道操作符等特性,为MLIR提供直观可扩展的接口。Beaver支持快速的开发迭代,并致力于实现Elixir到原生/WASM/GPU的编译。此外,该项目还探索了在硬件加速环境下重新审视符号AI,为机器学习领域引入了新的技术栈。

海狸 🦫

软件包 文档 检查上游

用一些神奇的长生不老药来增强全能的蓝银龙! 🧙🧙‍♀️🧙‍♂️

动机

在使用MLIR的事实标准方式中,我们需要使用C/C++、TableGen、CMake和Python(在大多数情况下)。这里的每种语言或工具都有一些我们想要利用的功能和便利性。选择最流行和上游支持的解决方案没有任何问题,但是拥有构建基于MLIR项目的替代方法仍然是有价值的,或者至少值得尝试。

Elixir实际上可能是一个很好的MLIR前端选择。Elixir具有SSA、模式匹配、管道操作符。我们可以使用这些语言特性以自然和统一的方式定义MLIR模式和传递管道。Elixir是强类型的,但不是静态类型的,这使它成为快速构建原型以验证和探索新想法的绝佳选择。

在Beaver中构建一段IR:

Func.func some_func(function_type: Type.function([], [Type.i(32)])) do
  region do
    block _() do
      v0 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32)
      cond0 = Arith.constant(true) >>> Type.i(1)
      CF.cond_br(cond0, Beaver.Env.block(bb1), {Beaver.Env.block(bb2), [v0]}) >>> []
    end

    block bb1() do
      v1 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32)
      _add = Arith.addi(v0, v0) >>> Type.i(32)
      CF.br({Beaver.Env.block(bb2), [v1]}) >>> []
    end

    block bb2(arg >>> Type.i(32)) do
      v2 = Arith.constant(value: Attribute.integer(Type.i(32), 0)) >>> Type.i(32)
      add = Arith.addi(arg, v2) >>> Type.i(32)
      Func.return(add) >>> []
    end
  end
end

这是一个小例子,展示了如何在Beaver中定义和运行一个pass(带有一些单子魔法):

alias Beaver.MLIR.Dialect.Func

defmodule ToyPass do
  use Beaver.MLIR.Pass, on: "func.func"

  defpat replace_add_op() do
    a = value()
    b = value()
    res = type()
    {op, _t} = TOSA.add(a, b) >>> {:op, [res]}

    rewrite op do
      {r, _} = TOSA.sub(a, b) >>> {:op, [res]}
      replace(op, with: r)
    end
  end

  def run(%MLIR.Operation{} = operation) do
    with "func.func" <- Beaver.MLIR.Operation.name(operation),
          attributes <- Beaver.Walker.attributes(operation),
          2 <- Enum.count(attributes),
          {:ok, _} <- MLIR.Pattern.apply_(operation, [replace_add_op(benefit: 2)]) do
      :ok
    end
  end
end

~m"""
module {
  func.func @tosa_add(%arg0: tensor<1x3xf32>, %arg1: tensor<2x1xf32>) -> tensor<2x3xf32> {
    %0 = "tosa.add"(%arg0, %arg1) : (tensor<1x3xf32>, tensor<2x1xf32>) -> tensor<2x3xf32>
    return %0 : tensor<2x3xf32>
  }
}
""".(ctx)
|> MLIR.Pass.Composer.nested("func.func", [
  ToyPass.create()
])
|> canonicalize
|> MLIR.Pass.Composer.run!()

目标

  • 利用Elixir可组合的模块化和元编程特性,为MLIR提供简单、直观和可扩展的接口。
  • 以秒级完成编辑-构建-测试-调试循环。Elixir和Zig中的所有内容都是并行编译的。
  • 在MLIR的帮助下,将Elixir编译为本机/WASM/GPU。
  • 在硬件加速的世界中重新审视和重生符号AI。Erlang/Elixir有Prolog的根源
  • 为机器学习引入新的技术栈。
    • 高级:Elixir
    • 表示:MLIR
    • 低级:Zig

为什么叫做Beaver(海狸)?

海狸是一种增加生物多样性的伞护种。我们希望这个项目能够像海狸池塘成为许多其他生物栖息地那样,为其他编译器和应用程序提供支持。许多Elixir项目也使用动物名称作为它们的包名,这通常是为了提高人们对濒危物种的认识。要了解更多关于海狸对我们星球重要性的信息,请查看这篇国家地理文章

快速介绍

Beaver本质上是Erlang/Elixir上的LLVM/MLIR。看到两个成熟社区和四个子社区的交叉很有趣。以下是关于它们每一个的简要信息。

对于Erlang/Elixir分支

  • 用一句话向我解释这个MLIR

    MLIR可以被视为编译器的XML,而MLIR方言就像HTTP标准,为通用格式提供了现实世界的语义和功能。

  • 查看MLIR的主页

对于LLVM/MLIR分支

  • Elixir这种编程语言有什么好处?

    • 它被编译成Erlang并在BEAM(Erlang的VM)上运行。因此,它具有Erlang的所有容错和并发特性。
    • 作为一种Lisp,Elixir拥有Lisp语言的所有优点,包括卫生宏和基于协议的多态性。
    • Elixir有一个强大的模块系统来保存编译时数据,这允许库用户轻松调整运行时行为。
    • 最小化,很少的关键字。大部分语言都是用自身构建的。
  • 查看Elixir的官方指南

入门

安装

可以通过在 mix.exs 的依赖列表中添加 beaver 来安装这个包:

def deps do
  [
    {:beaver, "~> 0.3.9"}
  ]
end

.formatter.exs 中添加以下内容,可以让格式化工具正确处理 beaver 引入的宏

import_deps: [:beaver],

与 Beaver 相关的 Erlang 应用

LLVM/MLIR 是一个庞大的项目,围绕它构建的 Beaver 包含数千个函数。为了适当地发布 LLVM/MLIR 并简化开发流程,我们需要谨慎地将不同层级的功能拆分到同一个伞形项目下的不同 Erlang 应用中。

  • :beaver:Elixir 和 C/C++ 混合。
    • 顶层应用,提供高层功能,包括 IR 生成和模式定义。
    • MLIR CAPI 封装,通过解析 LLVM/MLIR CAPI C 头文件构建,以及一些中层辅助函数以隐藏 C 指针相关操作。这个应用会将加载的 MLIR C 库和管理的 MLIR 上下文添加到 Erlang 监督树中。该应用也使用 Rust,但主要用于 LLVM/MLIR CMake 集成。
    • 所有在标准 MLIR 方言中定义的操作,通过查询注册表构建。这个应用会以符合 Erlang 习惯的方式(如行为遵从)发布 MLIR 操作。
  • :kinda:Elixir 和 Zig 混合,从 MLIR C 头文件生成 NIF。仓库:https://github.com/beaver-lodge/kinda
  • :manx:纯 Elixir,Nx 的编译器后端。

使用和开发注意事项

  • 只有 :beaver:kinda 被设计为可以作为独立应用直接被其他应用使用。
  • :manx 只能与 Nx 一起工作。
  • 虽然 :kinda 是为 Beaver 构建的,但任何对打包 C API 感兴趣的 Erlang/Elixir 应用也可以利用它。
  • 命名空间 Beaver.MLIR 用于任何 MLIR 工具中通常预期的标准功能。
  • 命名空间 Beaver 用于仅存在于 Beaver 中的概念和实践,这些主要是作为一组宏提供的 DSL(包括 mlir/0block/1defpat/2 等)。实现通常在 Beaver.DSL 命名空间下。
  • 在 Beaver 中,Erlang 应用名称和 Elixir 模块名称之间没有严格的一致性要求。两个具有相同命名空间前缀的模块可能位于不同的 Erlang 应用中(这在 Beaver.MLIR 命名空间中经常发生)。当然,应避免重复定义具有相同名称的 Elixir 模块。

工作原理

要实现 MLIR 工具包,我们至少需要以下几组 API:

  • IR API,用于创建和更新 IR 中的操作和块
  • Pass API,用于创建和运行 Pass
  • Pattern API,用于声明特定操作结构的转换

我们借助 MLIR C API 实现 IR API 和 Pass API。既有从 C 头文件生成的低级 API,也有更符合 Elixir 习惯的高级 API。 Pattern API 借助 PDL 方言 实现。我们使用低级 IR API 将 Elixir 代码编译为 PDL。另一种看待这个的方式是,Elixir/Erlang 的模式匹配作为 PDLL 的替代前端。

设计原则

转换优于构建器

使用构建器模式构建 IR 在面向对象的编程语言(如 C++/Python)中很常见。 这种方法的一个问题是,编译器代码看起来与它生成的代码非常不同。 由于 Erlang/Elixir 本质上是 SSA 的,在 Beaver 中,MLIR 操作的创建非常声明式,其容器会用正确的上下文信息对其进行转换。通过这种方式,我们可以:

  • 保持编译器代码的结构尽可能接近生成的代码,减少噪音,提高可读性。
  • 允许不同目标和语义的方言引入不同的 DSL。例如,CPU、SIMD、GPU 都可以有针对其独特概念定制的专门转换。

一个例子:

module do
  v2 = Arith.constant(1) >>> ~t<i32>
end
# module/1 是一个宏,它会将 SSA `v2 = Arith.constant..` 转换为:
v2 =
 %Beaver.SSA{}
  |> Beaver.SSA.put_arguments(value: ~a{1})
  |> Beaver.SSA.put_block(Beaver.Env.block())
  |> Beaver.SSA.put_ctx(Beaver.Env.context())
  |> Beaver.SSA.put_results(~t<i32>)
  |> Arith.constant()

此外,使用声明式方式构建 IR,可以自然形成正确的支配关系和操作数引用。

SomeDialect.some_op do
  region do
    block entry() do
      x = Arith.constant(1) >>> ~t<i32>
      y = Arith.constant(1) >>> ~t<i32>
    end
  end
  region do
    block entry() do
      z = Arith.addi(x, y) >>> ~t<i32>
    end
  end
end

# 将被转换为:

SomeDialect.some_op(
  regions: fn -> do
    region = Beaver.Env.region() # 创建第一个区域
    block = Beaver.Env.block()
    x = Arith.constant(...)
    y = Arith.constant(...)

    region = Beaver.Env.region() # 创建第二个区域
    block = Beaver.Env.block()
    z = Arith.addi([x, y, ...]) # x 和 y 支配 z
  end
)

Beaver DSL 作为 MLIR 的高级 AST

Beaver SSA DSL 和 MLIR SSA 之间应该有一对一的映射。可以通过解析 MLIR 文本格式并将其转储为 Beaver DSL(本质上是 Elixir AST)来实现往返转换。这使得以更可编程和可读的方式轻松调试 IR 片段成为可能。

在 Beaver 中,无论是生成、转换还是调试,处理 MLIR 都应该使用同一种格式。

符合 Erlang/Elixir 习惯的高级 API

在可能的情况下,低级 C API 应该被封装为支持常见 Elixir 协议的 Elixir 结构。 例如,对 MLIR 操作的操作数、结果、后继、属性、区域的迭代应该在 Elixir 的 Enumerable 协议中实现。 这使得可以使用 Elixir 标准库和 Hex 包中丰富的函数集合。

Beaver 是编译器还是 LLVM/MLIR 的绑定?

Beaver 既是编译器也是 LLVM/MLIR 的绑定。它提供了一套用于构建编译器的工具和 API,同时也作为 LLVM/MLIR 的 Elixir 接口。Beaver 允许用户以 Elixir 风格使用 MLIR 的功能,并提供了额外的抽象和工具来简化编译器开发过程。 Elixir是一种为各种用途而构建的编程语言。在整个Erlang/Elixir生态系统中存在多个子生态系统。每个子生态系统看似互不相关,但在实际生产中它们实际上是相互补充的。举几个例子:

这些子生态系统都始于一个种子项目或库。Beaver应该发展成为一个用Elixir和MLIR构建的编译器子生态系统。

MLIR上下文管理

在调用高级API时,最好不要到处传递MLIR上下文。如果没有提供MLIR上下文,属性和类型获取器应该返回一个以MLIR上下文为参数的匿名函数。在Erlang中,所有值都是复制的,所以传递这些匿名函数是非常安全的。在创建操作时,这些函数将在操作状态中使用MLIR上下文被调用。通过这种方法,我们既实现了简洁性又实现了模块化,而不需要全局MLIR上下文。在Beaver中,接受MLIR上下文来创建操作或类型的函数通常被称为"创建器"。

开发

  1. 安装Elixir,https://elixir-lang.org/install.html
  2. 安装Zig,https://ziglang.org/learn/getting-started/#installing-zig
  3. 安装LLVM/MLIR
  • 选项1:使用pip安装

    python3 -m pip install -r dev-requirements.txt
    export LLVM_CONFIG_PATH=$(python3 -c 'import mlir;print(mlir.__path__[0])')/bin/llvm-config
    
  • 选项2:从源代码构建 https://mlir.llvm.org/getting_started/ 推荐的安装命令:

    cmake -B build -S llvm -G Ninja -DLLVM_ENABLE_PROJECTS=mlir \
      -DLLVM_TARGETS_TO_BUILD="host" \
      -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
      -DLLVM_ENABLE_ASSERTIONS=ON \
      -DLLVM_ENABLE_OCAMLDOC=OFF \
      -DLLVM_ENABLE_BINDINGS=OFF \
      -DCMAKE_BUILD_TYPE=RelWithDebInfo \
      -DCMAKE_INSTALL_PREFIX=${HOME}/llvm-install
    cmake --build build -t install
    export LLVM_CONFIG_PATH=$HOME/llvm-install/bin/llvm-config
    

    (可选)使用Vulkan:

    • 安装Vulkan SDK(需要全局安装),参考:https://vulkan.lunarg.com/sdk/home

    • 通过将以下命令添加到你的bash/zsh配置文件来设置环境变量:

      # 你可能需要在这里更改版本
      cd $HOME/VulkanSDK/1.3.216.0/
      source setup-env.sh
      cd -
      
    • 使用vulkaninfovkvia来验证Vulkan是否正常工作

    • 在LLVM CMake配置命令中添加-DMLIR_ENABLE_VULKAN_RUNNER=ON

  1. 开发和运行测试
  • 在同一目录下克隆此仓库和kinda

    git clone https://github.com/beaver-lodge/beaver.git
    git clone https://github.com/beaver-lodge/kinda.git
    
  • 确保LLVM环境变量设置正确,否则可能无法构建

    echo $LLVM_CONFIG_PATH
    
  • 构建并运行Elixir测试

    mix deps.get
    BEAVER_BUILD_CMAKE=1 mix test
    # 使用过滤器运行测试
    mix test --exclude vulkan # 使用此命令跳过vulkan测试
    mix test --only smoke
    mix test --only nx
    
  1. 调试
  • 设置环境变量以控制Erlang调度器数量,ERL_AFLAGS="+S 10:5"
  • 在LLDB下运行mix test,scripts/lldb-mix-test
  1. Livebook
  • 请使用Elixir 1.14并从GitHub源代码安装Livebook:

    mix escript.install github livebook-dev/livebook
    
  • 要在Livebook中使用Beaver,在源目录中运行:

    livebook server --name livebook@127.0.0.1 --home .
    
  • 在设置单元格中,将内容替换为:

    beaver_app_root = Path.join(__DIR__, "..")
    
    Mix.install(
      [
        {:beaver, path: beaver_app_root, env: :test}
      ],
      config_path: Path.join(beaver_app_root, "config/config.exs"),
      lockfile: Path.join(beaver_app_root, "mix.lock")
    )
    

发布新版本

更新Elixir源代码

Linux

Mac

  • 使用以下命令运行macOS构建:

    rm -rf _build/prod
    bash scripts/build-for-publish.sh
    
  • beaver-nif-[xxx].tar.gz文件上传到发布

生成checksum.exs

rm checksum.exs
mix clean
mix
mix elixir_make.checksum --all --ignore-unavailable --print

检查输出中的版本是否正确。

发布到Hex

BEAVER_BUILD_CMAKE=1 mix hex.publish

(可选)格式化CMake文件

python3 -m pip install cmake-format
cmake-format -i native/**/CMakeLists.txt native/**/*.cmake
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号