2023年难忘的书

新年快乐

2024到了, 瑞兔辞旧去,龙腾新年来, 祝新的一年事事顺心. 这里分享几本在2023年读过难忘的书.

书单

  1. The Inner Game Of Tennis: The Classic Guide To The Mental Side Of Peak Performance: 关于调整心态能让自己有最佳发挥的书. 作者以网球比赛举例子但实际上任何事都会有心态的参与. 首先, Relaxed Concentration是形容心态的最合适的水平. 注意力完全集中但是身体却是很放松的. 要达到这种水平, 有两个影响因素Self 1(后天训练得到的心态)以及Self 2(先天的心态). 不要让Self 2与Self 1分割, 并且不要盲目的依赖于Self 1. 培养nonjudgmental awareness. 停止评判Self 2. 客观的看待事物而不加修饰心态. 用身体和脑一起去学习, 让Self 1描绘出最好的结果, 然后配合Self 2一起去实现.

  2. 9 Out of 10 Climbers Make the Same Mistakes: Navigation Through the Maze of Advice for the Self-coached Climber : 关于学习技巧的书. 如果不是为了追求如何提高登山技巧的话, 读完第一章即可. 书中有句话说的太好不得不引用原文:

climbers are stuck on the basics, but lost in the details. - p7

climbers可以是要完成/精进任何一项事的我们大概率会犯的错 - 在信息爆炸的今天, 明明对于基础以及大框架的忽视, 但是缺扎在了细节海洋里面. 反思与总结才能理清楚什么是真正基础并且重要的. 此外, 减少百分之四的努力不会减少百分之四的产出, 减少百分之四的努力会减少百分之九十的产出.

  1. The 12 Week Year: Get More Done in 12 Weeks than Others Do in 12 Months: 如标题所示, 一本关于方法论的书. 要达到这本书所描述的效果, 前提是相信only actions lead results. 接下来这本书会指导如何action去达到results:
  1. 持续的action在重要的task上面.
  2. 减短反馈周期 (把一年定义为12 weeks而不是12 months)
  3. 制定明确并且可行动的目标 (首先是制定12 weeks的目标,之后划分到以周为行动单位)
  4. 衡量完成度并且打分 大体上就是缩短反馈周期, 主旨就是这样但是书中有很多操作细节.
  1. The Almanack of Naval Ravikant: A Guide to Wealth and Happiness: 第二次读了,绝对是一本每年值得读一本的书. 今年计划再读一次. 每一次读都有不一样的收获. 这本书覆盖面太广了,总而言之,这本书回答的是如何过更幸福快乐的人生. 是视频How to Get Rich内容整理成书.Naval是个值得深挖的人. 他的谈话和见解总是能直接了当的阐明本质, 并且能与实践有很直接的关联. Absolutely pure gold.

  2. 寻觅意义: 王教授在复旦上课的讲话总结. 相比于注重操作的西方哲学,国学往往被认为不具有实践意义或者说只是教化人的工具. 王教授解释了儒释道三家的实践意义. 无心而为,无为而无不为. 这本书肯定能带来一丝心灵的平和与宁静.

记录一次对v4l2相机驱动的系统优化

背景

最近有个需求需要重新优化供应商提供的相机驱动的代码, 来实现更低的cpu占用率以及更好的实时性. 同时要发布的消息里的时间戳是相机曝光时间戳并且是以unix时间戳为基准. 平台为Jetson Orin. 需要做localhost ipc来让下游应用使用. 相机原始默认编码是yuyv, 需要转换成bgr发送.

之前从来没做过linux驱动之类的开发, 也没研究过图像转码, 有因为别的需求看过旧版相机代码. 整个任务自己从调研到实现大概花了两周时间, 其中一周卡在了视频编解码上面. 最后项目上面也没应用上. 因此开源了这个仓库, 算是给近期比较有意思需求的一个方案. 这篇文章将一步步的介绍并解决这个问题.

