Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify the goals/aims of this project | 讨论并明确项目的目标 #46

Open
inkydragon opened this issue Jan 5, 2025 · 3 comments

Comments

@inkydragon
Copy link

inkydragon commented Jan 5, 2025

背景/上下文

我看了 Readme 然后发现并没有 libm 也就是 math.h 数学库相关的部分。
想了一些数学库实现相关的具体问题,我的感觉是 mlibc 项目的目标不太清晰,我读完之后还是有疑问。
因此打开这个 issue 讨论并明确想项目的目标。

关于 libm 部分的具体疑问

一些疑问:
(这个issue并不是来专门讨论这些问题的具体答案的,提出这些疑问是为了引发后续的讨论。当然我也很高兴能获得这些问题的答案)

  1. 完整的 libc 肯定是包含数学库以及浮点数环境的,目前 mlibc 还没有开始,这是否在路线图上?
    或者说libc各个部分的开发优先级是什么?
  2. C 语言由对应的标准,libc 通常会遵循他们, mlibc 准备使用哪个版本, C99, C11, C23?。
    具体到数学库来说,使用不同的版本需要实现的 API 也有所不同。
    我大概会建议 C11;C99有点旧了;C23 加了不少新的数学函数,其他 libc 也在实现中。

项目期望里提到了“为小资源系统设计”和“针对risc-v 32/64进行优化”。
我理解前一点是需要限制编译后生成的代码的大小,后一点暗示会使用结构特定的汇编进行优化。
具体到数学库上,是性能优先还是可移植性优先?

  1. "精度 - 性能 - 可移植性" 的优先级是怎么样的?

    • 性能 - 可移植性:使用标准 C 并避免使用汇编,这样可移植性就高。
      反之使用具体平台的汇编,或者特定编译器的内部函数以提高性能
    • 精度 - 二进制大小:主要涉及打表的问题,float64 的实现有很多是一个巨大的表驱动;或者是实现自己的 float128
    • 精度 - 性能:如果不是要求特别高的精度,这两点的平衡相对好处理:
      确定精度目标 0.5eps 还是 < 1eps,然后尽力优化性能。
  2. mlibc 使用的是 MIT,那么是否介意直接移植现有的 libm 实现,然后再进行优化?
    比如 musl-libc;或同为 MIT 的 https://github.com/JuliaMath/openlibm
    抑或是精确舍入的 CORE-MATH (MIT) https://core-math.gitlabpages.inria.fr

题外话:关于我的动机

我是 julia,openlibm 的维护者之一,最近我还在为 openlibm 添加龙芯的 CI 构建及测试支持。
JuliaMath/openlibm#313
但最近也是我在推动从 julia 中移除对 openlibm 的依赖。
JuliaLang/julia#56875

(移除 openlibm 不是由我发起的,经过了很多尝试。我这次的尝试能成功构建并通过测试,离合并不远了)
因为系统提供的 libm 已经足够好了,openlibm 已经完成了它最初的目标。
等将 openlibm 从 julia 移除之后,我大概会在 openlibm 上打开一个 issue 讨论一下项目的后续计划,
因为项目的最大(?大概)用户没了,进一步开发的动力就小了许多,当然保持现状继续维护是没有问题的。

我的一个私心是:

  • 如果 mlibc 不介意使用现有的代码,那么我推荐直接引入 openlibm
    • 我近期的开发计划:使用 musl 的libm测试集,提高代码覆盖率;逐步合并入 core-math 的正确舍入实现
  • 如果认为 openlibm 的代码来源复杂,则可以尝试移植 core-math,MIT 协议,从头编写。
    对于版权或实现有任何疑问都可以联系作者,他在在开源社区很活跃,你在 openlibm 的 issue 里也能看见他。
    据我观察 core-math 正在逐渐合并入各种 libc,包括但不限于 glibc,llvm-libc
  • 当然如果希望完全重新编写并消除任何潜在的版权隐患,
    那么我建议使用最新的 libm 构建/生成技术来重新开发(新旧相对于 fdlibm 来说)。
    例如:

关于项目目标的讨论

现在主页上的“期望”是

mlibc/README_zh.md

Lines 39 to 49 in 39a1341

### 我们的期望
● mlibc可以支持到多种嵌入式工具链,能够使用gcc(arm/risc-v)、甚至LLVM等编译器
● 为小资源系统设计,完美地支持到一些嵌入式实时操作系统(RT-Thread等)和裸机
● 针对risc-v 32/64进行优化,能够适配主流的一些RISC-V MCU
● 采用xmake和scons构建
● reserve

我用 openlibm 的目标作为对比:

OpenLibm is an effort to have a high quality, portable, standalone C mathematical library (libm). It can be used standalone in applications and programming language implementations.

The project was born out of a need to have a good libm for the Julia programming language that worked consistently across compilers and operating systems, and in 32-bit and 64-bit environments.

小结一下

  • 项目范围限定:openlibm 专注于 libm 也就是数学库函数
  • 可移植性:项目是 portable 可移植的,因此不使用平台相关的汇编
  • 精度(速度):high quality 关心精度,这里没有明确提到速度的问题
  • 支持的平台/工具链:支持多个编译器、平台架构
  • 项目大的目标(起因):明确提到 openlibm 为 Julia 服务。
    展开说是部分平台上系统 libm 有问题(msvcrt / win),使用 openlibm 提供比系统更好的实现。

一些可供讨论的问题

既然是讨论,我就多提出一些可以讨论的点,并提供我的观点抛砖引玉。
或许不一定能在短时间内达成共识,但至少指出了潜在的问题,提供了一个讨论的起点。

  1. 是出于什么原因/动机开发的 mlibc,或者说其他现有的 libc 有哪些问题,mlibc 又解决了哪些问题
  2. mlibc 是否打算实现完整的 libc,或者实现哪些子集,是否遵守 C 标准。
    鉴于 mlibc 已经明确提及针对于嵌入式目标,那么有些部分或许优先级会变低,或者干脆不提供实现。
    例如:https://github.com/embeddedartistry/libc 明确表示:“不提供完整的 C 标准库实现。相反,我们选择了对裸机嵌入式系统有用的函数子集。malloc 和 free 不包含在此库中”
    • 内存分配函数是否打算自行实现?这也是 musl 的弱点,一般会替换为其他的 malloc 库
    • libm 呢
    • locale.h, time.h
  3. 一个简单的 路线图/计划。
    比如:近期目标以能编译 XXXX 为主,优先实现需要的函数
  4. "为小资源系统设计" 具体是有多小,完整编译后 .a/.so 在什么量级?
    参考外部链接 1,musl 大概是 500k 的量级,而 glibc 是 2~8 MB 的量级
  5. 精确性 - 实现复杂度(或许还包含代码的可读性)
    举例:浮点数的准确打印,至少你 printf/strtof 的组合能够自洽。
    但精确的浮点打印或许需要 https://github.com/ulfjack/ryu ,大部分编程语言选择移植 ryu 或性能更高但更复杂的实现。
    当然嵌入式平台可以舍弃一些一致性/精度以换取实现的简单+更小的资源消耗
  6. 性能 - 可移植性
    我建议:目前以功能的正确性、完整性为主,暂时不考虑性能。
    可移植性明确偏向于 RiscV,即接受 RV 的汇编实现以提高性能,其他架构共享纯 C 的实现。
  7. 对贡献的要求。
    例如:是否接受相同许可证的代码移植,或者是兼容许可证的(MIT-Apache2)。或者要求重新编写干净的实现。
  8. 打算支持的平台和架构。目前的期望里提到了 RV 和裸机。
    那是否接受 x86_64 的支持,龙芯呢?
    我的建议:同第5点,除了必要的平台特定的汇编外(如 CRT0),实现一般是可移植的纯 C,短期内仅允许对 RV 平台的汇编级别优化
  9. 支持的编译器,目前明确提到 gcc 系,LLVM 只是在“甚至”中。
    一个现实的问题,当为此项目添加 CI 时,是否应该加入 clang。
    是否打算支持其他的嵌入式工具链,如 SDCC
  10. 或许可以和同类的嵌入式 libc 进行简要的比较,以突出 mlibc 的目标
  11. 欢迎补充

外部链接

  1. Comparison of C/POSIX standard library implementations for Linux
    经常被引用的 libc 实现比较,但数据应该很久没更新了,仅供参考。另外可以提供一些libc实现中可能的取舍点。
  2. C Library - OSDev Wiki
    一些开源的 libc 实现的比较
  3. A tale of two libcs
    这篇博客比较了 musl 和 glibc 关于 isalnum 函数实现的可读性比较。
  4. musl - Roadmap
    musl 的开发路线图,其中的 Open future goals 约等于 libc 中棘手的部分

一些协议友好的 libc

@BernardXiong
Copy link
Collaborator

