V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 2 页 / 共 16 页
回复总数  315
1  2  3  4  5  6  7  8  9  10 ... 16  
1. 通过链式调用来写,比如

z.Add(&x, &y)
.Mul(&z, &p)
.Div(&z, &q)

这样可以减少中间值的使用,还是比较直观的

2. 使用 dot import ,如果你能将代码作用域规划得非常清晰,可以做到

var i Int // big.Int
j := NewInt()

不推荐但是确实有用

3. 直接使用 ai 的 tab 补全,用注释写数学表达式,配合人工或者测试用例做 review 即可
42 天前
回复了 silvernoo 创建的主题 Android Android 开发现在心智负担如何
AndroidX 只是个支持库,楼主描述的问题核心是 gradle 这种构建系统复杂,与使用什么样的支持库没有关系。我不是专业的 Android 开发,只是经常做一些逆向或者开发个人用的工具,经验不一定靠谱,以下我说的仅供参考。

简单说,一劳永逸解决心智负担的方法就是学明白构建原理以及构建工具的使用方法。

我认为这个问题本质上就和 git 图形化插件一样,你对 git 越熟悉,用图形化插件就越不容易出错。假如对 git 理解不到位,用图形化插件就容易爆炸,最终很可能还是要回到命令行去排错。

构建本身就是个非常复杂的过程,可能很多专业开发写了很多年 Android 都没有尝试过手动构建,因为 IDE 隐藏了构建工具( gradle )的细节,构建工具又隐藏了构建过程的细节。当底层构建过程出错的时候,经过两层抽象之后暴露给开发者的信息就非常有限,容易让人摸不着头脑。

我们就用手动模拟 gradle 工作流程的方式,从最简单的开始一步一步说。首先要 Java 编译,所以要配置 jdk ,同时要配置依赖,这里依赖的来源可以是线上仓库,可以是本地引入等等。假如引入的依赖有 C 之类的原生库,就要引入 ndk 做交叉编译。这里就不深究了,假设直接用 CMake 构建工具完成了。

以上只是编译出各个组件,离完整的应用包还有距离。这里字节码要打包成 dex ,资源文件要用特定工具压缩,最终还要把各个模块再打包成 apk ,还要处理签名等等。

如果只看核心的构建过程并不是很复杂,构建 variants 、测试和缓存等等一系列功能不影响理解这个过程。因为 gradle 的配置本身是个 DSL ,如果你不理解 DSL 背后所代表的实际工作过程,想要通过修改 DSL 代码来排错 debug 就不现实。

在理解 gradle 的原理和 IDE 的使用方法的基础上,手动排错 debug 就不是特别难的事情。当然这个事情不绝对,有些项目时间跨度很长,而某些支持依赖没有语义化版本,对应的 gradle 配置可能无法向后兼容,甚至出现版本一换循环依赖解析失败的问题,这时候就要手工重构替换掉特定依赖了。
42 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@burtnonald2 #21

感谢总结,相对来说原帖不如后面回帖里讨论的东西有价值。
一般来说 SoC 层面没什么问题。上市前半年官方分支就开始加驱动代码,这样产品上市的时候,相关驱动才能赶上合并的时间节点。有问题的一般是键盘、触摸板、声卡(严格来说是 dac/adc )、摄像头、指纹以及 bios/ec 这些。

Intel 阵营这边,越是公版设计,越是丐版没什么外设的越能做到开箱即用,最典型的就是小米。

有个比较简单的方式,通过 https://linux-hardware.org/ 来查找,比如 https://linux-hardware.org/?probe=a81f6be044 看到检测到的硬件,以及对应的驱动状态。一般来说 works/detected 都没什么问题,failed 不代表就不行。当然这个数据库一定要有人上传数据才行,属于前人栽树的行为。如果没有的话,可以考虑去品牌线下店,用内核比较新的 linux live 引导,然后 hw-probe 上传一下。

随便说一下容易出现没驱动设备的解决方法:

- 键盘,一般是因为硬件上走了比较特殊的连接方式,比如键盘先连 ec ,ec 再连 ps2 这样,厂家可以通过 ec 拦截某些按键组合实现硬件上的功能调整。这个驱动不了的话一般只能等 fix 。