基于V4l2的相机驱动实现流程

首先, 旧版的相机驱动是基于v4l2的. v4l2就是linux提供的一系列api. 基于v4l2开发的驱动也是有一个比较固定的流程的. 大体如下:

1. 打开相机获得句柄
2. 准备好v4l2_buffer并且配置v4l2_buffer
3. 等待句柄事件触发
4. 事件触发后访问v4l2_buffer存的就是准备好的相机buffer(v4l2_struct) 

此外相机是在硬件buffer里获取, 需要让用户态程序能够访问有三种方式dma, mmap和userptr. 我们希望尽可能低的cpu使用率因此选择dma. 因为其他两种方式相比, 从用户态程序操作buffer是直接对硬件buffer做操作 .而mmap和userptr都是对虚拟地址做操作的, 因此视频转码之后需要多一步I/O拷贝的硬件buffer.

了解了相机驱动大体流程之后, 首先先是尝试了v4l2_open()open(), v4l2_ioctl()ioctl()这些系统api的替换, 观察是否能有优化的效果. 经过一番尝试后发送如果换成v4l2_open()都无法打开用open()能打开的句柄.

图像转码

因为我们的平台是Jetson Orin. 自然会去想使用Nvidia提供的一些硬件视频编解码或者其他工具链路来完成, 比较直观的想法是在gpu上做yuyv到bgr的转换, 或者能直接在硬件buffer里面完成转码肯定是最好的. 所以先尝试了能否在硬件上直接做转码. 就开始了解Nv的工具链, Jetson Pack等等. 当然也会在Nv的论坛上看别人的post, 最开始看回答的时候也有些云里雾里的. 还有很多时候明明也没有解决方案,Nv也会把帖子标为solved. 当然这里不会过多论述踩坑的过程, 比较有帮助是下面这张图, 这张图展示了Nv的生态工具链:

nvtoolkts

ok, 明确了大体上Nv所提供的工具链之后, 接下来解决这一步的问题: 原始相机吐出来的格式是YUYV要转换成BGR24, 在这一过程要达到性能最优.

根据上面的需求尝试了以下几个方案:

  1. 基于硬件的编解码,基于JetPack 5使用nvbuf_utils的NvBufSurface来存储相机原始数据buffer, 再使用NvBufSurfaceTransform
  2. 基于cuda做手动的转码,手写kernel. 根据这个公式, 一组YUYV可以转成成2个BGR像素. 以及jetson_multimedia_api的samples里面的转rgb的例子.
  3. 使用cuda加速版的opencv的cvtColor
  4. 使用Npp来做处理

尝试之后发现: 方案1: 硬件只能转换成BGRA, 因此要从BGRA转换成BGR的话. 不可避免的就需要在用户态开辟buffer做转换. 过程比较繁琐. 方案3: 性能相比于方案2要差, 猜测原因是cuda opencv不能自定义threads和block的比例. 方案4: 有奇怪的bug. 转出来的图片不符合预期,也没有论坛post去讨论这个问题. 因时间原因, 解不了只好放弃.

这一步最终选择了方案二. 使用CUDA Zero Copy Mapped Memory(host mem与device mem映射), 然后使用核函数去做转换.

时间戳对齐

相机驱动输出的结构体是v4l2_struct. 而v4l2_struct带的timestamp是相对时间戳(从系统上电开始计时)并且是Start Of Frame(即buffer里面第一个字节有数据时的时间),而需要转换到unix时间戳。那如何做转换呢? 通过clock_gettime函数里面, 传入两个timespec.一个为realtime另一个为monotonic. 再做差值就是两个尺度原点的差值. 此外, 还需要加RTCPU与CPU时间戳的偏移量(见链接):

t0 = adjusted_sof = TSC - offset_ns

TSC即下图的t1, 其中offset_ns代表latency.

sof-latency-path

