LZ AI NOTE · AI 公众号与知识站
TechNotes
TechNotes
简介:个人技术思考与总结
读 《埃隆 马斯克传》有感
擅长航天和电动车工程的人,也应该同样擅长AI和自动驾驶软件系统?
但本质上,这几类系统的难度类型完全不同。
航天与电动车属于“物理工程问题”:目标明确(能飞、能跑、成本更低),系统边界相对清晰,失败模式通常可定位、可复现,工程迭代路径也较稳定。
而AI与自动驾驶属于“开放世界概率问题”:系统需要在无限复杂的现实场景中保持安全与可靠,面对的是长尾风险、不可穷尽的边界条件,以及难以形式化验证的安全标准。即使整体表现很好,也可能因为极少数错误场景导致严重后果。
因此,差异不在于“工程能力强不强”,而在于问题性质:
硬件系统:收敛问题(优化已定义目标) AI系统:开放问题(目标与边界本身不完全确定)
这也解释了为什么同一组织在航天、电动车领域表现突出,但在AI与自动驾驶软件上仍可能面临持续挑战与人才流动。
MCP SSE 的缺陷
当前 MCP 基于的 sse 无状态通信还是有缺陷,会导致客户端多次访问丢失 session(多个 pod 负载均衡的时候很容易出现),还得往 websocket 方向演进
曾经前端热替换(HMR)有过类似的演变
为什么 mac 不再使用 CUDA?
核心原因:符合 Apple 的封闭生态技术路线,转向 Metal 让 Apple 能更好地优化自己的硬件与软件系统,而不依赖外部技术。
为什么 jest 需要 watchman?
- 原因:因为 jest 需要监听文件变化,而 watchman 是文件系统监听工具,更适合大型项目(所以 watchman 得崩溃可能会导致 jest 无法正常工作)
- watchman 的启动 :在第一次 run jest 时,会启动 watchman 持续监听文件变化
- watch 范围:历史启动过 jest 的目录都会纳入到 watchman 的监听范围,在 MAC 的活动监视器找到 watchman 打开的文件或者窗口看到
为什么 git 有了分支还需要 tag?
- 分支:一个指向提交的可变指针,随着你在分支上提交新的更改,分支会自动向前移动。这意味着分支是动态的、可变的,它们通常代表开发进度,例如 master、develop、feature/xyz 等分支名。
- 标签:一个指向特定提交的不可变的指针。标签通常用于标记代码的特定状态,比如一个版本发布(v1.0.0),而这个标签一旦打上,它就永久指向那个特定的提交,不会随时间变化。
Trace ID 三段 代表啥?
- e9e976fde12bc6745f1c51fa80652d01:000000788c34c719:0000000000000000
- 第一段全局唯一 Trace ID
- 第二段:spanId
- 第三段:parentSpanId
图像相似度
pixelmatch 图片相似度
简介:具有准确的抗锯齿像素检测(通过色差阈值不一定是差异,还可能是锯齿)和感知色差度量(YIQ )。
- Y 是最重要的信息,因为黑白电视只依赖亮度。
- I(橙蓝对比)次重要,它对人眼的视觉感知贡献较大。
- Q(紫绿对比)的重要性较低,因此可以被更大程度地压缩。 为啥有用
- 因为人眼对颜色的感知是非线性的,对不同颜色分量的敏感度不同。
- 即使两个像素在 RGB 空间中看似差异较大,通过 YIQ 空间测量后可能反映出的感知差异较小,从而避免错误地标记为显著不同。
- 将 RGB 颜色转换为 YIQ 后,可以根据人眼感知的差异调整颜色分量的权重。比较两张图片时,可以更加准确地反映视觉上的差异,而不是单纯的数值差异。
场景:专注于精确检测像素差异,常用于回归测试、差异图生成等场景,优先考虑性能。
结构相似性指标(structural similarity index,SSIM index)
简介:是一种用以衡量两张数字图像相似程度的指标。当两张图像其中一张为无失真图像,另一张为失真后的图像,二者的结构相似性可以看成是失真图像的图像质量衡量指标。
为啥有用
- 结构相似性的基本观念为自然图像是高度结构化的,亦即在自然图像中相邻像素之间有很强的关系性,而这样的关系性承载了场景中物体的结构信息。
- 人类视觉系统在观看图像时已经很习惯抽取这样的结构性信息。
场景:专注于结构相似性,常用于图像质量评估、图像压缩、图像增强等场景,优先考虑准确性。
识别深度伪造
英特尔公司宣布了一个叫做 FakeCatcher 的软件,号称可以查出某个视频是否为深度伪造,准确度高达96%。
它的原理是识别视频人物皮肤的静脉血液流动。如果是真人,血液时刻在体内循环,皮肤上的静脉就会有周期性的深浅变化,深度伪造的人就没有。
大数据已死?
大数据作为单一问题就不存在了,变成了海量存储和大型计算两个问题。 博客本周话题
尤雨溪解读2022 Web前端生态趋势
和 Svelte相比,Vue的 Reactivity Transform 和 Solid -labels 都属于统一模型,也就是他不受限于组件上下文,它可以在组建内使用,也可以在组建外使用,优势就是有利于长期的重构和复用
E2E 框架原理
- puppeteer,playwright 基于浏览器调试协议;偏底层,功能更丰富,但是跨浏览器一致性有限;
- 例如:playwright 跟 chrome 的交互是基于 chrome devtools 协议;
- 猜想:能够使用 playwright 实现的能力(例如 UI 的录制回放),也能直接开发对应的浏览器插件实现?
- selenium 基于 webdriver API;跨浏览器一致性,功能有限
CAP 定理
数据一致(Consistency)、高可用(Availability)、数据分区(Partition tolerance)
CAP 定理的核心观点是,在设计和运行分布式系统时,需要根据实际应用场景的需求,做出选择和权衡。例如,
- 如果一个系统必须保证数据的实时一致性,比如银行系统,那么它可能需要在网络分区时牺牲一部分可用性。
- 而如果系统更注重用户体验和服务的不间断性,如社交网络,可能会在网络分区时选择保持高可用性,同时允许暂时的数据不一致。
数据存储于可视化
- Prometheus + Grafana
- Elasticsearch + Kibana
elasticsearch vs 数据库
es 侧重于搜索(倒排索引,水平可扩展性更强),数据库侧重于存储(事务,一致性,复杂的关系模型)
内存对齐
空间换时间? 优势:对齐可以加快 CPU 访问速度 劣势:会浪费空间
符号表 2023-3-6
- wiki 符号表是一种用于语言翻译器(例如编译器和解释器)中的数据结构。在符号表中,程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址。
- Example Symbol_table#Example
- Go 编译器默认编译出来的程序会带有符号表和调试信息,一般来说 release 版本可以去除调试信息以减小二进制体积。
- ABI(Application binary interface): 约定数据在二进制流上的排列,还有 calling convention 等方式便于不同语种交互
- low-level, hardware-dependent vs API(high-level, hardware-independent,human readable format)
- 思考 有点类似 js 的 source-map?
风控引擎优化 2023-3-2
- 什么是风控?
input:风控参数,参数权重,参数组合判定逻辑
output:reject/pass
- 为什么需要优化?
风控的判定效率越高,对下游真实业务的阻塞也就越小
- 如何做?
- 参数获取:参数的获取可能渠道获取(redis/kafka/db 等)
- 并行
- 参数筛选排序拉取:根据业务数据使用权重,推迟小概率使用的参数拉取
- 逻辑组合
- 时序调整(调整逻辑执行先后顺序)
- 剪枝(编译阶段无效判定逻辑消除)
扩容机制 2023-3-2
需要处理好扩容跟收缩
-
栈空间(或者堆空间)策略
- 分段机制
- 旧秩序的延续
- 保留旧有数据空间,申请新空间存放溢出数据,通过链表连接各片段
- 连续机制
- 新秩序替换旧秩序
- 触发扩容会申请更大的空间(2倍?类似 golang 切片的动态扩容机制),把旧空间数据搬到新空间,并释放掉旧空间
- 优化:延迟归还给操作系统,自己管理内存的伸缩,例如:golang 的 goroutine go内存管理
- 分段机制
-
服务能力
- 横向:拓展更多集群服务
- 流量降低时会触发收缩
- 纵向:提升单体服务能力
- 横向:拓展更多集群服务
AOP(面向切面编程) 2023-3-1
- 解释:运行时动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
- 例子:js 的装饰器
- 场景:使用 AOP 将其从核心关注点中分离出来(实际是对执行过程进行代理重写)
- Node.js 日志log
- 埋点、数据上报
- 性能分析、统计函数执行时间
- 给ajax请求动态添加参数、动态改变函数参数
- 分离表单请求和验证
- 防抖与节流…
- 遭遇:golang 垃圾回收 三色标记法与读写屏障
- 摘要:AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。什么是面向切面编程 AOP?
逃逸分析
解释:在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。它涉及到指针分析和形状分析。
遭遇:在了解 golang reflect 的过程 “用于获取接口值 reflect.Value 的函数 reflect.ValueOf 实现也非常简单,在该函数中我们先调用了 reflect.escapes 保证当前值逃逸到堆上,然后通过 reflect.unpackEface 从接口中获取 reflect.Value 结构体;”
逃逸基础
- 不同于jvm的运行时逃逸分析,golang的逃逸分析是在编译期完成的。
- Golang的逃逸分析只针对指针。一个值引用变量如果没有被取址,那么它永远不可能逃逸。
- C 或者 C++ 是手动分配到栈或者堆上,后面是否会加上运行时逃逸分析
功能
- 逃逸分析辅助编译器优化编译结果
- 将堆分配转化为栈分配。如果某个对象在子程序中被分配,并且指向该对象的指针永远不会逃逸,该对象就可以在分配在栈上,而不是在堆上。在有垃圾收集的语言中,这种优化可以降低垃圾收集器运行的频率。
Reference
文章精读之设计原则/模式
设计原则:SOLID原则
- Single Responsibility Principle 单一原则;
- 一种Class只负责一类事情
- Open/Closed Principle 开闭原则;
- 允许在不改变已有代码前提下添加新功能
- Liskov Substitution Principle 里氏替换原则;
- 不要从可实例化的父类中继承(方法),而是要使用基于抽象类和接口的继承。
- Interface Segregation Principle 接口隔离原则;
- Clients should not be forced to depend upon interfaces that they do not use. 对于非必须的参数做可选配置,精简接口,简化使用
- Dependency Inversion Principle 依赖反转原则
- High-level modules should not depend on low-level modules.
- Both should depend on abstractions.Abstractions should not depend upon details. Details should depend on abstractions.
- 降低耦合,方便重构跟扩展
设计模式
Creational Patterns
- Fatory Method
- SingleTon
Behavioral Patterns
- Strategy
- Observer
- Iterator
Structural Patterns
- Adapter
- Decorator
- Proxy
设计原则 vs 设计模式
- 简洁理解:设计模式(语言相关)是对设计原则(语言独立)的实现
- 长文理解
- Design principles provide high level guidelines to design better software applications. They do not provide implementation guidelines and are not bound to any programming language.
- Design Pattern provides low-level solutions related to implementation, of commonly occurring object-oriented problems. In other words, design pattern suggests a specific implementation for the specific object-oriented programming problem.
Reference
- https://github.com/ryanmcdermott/clean-code-javascript#solid
- https://www.tutorialsteacher.com/articles/difference-between-design-principle-and-design-pattern
- https://github.com/sohamkamani/javascript-design-patterns-for-humans#behavioral-design-patterns
- https://github.com/lpxxn/rust-design-pattern
关于继承
谈到继承就会说到“类”,“类”封装的是数据,是对同一类数据实体以及其处理方法的抽象。
-
目的:
- 代码复用
- 多态(同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。实现方式:覆盖,重载;某种程度也算代码复用?)
- 覆盖:指子类重新定义父类方法,例如js的prototype
- 重载:多个同名但参数不同的方法,例如 Rust 函数模板
-
继承方式:
- 正统:C++,java
- 模拟:js原型链
- 无:Rust trait 组合实现多态 思考:Rust 的编程建模模式更像是组合(Composition) 而不是继承(Inheritance)?有点类似 Vue3 选择 Composition-API 代替 Class-Option?
多态图解:
Reference
关于面向对象
- 解释:面向对象的编程语言通常实现了数据的封装与继承并能基于数据调用方法。
- 有:js一切都是对象
- 无:Rust 不是面向对象的编程语言,但这些功能都得以实现。
React状态管理
- Redux
- 通过props注入 dispatch/state,规避了 useContext 方案可能导致的重复渲染
- Mobx (有点类似createContext + useContext + reactivity)
- 特征:Mobx + React 类似等于 Vue
- createContext + useContext + useReducer
- 坑:createContext 返回的 Provider 中 value变化会触发所有子组件重复渲染
- 关联思考:这种方案适用于小型的状态管理?
- Todo:了解重复渲染的运转机制
- 优化:对渲染做 useMemo
- 坑:createContext 返回的 Provider 中 value变化会触发所有子组件重复渲染
文章精读之前端框架设计
组件类型
- 纯展示型的组件,数据进,DOM出,直观明了
- 接入型组件,在React场景下的container component,这种组件会跟数据层的service打交道,会包含一些跟服务器或者说数据源打交道的逻辑,container会把数据向下*传递给展示型组件
- 交互型组件,典型的例子是对于表单组件的封装和加强,大部分的组件库都是以交互型组件为主,比如说Element UI,特点是有比较复杂的交互逻辑,但是是比较通用的逻辑,强调组件的复用
- 功能型组件,以Vue的应用场景举例,路由的router-view组件、transition组件,本身并不渲染任何内容,是一个逻辑型的东西,作为一种扩展或者是抽象机制存在
状态管理
如何管理将事件源映射到状态变化的过程,如何将这个映射的过程从视图组件中剥离出来,如何组织这一部分代码来提高可维护性,是状态管理要解决的本质问题
CSS方案
- css modules
- css-in-js
构建工具
解决的问题
- 任务的自动化
- 开发体验和效率(新的语言功能,语法糖,hot reload 等等)
- 部署相关的需求
- 编译时优化
Reference
CSS Animation,Transition 原理
-
GPU比较擅长于:
-
绘制位图到屏幕
-
重复的绘制同一个位图
-
在不同的位置,以不同的旋转角度,或者不同的缩放大小来绘制同一个位图。
-
-
GPU相对慢的地方:
- 将位图加载到显存里。
例子:
/*hover的时候height变化 100 -> 200的过程中重复计算元素当前位图然后回传给GPU绘制*/
div {
height: 100px;
transition: height 1s linear;
}
div:hover {
height: 200px;
}
/*
CSS的transform属性不会改变元素的布局,也不会影响到其周围的元素。它把元素当做一个整体看待——缩放整个元素、旋转整个元素或者移动整个元素。
所以位图只会在cpu跟GPU 之间传递一次,scale的重新绘制只发生在GPU,而不会阻塞主线程,也就是更加流畅?
*/
div {
transform: scale(0.5);
transition: transform 1s linear;
}
div:hover {
transform: scale(1.0);
}
总结
选择独立性比较强的CSS属性有助于改善动画效果:transform,opacity,filter (依赖于过滤器的复杂度和浏览器)
拓展:渲染线程分为 主线程 (main thread) 和 合成线程 (compositor thread)
Reference
Vue运行时错误处理
Vue源码里面lifeCycle/watch/directive/transition等的callback/hook等一般会被封装执行
const res = callWithAsyncErrorHandling(hook, target, type, args)
- 封装执行要素:
- fn: Function,
- instance: ComponentInternalInstance | null,
- type: ErrorTypes,
- 错误分类:LifecycleHooks | ErrorCodes(其他函数执行点:callback + hook等)
- 同步错误处理:尝试函数运行处理同步错误,并搜集返回值
- 异步错误处理:根据返回值是否Promise来处理异步错误
Reference
插件对比(Vue vs React)
插件
- Vue插件
// calls `MyPlugin.install(Vue)`
Vue.use(MyPlugin)
new Vue({
//... options
})
/**
*
* 适配 Vue3的 Composition-API场景
* 1. 在MyPlugin初始化阶段 app.provide(injectKey)注入实例
* 2. 在MyPlugin use阶段 app.inject(injectKey)获取实例
*
* Class 场景
* 1. 初始化阶段 把 MyPlugin 逐步 mixin 到子组件
* 2. 调用阶段直接在组件实例上获取
- React插件
// 例子
<Provider store={store}>
</Provider>
// 使用地方:一般需要对组件做二次封装进行connect,然后可以useDispatch,类似Vue inject?
插件的插件(Vuex vs Redux)
- Vuex插件
// 插件模板
const myPlugin = (store) => {
// called when the store is initialized
store.subscribe((mutation, state) => {
// called after every mutation.
// The mutation comes in the format of `{ type, payload }`.
})
}
// 插件使用
const store = createStore({
// ...
plugins: [myPlugin]
})
// 插件案例 vuex-persistedstate通过对mutation的拦截,对模块paths进行精准持久化
- Redux 中间件
// 插件例子
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
// 插件使用
import { createStore, combineReducers, applyMiddleware } from 'redux'
const todoApp = combineReducers(reducers)
const store = createStore(
todoApp,
// applyMiddleware() tells createStore() how to handle middleware
applyMiddleware(logger, crashReporter)
)
// 复杂案例 redux-persist
插件的插件的插件
- redux-persist的插件,或者称为 Transforms 例子
import createExpirationTransform from 'redux-persist-transform-expire';
const expireTransform = createExpirationTransform({
expireKey: 'customExpiresAt',
defaultState: {
custom: 'values'
}
});
persistStore(store, {
transforms: [expireTransform]
});
总结
Vue的mutable 比 React的immutable对数据的状态的操作来的直接,导致在状态处理插件模式也会不同;vuex(直接mutation改变数据)整体上比redux(通过dispatch action,走整个reducer流程返回新的state)更加直接,导致插件模式上vuex直接劫持mutation比redux劫持reducer来的更加简单
Reference
- https://vuex.vuejs.org/guide/plugins.html
- https://redux.js.org/understanding/history-and-design/middleware
- https://v2.vuejs.org/v2/guide/plugins.html?redirect=true
模拟LRU的数据结构(Set vs Array)
Set会自动追踪插入的先后顺序
- Set结构
let s = new Set()
s.add(3)
s.add(2)
// make this key the freshest
s.delete(3)
s.add(3)
// get Oldest
console.log(s.values().next().value); //2
- Array结构
let s = []
s.push(2)
s.push(3)
// make this key the freshest
s.splice(s.indexOf(3))
s.push(3)
// get Oldest
console.log(s[0])
ORM 模块组成
- ORM
- ORM
- Engine (open db, connect dialect and db_path)
- Session(prepare for next operation, open/close DB)
- Dialect
- dialect : db and dialect container
- sqlite3 : language type <- map -> db column type
- mysql …
- Schema (Parse a struct to a Schema instance)
- 静态语言(例如 golang):type struct(Modal) <- 反射 -> Table Fields
- 动态语言(例如 js):手动 Table Fields 创建 -> Table Fields
- Clause (SQL Builder)
- generator: keyword sql string(vars) generation
- clause : depend on generator, set/build final sql string
- Session ( SQL Execution )
- raw: raw sql execution
- table: table operation
- record: ORM Open API (Find/Select/Update/Delete/Where/Limit …) ,depend on Clause/clause & table & raw
- hooks: ORM Open API hook
- transaction: Atomicity/Consistency/Isolation/Durability
- migration
- ORM