- 触摸板,比较新的触摸板有可能只是没适配,驱动多数是走 libinput 的,看看相同型号有没有其他设备支持的,有的话一般来说写个 quirks 文件就可以了。

- 声卡,如果是固件问题多数时间要等上游修,和上面触摸板 quriks 修正差不多。另一个常见的问题就是用了 XX 牌子扬声器,本身声卡驱动是没问题的,只是无法正确激活响应的 dac/adc ,也要等上游修。

- 摄像头,驱动不了的摄像头大多数是走了 IPU 而不是 usb 。目前 linux Intel IPU 支持也慢慢跟上来了。还是等修……

- 指纹,看厂家,基本没得修。因为厂家不提供驱动的情况下,指纹类设备就是黑盒,想做驱动只能逆向。指纹的驱动不在内核里,看 libfprint 的支持列表吧。

- bios/ec ,看厂家也看型号,现在来说做得比较好的是 thinkbook/thinkpad ,其他的都半斤八两。
44 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@xziar #18

Everything 快的原因是 ntfs 有个 USN Journal ,所有查询快的方法说到底都是提前写好了要查询的内容。

理论上可以把 Everything 用于非 ntfs 文件系统甚至是网络文件系统映射,那就就退化到了普通的索引再查询的机制上了,也就不会那么快。

当然作为一个 GUI 应用,Everything 在索引数据结构和 UI 架构上做得也很不错,你提到的场景我更推荐 linux 的 cboxdoerfer/fsearch 这个工具,文件系统无关,同时针对文件(条件)检索优化。

命令行的话需要持久化索引就用 locate 系列,不需要索引 fzf 也可以。
在编程语言 Null/Nil/Zero 设计取向这个问题上,我很难说清自己的偏好和立场。这个问题我也尝试去理解、思考过很多次,暂时的结论是,对语言设计者来说这是个技术或者取舍问题,对语言的使用者来说更倾向于是一个工程问题。



我就以 Go/Rust 为例做个对比。

Go 的设计是 make zero useful ,相当于扩大了 zero 的功能,减少了 null 的存在。这样做的好处是:

1. 避免了 c 或其他语言的 UBI 初始化前就使用的情况( go 设计受 c 的影响很大)
2. 简化了编译器开发,go 早期的编译器基本上是手搓的,没有 null 的话数据类型在内存的布局就很简洁
3. 直接支持组合,go 是 duck typing 和组合优于继承思想的产物,基础类型默认零值初始化那么组合类型也间接初始化了
4. 代码稍微简化一点,不太用写 null 判断了

缺点主要是两方面:

1. 零值有歧义
2. 引用类型的 nil 可能不够安全(运行时)

Rust 走的是完全内存安全的路线,所以设计上就没有传统的 Null ,所有 & 引用都永远不会是 Null 。

为了达到这个效果,Rust 强制开发者使用 Option<T> 处理可能的 Null ,同时代码上也要显式初始化变量。即便有默认零值,也是通过 Default trait 来显式指定的。

内存布局方面,因为要在数据本身之外存储是否为 Null 的标记,所以这个内存布局是相对复杂的。为了解决这个问题,Rust 引入了一个叫 Null Pointer Optimization 的优化,具体原理我不太清楚。

总体上代码编译慢和这些设计因素是分不开的。好处是确实不需要写 Null 判断了。

这两个语言在 nil 安全上是没有本质区别的,对 nil 解引用只能在运行时才能发现。而这两个语言都有 ffi 的需要,所以业务层面也是无法避免的。



所以设计起点比较高的语言,根本的目标都是想要优化 Null 处理逻辑,只是方法和路线不一样。

当涉及到具体的业务时,只要用到“数值代表状态”的逻辑,最终总要有代码做判定,区别只是这个代码发生在什么地方。原本这个逻辑和 Null/Zero 不相关,只是因为开发中 Null/Zero 最常用,所以才出现业务代码要判定 Null/Zero 的需求。

所以我的认知里,Null 应当是一个技术细节,而永远不应该是一个业务特性。特别是软件开发越来越强调网络交互而不是单机单体应用的时候,Null 就是个隐患。我更倾向于强制在业务层面上禁止 Null 的使用。但我这种想法实施起来难度很大,几乎稍微有点历史的数据库都是会区分 Null/Zero 的,没有办法禁止其他人使用 Null 特性。
44 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@hanyuwei70 #16

btrfs 文件系统损坏的话题我特意没有在正文里提,因为我这里无论笔记本还是 NAS 都是 raid1 配置,没有遇到过无法恢复的情况。实际上大部分单副本配置的 btrfs 在底层设备损坏的情况下是无法恢复的,我如果说 btrfs 很稳定会造成误导。



从原理上说,btrfs 和 ext4 这类的区别是:btrfs 不关心也不管理底层块设备的坏块映射,ext4 会有坏块列表。btrfs 认为底层 hdd/ssd 会自己管理坏块的重映射。但这并不能说明 ext4 的处理机制就更完善,实际上 ext4 只有在创建的时候才会初始化坏块列表,日后的使用过程中,只有出现 io 错误且用户主动标记才会更新这个坏块列表。而 btrfs (包括 zfs )这类都是根据数据校验信息来间接判定底层设备出现了问题。

上面这段话的推论是,在使用过程中如果底层存储设备发生了块损坏,btrfs/zfs/ext4 都能发现 IO 类损坏,同时 btrfs/zfs 还可以发现静默类( bit rot )损坏。但是在没有额外副本的情况下,btrfs/zfs/ext4 都没有修复能力。

所谓的修复,仅限于数据和文件系统的信息不一致的情况下,通过假设文件数据本身没有问题,来重建文件系统元数据,使其匹配数据本身。这个假设在存储密度越来越大的今天已经很难成立了,所以新的文件系统都转向了 file extent 级别的校验,而非块级别的校验。

这是存在于大量用户认知中的误区,认为底层设备坏了,运行个命令就能修复,这是不可能的。这也是我坚持 raid1 的原因,本质上它不是备份而是可用性手段,基于两个 ssd 发生相同数据映射的块同时损坏概率非常低这个假设,可以快速(自动)判断数据正确的那个副本,而不是必须要立即去其他备份副本提取。



假设现在有一个不开启 raid 默认配置的 btrfs ,文件系统元数据(目录结构、权限属性这些)是双副本的,单个文件的元数据( inode 和时间戳这些)因为是文件系统元数据的一部分,所以也是双副本的,只有文件数据本身是单副本的。

底层设备损坏有两种情况:

1. 主控坏掉了,多发生在 ssd 设备上
2. 部分物理块损坏,然后这个块可能对应着纯数据块,也可能对应着元数据,还可能同时对应着数据和元数据,甚至损坏量大的话,元数据的双副本也会损坏

前者的情况对于所有文件系统都一样,第二种情况这里可以简单分析一下。

- 如果只是数据块损坏了,它是系统文件可能就无法引导,普通文件的话就是坏掉了。这种情况下 btrfs 检测到,两份元数据是一致的,而数据校验信息和元数据对不上,就会非常明确地提示用户数据损坏,但是很显然 btrfs 是没有修复这个损坏数据的能力的
- 如果恰好只坏了某个文件的元数据一个副本,那文件系统是可以根据数据和它另一个元数据副本校验正确这个事实,来推断出前一个元数据副本损坏了,从而完成元数据副本的修复
- 如果大量物理块失效,数据和元数据都损坏了,就无从判断了

由于目前 ssd 的存储密度太高了,一旦发生损坏基本都是大量块同时,所以容易造成 btrfs 不稳定不安全的错觉。传统机械硬盘存储密度低,

我想表达的是,当出现明显的数据异常的时候,还假定数据本身没问题,想要文件系统去尝试修复,这个行为是错误的,在这个情况下 btrfs 之类的文件系统撂挑子反而是非常正确的行为。



由于我非常清楚这个底层的机制,同时坚持使用多硬盘的 raid ,所以即便 ssd 上有坏块,也被 btrfs 自动修复了。很可惜我没有遇到过主控好而颗粒坏的情况,没有相关的经验。在 NAS 的机械硬盘版本上,我是有遇到过硬盘损坏的,系统大量报错,然后因为硬盘自身坏了,btrfs 自动修复行为也反复失败导致降级。最终的结果就是我把坏的硬盘换掉,重新 balance 就好了,不会去尝试修复。我现在没有什么数据支撑,因为它没有坏过,所以我也确实不关心 btrfs raid1 在后台 scrub 的过程修复了多少错误。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@ndxxx #14

数据并没有完全本地化,有一部分冷备用的是公有云最便宜的一档,按目录价来说大概 100 元/TB/年,协议价会少一半吧,总体并不便宜。

公网环境就是普通的 300Mbps ,没有上到千兆。之前有因为移动合约拉过第二条,仅仅是作为备份用,没有真正做过双接入或者负载平衡的设计。

主要原因是我的大部分数据并非来源于 pt ,而是日常工作、生活的私有数据,所以不太依赖公网。

我的算力需求主要是编译 Android 系统(自己刷机用)和一些个人应用,另外是编译 linux 内核和一些 rust 工程。这类 cpu/ram 需求用抢占式实例非常便宜,大概几毛钱一小时。但是类似服务器转码 h264 h265 就不太合适,因为流量费用并没有折扣。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@totoro625 #7

物理上如果数据不能存在于单一存储池中,就必须要用物理手段对存储设备打标签。比如我之前只有六个盘位但有二十块盘的时候,只能把冷备盘打个 tree 结构用来检索了。后面就只说软件层面的检索问题。

Linux 有个 fsearch 和 everything 的原理比较接近,但它只能做元数据实际上也就是文件名的检索。我研究过 Spotlight 之类的系统级索引工具,提供内容检索的方式手段有两个,一个是增加特定数据格式的解析,比如 office 系列用的 xml 格式,二是应用程序主动适配系统的检索接口。

所以我缺少的只是基于内容的检索,涉及到元信息的检索可以用这些已有的工具。



我解决内容检索的思路是:首先明确我需要快速检索的内容有哪些(多数非我自己产生的内容可以不需要检索),然后确定我需要用什么方式来检索(就是建立哪些索引),最终就是构建我自己的索引数据库并开发配套的后台索引和检索工具。

我这样做的底层逻辑是,数据索引一定要在数据产生时就同时构建,类似于数据库,想要查得快,就一定要提前花时间写索引。我又是长期的 Linux 用户,生态上来说只能自己造轮子了。



数据库方面有三个选择,csv/sqlite/嵌入式 kv ,最终选择了 csv 。因为我想了一下,这个方式用 grep 之类的工具检索最简单,存储结构比 sql 灵活,也不需要额外依赖。同时 csv 基本只有追加和按行检索,数据量几十 MB 放内存也没什么性能问题。一条典型的数据大概是这样的:

XXXX.pdf, /path/to/pool/location, keyword1, keyword2, keyword3, ...

第一栏固定为文件名,用于检索到之后再去使用元信息定位文件本身。第二栏是我自己习惯的存储路径,人能看懂就行。后面全是关键词,有关键词就能匹配到。



基于这个数据结构,所谓的建索引就是根据文件内容手动或者尽可能自动生成关键词。后台索引就是基于 inotify 监听我关心的数据文件夹,每当有文件变动时,后台索引模块就根据文件格式去匹配我的索引规则脚本,执行脚本更新索引数据库。

例如项目代码之类的我就忽略掉了。纯文本类文档的处理脚本就是做个分词,去掉无意义的,然后保留五到十个高频关键词。pdf 类的不太好处理,我一般手动标记一下。

之前视频和图片类也不太好处理,大概从前年开始,我给图片加入了处理逻辑。原理就是用文生图的反向 CLIP 模型,通过图片来生成文字描述,同时 ocr 过一下提取一些文本。本身这个检索就是为我自己服务的,如果需要类似于人脸识别归类,这些放到 NAS 上面用专门的工具去做。

检索前端就是 dmenu 配合 fzf 去检索这个 csv 数据库,基本上任何一个 launcher 类应用都能胜任,也能用于命令行。通过串联脚本可以实现类似实时预览的高级功能。


总体来说这是个非常原生态、专注吃狗粮的做法。思路不难,实现难度也不高,只是实践起来有比较高的初次迁移成本。还是那句话,每个人的需求不同,没有标准化的普适方案。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@totoro625 #7

1. 我在 zfs/btrfs 上都不用 dedup 功能。有过一次人工去重,前后花了大概一个月吧……

2. 笔记本->m6->NAS 或者 笔记本->NAS 的数据流是单向的,严格来说不能叫同步,如果是双向的脑子就不够用了。我大概五六年前就不再使用类似 dropbox 的多节点双向同步盘方案,作为人来说很容易确认哪个设备上的副本是权威副本,但机器处理这种分布式同步问题是比较无力的。

3. 我用过退役的机架服务器,待机功耗就有 100w 左右,主要还是工艺太落后了吧,但也没到不能接受的程度。只是这个设备在家不好放,也没有好用的散热和静音方案。后面改用超微标准板型的,用主流设备就好很多。这个平台上主要是 hba 卡,网卡虽然也会增加基础待机功耗,功耗的大头已经转移到十多块盘本身上面了。所以我不再关心功耗问题,更关心长期维护的事情。

检索方案有点复杂,我单独开个回复来说。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@idontunderstand #4

如果电源瓦数低,可以直接限制 ssd 本身的功率。一般 ssd 有几个功率等级,高的话能到 8w 左右,限制到 2~4 档位就比较稳了。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@dilidilid #3

现在 zfs 扩容也方便了很多,在我开始用的时候还没学习到,走了不少弯路。

btrfs 的 raid 5/6 我没用过,因为几乎所有文档都说不稳定。

除了笔记本上因为盘位限制只能 raid1 ,一般 NAS 多盘位的情况下,只需要决定冗余程度(数据和 meta 副本数量),或者说能承受几个盘数据损失就够了。从数据恢复的角度上说,多条带不是很友好,多副本更灵活一些。

我没用 zfs 主要是它不在内核主线里。
45 天前
回复了 kuanat 创建的主题 NAS 全闪 NAS 的一些心得体会
@thetbw #8

苹果稍微有点不同,可以认为是强制开启 FDE 全盘加密,而主控在 SoC 里面,所以扩容是可以的,但主控和存储颗粒分离就会因为无法解密而丢失数据。
45 天前
回复了 eurstein 创建的主题 NAS 零刻 ME mini 6 盘位固态 NAS 值得入手吗
好不好关键看是不是匹配需求。这个价位的全闪 nas 对于手里有前些年淘汰下来 256g 512g 或者 1t 存储的用户比较友好。

以我个人经验来说,盘位越多越舒服,特别是全闪 nas 4/6 盘位几乎没有体积差别,而 hdd 能差 50%。体验改善这主要得益于软件进步,一方面存储卷可以无视底层硬件,老旧 ssd 也可以发挥余热,另一方面可以渐进升级,基于 file extent 的各种软 raid 可以以一块盘为单位做替换。比如新淘汰的 1t 可以替换掉系统原有的 256g 的盘,这个 rebalance 过程很快,灾难恢复也简单。
假如你很关心 bit rot ,然而硬件上又没有 ecc 内存,还有一个替代方案。

这个方案是利用 intel/amd cpu 自带的内存加密技术,intel 这边叫 Total Memory Encryption TME ,amd 叫作 Transparent Secure Memory Encryption TSME 。大概 intel 是在 11 代消费级 cpu 上开始支持,amd 大概 3000 系就有了,但是具体还要看主板 bios 是否有对应开启选项。

这个功能的原理是 cpu 内部在每次启动后自动生成一个 aes 密钥,对内存数据进行透明加密,单个加密块的大小为 512bits/64Bytes 。

当发生 bit rot 的时候,1 bit 的变化最多会造成 512bit 的数据变化,极大概率会造成 crash 或者用户可见的数据异常。
一点点产品经验:

对于一个有替代的产品来说,一定要想清楚痛点和痒点。痛点是用来吸引用户的,痒点才是用来创收的。

砍掉止痛的根基,就不要想止痒的事情了。
写的很好啊,有很多点我也有类似的经历。

我补充一点关于定责和复盘这些非技术的事情。因为这些年我做过一线、负责人和老板,所以各个角度都有体验。定责复盘,实际上可以分为负责人(项目、组织经理)向老板汇报、向团队成员复盘两个部分,只是经常放在一起,而且以前者居多。

我的建议是负责人要勇于承担责任,团队成员的失误就是负责人的失误。即使承担责任压力很大,也切忌甩锅。(开发、测试和运维团队之间可以适当相互帮忙背锅,分担一下压力。)从老板的角度上说,损失已经发生了,也不会非要把直接责任人找出来开掉。老板也需要台阶下,不然后续还怎么继续展开工作。从权责对等的角度上讲,如果一个团队总是不粘锅,那它就不重要,所以是极有可能最先被优化掉的那个。

作为负责人,向团队成员做技术复盘的时候,更要注意对事不对人。本质上换个人并不能解决根本问题,能解决问题的只有排除人为失误的可能。那种出事临时工背锅的思路是不利于带团队的,人心散了。
51 天前
回复了 tenzinjamyangzhs 创建的主题 Linux 为什么 fedora 的性能释放这么拉胯?
tuned-adm 看一下,切换到 latency-performance 之类的配置试试。这个东西替代了之前 power profile 。

另外 fedora 默认 selinux 开启可能会有一点影响,这个我隐约有个印象测试能差 5%,记不清了。
@shalingye #21

考虑先实现出来吧,就用系统的 api 这样可以覆盖所有情况,延迟和性能的问题先不去处理。

粗略估计 4k 120hz 10bit 无压缩每帧数据量在 30MB 左右,单帧时间 8.3ms ,单从数据量上来说,如果是通过某种网络协议传输是没有带宽压力的,主要困难是延迟,发送接收涉及编解码。obs 有个 ndi 实现(现在叫 DistroAV ),就是在一台设备上采集,然后通过网络将数据传输到另一台设备上进行编码,可以参考一下它去掉网络开销的延迟水平。



共享内存的路线,Linux 的实现方式大概是这样的:

1. Linux 宿主机内核驱动 virtio 实现了 IVSHMEM

2. Linux 虚拟机或者 windows 虚拟机也通过 virtio 的驱动模块启用 IVSHMEM

3. 虚拟机和宿主机通过指定相同的物理地址实现内存共享( IVSHMEM 本质是虚拟 PCI 设备)

4. 虚拟机内的特定应用(你要开发的)将 framebuffer 封装好后写入 IVSHMEM 的特定区域(手动管理)

5. (可选)宿主机应用通过 IVSHMEM 读取对应的 framebuffer 并做后续处理



这里可以看到 IVSHMEM 实际只是非常粗糙的共享内存机制,还需要在它的基础上实现用它完成 fb 数据交换。之前提到的 looking-glass 就是实现了一个叫 KVMFR 的模块,在 IVSHMEM 的基础上封装了一个用于 fb 数据交换的接口(硬件设备),同时实现了宿主与虚拟机之间的同步、锁,另外它用来做数据交换的格式是 dma-buf ,这样宿主机上的窗口合成器可以直接使用。之后,虚拟机的采集应用( obs/ffmpeg/系统采集)直接将数据写入 KVMFR 。

如果你要在 windows 实现类似的功能,需要把 vmbus 当作 IVSHMEM ,然后在上面实现一个 windows 版本的 KVMFR 。



估计这样表述应该就清楚了,由于我对 windows 不是特别了解,所以上面的方案不一定正确。
@kuanat #19
@shalingye #16

中间发不出来的一段是关于如何获取 framebuffer 的。通过系统 api 会有二次复制的延迟,通过显卡特定 api 更合适。
1  2  3  4  5  6  7  8  9  10 ... 16  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2647 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 25ms · UTC 13:01 · PVG 21:01 · LAX 06:01 · JFK 09:01
Developed with CodeLauncher
♥ Do have faith in what you're doing.