再补偿回这个偏移量就可以得到尺度一样的时间戳. 如果是在Nv的板上的话,偏移量会存放在/sys/devices/system/clocksource/clocksource0/offset_ns这个路径下. 加上了这个偏移量那输出的就是monotonic_time.

本机IPC(Inter Process Communication)

在编解码这一步我们使用了cuda开辟了gpu内存, 所以如果能基于cuda_ipc的话应该是最优解(没有多余的拷贝,客户端应用程序可以直接指向gpu buffer). 但是使用cuda-ipc的话就不能使用device和host memory的映射, 必须要cudaMemcpy. 并且我们希望cpu应用端也能直接拿. 接下来就考虑使用shared memory或者传dma_fd. 理论上传dma_fd的性能更高, 但是也依赖Nv的JetPack, 而刚好我们的JetPack版本在2023年12月这个时间点还不支持共享NvBufSurface. 如果以后支持了, 使用NvBufferTransformEx()类似这样的api应该是最优的做法. 还有, 可以用eglstream去做IPC, 比较麻烦的是每多一个订阅者就要改一次代码(开通channel). 因此也排除了基于Nv的生态做IPC.

现在考虑使用shared memory去做这件事. 有一个问题就是在应用端(驱动)去往内存块里写图片的时候, 客户端需要用进程锁. 如果这时候同步机制做的不好的话, 就很容易出问题. 一开始尝试使用boost的interprocess读写锁, 但是这里还是有内核态到用户态之间的切换. 基于纯用户态的话, 使用信号量做触发机制. 这样做是可行的, 但是对客户端接受编写接受图像代码逻辑有要求. 但还是有个问题就是这套机制没办法自定义QOS, 并且只限定于这种场景.

于是最终转向了Iceoryx. 因为这也是现有的基于shared memory(c++)最成熟的中间件. 相比于手写shared memory, 基于Iceoryx的通用性和可复用程序更高, 更有利于后续其他模块满足迁移通信机制的需求.

结果

做完上述处理过后的驱动相比于供应商提供给我们的驱动在cpu使用率在JetSon Orin上降低了10%左右. 如果你也有类似的问题, 参考这个仓库或许对你有所帮助.

总结

整个过程有以下几点总结:

  1. Nv的很多资料散落在论坛里面,需要多个post才能拼成一个完整的信息源.
  2. 问题最快的解决方式是直接让供应商找人去改. 能有时间和精力去研究这些当然好, 写完这篇文章或许能证明自己的一些学习能力. 但是所有问题的最快解决方案始终是找到对应的人. 因为很多问题遇到一次基本上也不会遇到第二次了, 很难说能从获得系统性的思考能力.
  3. 如果在现有的Nv板上在官方文档找不到解决问题的资料, 不妨往以前版本的Nv板的官方资料上面找.

参考资料

以下是在这过程中比较有帮助的参考资料:

  • https://developer.ridgerun.com/wiki/index.php/NVIDIA_Jetson_TX2_-_Video_Input_Timing_Concepts
  • https://forums.developer.nvidia.com/t/interprocess-zero-copy-image-transfer/231623
  • https://on-demand.gputechconf.com/gtc/2016/webinar/getting-started-jetpack-camera-api.pdf
发展思考的方式

这是一篇读书摘要汇总("The Art of Doing Science and Engineering: Learning to Learn")

写这篇文章的灵感来自于之前读Richard Hamming"The Art of Doing Science and Engineering: Learning to Learn". 书的内容来自于他给美国海军的一系列讲座集合, 之后整理成书. 书中Hamming有非常多精彩的观点,但是看书有一个问题就是到最后很难提炼成自己的东西, 所以这种好书就需要时间去吸收, 看完不是结束, 而是行动的开始. 这里选取了十五句本书对我最有影响的话.

Quote1.

“In science, if you know what you are doing, you should not be doing it. In engineering, if you do not know what you are doing, you should not be doing it.” - P49