👋 欢迎关注到mlibc

  • 不得不说,对于原本的mlibc来说,并没有考虑到太多的libm的部分,但如果有一套高质量的libm会非常欢迎;
    • 另外,libm应该在最终构建的时候是独立的libm.a,所以具体是否涉及到尺寸的问题,个人觉得关系目前不那么大;
  • mlibc的动机是为深度嵌入式系统考虑、逐步的,主因是newlib尺寸太大了 <当然目前mlibc vs newlib在有限资源的API占用上来说,似乎优势还不大>;
  • mlibc为深度嵌入式系统考虑,所以在helloworld例程目录下有不同平台的示例,当然也就粗略可以获得最终build出来的尺寸大小<因为API使用、验证并不完备,所以是粗略的>;
  • 深度嵌入式系统,比较典型的配置应该是:
    • 64kB Flash
    • 8kB/20kB SRAM
    • 例如青稞RISCV通用MCU CH32V103 系列
  • 许可证方面,MIT-Apache2兼容性许可证即可(不同的地方可以独立列出许可证情况),非常热烈欢迎贡献👋
  • 支持平台方面,优选RISCV、ARM、x86,当然其他平台也欢迎;
  • 暂时不考虑支持到SDCC,主要面向32位、64位MCU,而不是8位芯片;
  • 和其他libc的比较上,在计划中,不过可能需要先把mlibc做一定的完善和优化(包括资源占用的优化)

@BernardXiong
Copy link
Collaborator

从后续Roadmap来看,会考虑到以下几个阶段(及对应的feature规划,目前主要还处于拍脑袋阶段 😄 )

image

@unicornx
Copy link

基于本 issue 的讨论,我又提了一些问题,因为是 offline 讨论的,现将讨论的内容记录下来,以备忘。

问题 1:项目的定位。 issue #46 其实也在提这个问题。我看了一下并结合 mlibc 自己的 README 文件,感觉最基本的动机或者说需求还是和运行资源有关,譬如您回答 #46 问题中提到的以 CH32V103 为例,满足 64kB flash,8kB/20kB SRAM 的情况。是这样吗?

A(from @BernardXiong): 项目定位:面向资源紧凑型的小型libc库;

A(from @zhangjun1996): 我了解的这个项目的动机不仅仅是和运行资源有关,还为了统一RTT系统的宏定义和libc函数。因为当下因为rtt直接使用的各个编译器自带的libc和头文件,然后这些编译器的版本和平台非常乱,容易出现难以定位的bug,直接使用newlib,musl改过来的话我们没法审核一行行的代码,工作量比较大,所以需要一个为RTT服务的精简版的libc。

我了解的大概是这样。

首先是像newlib这些libc存在时间太长了,融合了很多不明不白的代码,出了问题就很不好查,newlib的代码臃肿问题在之前的实际使用中遇到过很多次。所以,最初设想的mlibc代码要少,出了问题也方便找出来。

问题 2:如果问题 1 中我们的理解正确,我们发现目前 mlibc 实际上还远不能满足以上要求(譬如 arm 下libmlibc.a 有点过于大了,113kB),而且缺乏测试框架,感觉离成熟还有距离。但是我们发现 #46 上提到的一个 https://github.com/embeddedartistry/libc 倒是看上去倒是实现得很小(x86 下只有 17kB,arm 的遇到交叉编译失败问题所以没有对比数据,但应该也是类似数量级),而且相对比较成熟(譬如完善的测试用例),只是还没有支持 RISC-V。所以我们感觉比较困惑的问题是,是否考虑过基于一些现有的成熟的资源进行开发,为啥要从头开始做一个新的 libc?

A(from @BernardXiong): 如果目前资源还偏大,这个是需要进行优化的部分了,目前应该还在功能这块,所以在资源占用优化上还没花心思;重新实现一份libc是因为目前的 libc 大多有挺多的历史包袱的,例如 newlib 有 newlib-nano,也有衍生的 picolibc,但这个历史包袱真的很重,不那么清晰;因为前面更多在功能这块,所以测试用例,文档这些都还不是特别成熟。。。嗯,我牵头的开源项目都会有些这类问题,先快速有份东西(能够用起来),然后优化(有一定优势方式的用起来),然后再逐步完善(大势已成,逐步完善细节。也许大势不成,也可能弃坑了)。

问题 3:同样和项目定位有关。针对 #46 中您回答的目标系统 CH32V103,满足 64kB flash,8kB/20kB SRAM 的情况。我理解这是将 mlibc 用于构建 RT-thread 标准版的内核,对吧?为了适应紧张的资源限制,mlibc 八成是要做裁剪的,就和 embeddedartistry/libc 的实现策略类似。可是我们在 您的第二个回复中(后续Roadmap)又提到要支持 RT-thread Smart?我理解 RT-Thread smart 的应用场景应该是针对比较高级的处理器吧,在这类处理器上的资源应该会宽松很多,但要 mlibc 支持的功能应该也会多些。是否需要区分对待?这个 mlibc 是不是会有两个版本?一个小一些,针对 CH32V103 这样的小 mcu,构建 RT-thread 标准版,一个大一些,针对高级点的处理器,构建 RT-thread smart。如果是这样,感觉我们的项目定位需要扩展,至少还要补充在高级 MCU 上的典型数据是什么样的。同时我们也需要总结一下 mlibc 针对不同场景下的 RTT,需要实现的 API 函数列表,这个由于没有测试用例覆盖,感觉也说不清楚我们到底要支持多少 c 库的函数 API。