工程和科学应该有清晰界定的范围, 对于实现工程, 很多时候会参杂着research. 但是要界定research的范围. 应该是在已知面(不应跳进未知领域的坑) 进行探索. 此外, 在工程中未知因素越多, 出问题可能性越大, 应当越简洁越好.

Quote2.

“I believe the best predictions are based on understanding the fundamental forces involved, and this is what I depend on mainly.” - P51

技术和时代变化如此剧烈的现在, 更应该总结和思考什么是基础因素.

Quote3.

“ so long as it takes you to greatness, is none of my business. You must, as in the case of forging your personal style, find your vision of your future career, and then follow it as best you can. No vision, not much of a future.” - P56

尝试制定并不断修正自己的计划. 才能让未来尽可能的与计划相接近.

Quote4.

“The people at the bottom do not have the larger, global view, but at the top they do not have the local view of all the details, many of which can often be very important, so either extreme gets poor results.” - P77

底层视野(执行层)和顶层视野(规划层)都很重要. 盲目的只注重哪一个方面都不会有好结果.

Quote5.

“I suggest you must rethink everything you ever learned on the subject, question every successful doctrine from the past, and finally decide for yourself its future applicability. The Buddha told his disciples, “Believe nothing, no matter where you read it, or who said it, no matter if I have said it, unless it agrees with your own reason and your own common sense.” I say the same to you—you must assume the responsibility for what you believe.” - P82

批判性思维, 足够清晰的了解问题才能最好的解决问题. 清晰的了解问题必须要经过独立思考的这个过程.

Quote6.

“Mathematics is the language of clear thinking.” - P625

下一次遇到了不清晰的问题可以尝试用数学的方式描述出来.

Quote7.

“What you did to become successful is likely to be counterproductive when applied at a later date.” - P713

不断的反思过去的行为, 然后加以学习总结以及改正.

Quote8.

“If you optimize the components, you will probably ruin the system performance.” - P752

部分最优不代表系统最优, 系统工程学的观点. 吴军也讲到过.

Quote9.

“You get what you measure” - Chapter29 Title

生活无法在没有指标的方面取得进步.

Quote10.

“In planning to change yourself clearly, the old Greek saying applies: “Know thyself.” And do not try heroic reformations which are almost certain to fail. Practice on small ones until you gradually build up your ability to change yourself in the larger things. You must learn to walk before you run in this matter of being creative, but I believe it can be done.”

改变是从非常微小且具体的步骤开始.

Quote11.

“Why are you not working on and thinking about the important problems in your area?” If you do not work on important problems, then it is obvious you have little chance of doing important things.” - P808

只有在重要的领域研究重要的问题才能取得重要的结果.

Quote12.

“intellectual investment is like compound interest: the more you do, the more you learn how to do, so the more you can do, etc”

掌握尽可能多的问题模型, 收益会体现在以后的问题上面.

Quote13.

“There is another trait of great people I must talk about—and it took me a long time to realize it. Great people can tolerate ambiguity; they can both believe and disbelieve at the same time. You must be able to believe your organization and field of research is the best there is, but also that there is much room for improvement! You can sort of see why this is a necessary trait. If you believe too much, you will not likely see the chances for significant improvements; if you do not believe enough, you will be filled with doubts and get very little done, chances are only the 2%, 5%, and 10% improvements.”

辩证思考, 至少需要同时看待事物的两个方面.

Quote14.

“All three are essential—you must learn to sell your ideas, not by propaganda, but by force of clear presentation. I am sorry to have to point this out; many scientists and others think good ideas will win out automatically and need not be carefully presented. They are wrong; many a good idea has had to be rediscovered because it was not well presented the first time, years before! New ideas are automatically resisted by the establishment, and to some extent justly.” - P822

清晰的表达比单纯只拥有一个好的点子更加重要.

Quote15.

“I believe it is—the chief gain is in the effort to change yourself, in the struggle with yourself, and it is less in the winning than you might expect. Yes, it is nice to end up where you wanted to be, but the person you are when you get there is far more important.”

相信并且观察和体会内在的变化.