A(from @BernardXiong): mlibc可以用于构建rt-thread rtos版本,但或许还不算那么完善,所以在这个过程中mlibc也是一个完善的过程。因为也包括rt-thread rtos实际上是一个很大的版本,见过编译出来好几M bin的rt-thread rtos版本,保罗万象,包括对POSIX完整的支持,文件,网络API接口,以及C++的完善支持。所以对于RT-Thread RTOS而言,mlibc只是一种option,支持到可配置也是一部分,也不排除rt-thread rtos会用其他的libc库。相反,rt-thread smart内核中的c库可能是一个相对小的c库(相对ch32v103这种会大,但相对在内核中要跑各种应用,还是算小)。例如目前rt-thread smart内核中使用musl libc库,是支持不到/支持不了pthreads api的。这个和linux内核中没有标准libc,但有一套klibc是类似的对比。所以从libc角度来说,rt-thread nano需要使用的libc最小,其次是rt-thread smart内核的libc,最复杂的是复杂功能的rt-thread rtos(例如要把一套手表系统用rt-thread rtos跑起来,甚至说包括多媒体音频,camera等这些)。目前的mlibc实现,应该顶多到rt-thread nano用的libc(不包含pthreads api的PSE51,或者说部分的PSE51),以及rt-thread smart的libc维度(PSE51 + 部分PSE52+部分PSE53)。复杂功能rt-thread rtos libc还远远没到(需要到PSE51 + PSE52 + 部分PSE53 + 部分PSE54)

问题 4:依旧和项目定位有关。在 #46 中的第二个回复中(后续Roadmap),提到要支持 smart 的用户态 libc 库,这个我理解将再次提高 mlibc 的功能要求。因为用户态的 c 库会更加像 glibc 或者 musl,而不是类似 问题 2 和 3 中是针对的内核构建。如果是针对用户态 libc 库,是不是应该直接用 musl 了?如果要 mlibc 也支持到这个地步,和问题 2 和 3 的目标初衷是否背离太大?最近周同学和我研究分析了一下 mlibc,有如下疑问想请教一下。这些问题参考了 #46 上的讨论,也是基于这个 issue 的进一步思考。

A(from @BernardXiong): 当要扩展到smart用户态libc时,实际上也不是一个全功能libc,而类似说,当要考虑到一些用户态系统服务时,做为一个简洁高效而存在的libc。通用性应用使用的还是musl libc,不可能在用户态替换到musl libc,这个API要求太宽,太全了。

这里面也包括为什么rt-thread smart需要一套libc,或者说需要一套内核态的libc。对于rt-thread smart来说,用户态选择的是musl libc(这点应该对于一些中型系统来说,应该大家都有一定共识,musl libc小巧,对接起来还算容易,提供和linux (glibc)的兼容性也非常不错),而在内核态目前选择的也是musl libc。但这个内核态的musl libc是不得已而为之。用newlib不太适合,主要是一些数据结构,宏定义,枚举值定义不一致。这样当用户态musl libc应用程序系统调用陷入到内核时,这个改动会很大 (当然包括目前也有一些这类用户态到内核态的适配改动,因为lwip socket相关的定义有些不一致)。所以目前是在内核中也使用了musl libc。但内核中使用musl libc是存在问题的,有些api类似于一个定时炸弹一样,例如说在smart内核中调用pthreads api时,铁定奔溃掉(本身来说,在内核中应该尽可能不应该这样调用的。但内核中链接的内核态musl libc也确确实实存在这样的api)。所以解决办法可以是什么样呢,那就 一些部分兼容musl libc定义,重新来实现一套libc。所以mlibc也就承担了一部分这样的角色了

问题 5:针对问题4,看你您的回答,是不是可以认为 mlibc 的 roadmap 应该不包括对 smart 的 用户态 libc 支持。我看在 后续 roadmap 上有个阶段是要支持到 smart 的用户态 libc 库。

A(from @BernardXiong): 可以这样认为的,不包括用户态的 libc, 用户态的 libc 非常重,要做全,那基本上就是 glibc,musl libc 的维度。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants