内存拷贝优化(2)-全尺寸拷贝优化

四年前写过一篇小内存拷贝优化:http://www.skywind.me/blog/archives/143 纠结了一下还是把全尺寸拷贝优化代码发布出来吧,没啥好保密的, 如今总结一下全尺寸内存拷贝优化的要点:

  • 策略区别:64字节以内用小内存方案,64K以内用中尺寸方案,大于64K用大内存拷贝方案。
  • 查表跳转:拷贝不同小尺寸内存,直接跳转到相应地址解除循环。
  • 目标对齐:64字节以上拷贝的先用普通方法拷贝几个字节让目标地址对齐,好做后面的事情。
  • 矢量拷贝:并行一次性读入N个矢量到 sse2 寄存器,再并行写出。
  • 缓存预取:使用 prefetchnta ,提前预取数据,等到真的要用时数据已经到位。
  • 内存直写:使用 movntdq 来直写内存,避免缓存污染。
……

阅读全文

视频编码原理简介

要彻底理解视频编码原理,看书都是虚的,需要实际动手,实现一个简单的视频编码器:

知识准备:基本图像处理知识,信号的时域和频域问题,熟练掌握傅立叶正反变换,一维、二维傅立叶变换,以及其变种,dct变换,快速dct变换。

来自知乎问题:http://www.zhihu.com/question/22567173/answer/73610451

第一步:实现有损图像压缩和解压

参考 JPEG原理,将RGB->YUV,然后Y/U/V看成三张不同的图片,将其中一张图片分为 8x8的block进行 dct变换(可以直接进行二维dct变换,或者按一定顺序将8x8的二维数组整理成一个64字节的一维数组),还是得到一个8x8的整数频率数据。于是表示图像大轮廓的低频信号(人眼敏感的信号)集中在 8x8的左上角;表示图像细节的高频信号集中在右下角。

接着将其量化,所谓量化,就是信号采样的步长,8x8的整数频率数据块,每个数据都要除以对应位置的步长,左上角相对重要的低频信号步长是1,也就是说0-255,是多少就是多少。而右下角是不太重要的高频信号,比如步长取10,那么这些位置的数据都要/10,实际解码的时候再将他们*10恢复出来,这样经过编码的时候/10和解码的时候*10,那么步长为10的信号1, 13, 25, 37就会变成规矩的:0, 10, 20, 30, 对小于步长10的部分我们直接丢弃了,因为高频不太重要。

经过量化以后,8x8的数据块左上角的数据由于步长小,都是比较离散的,而靠近右下角的高频数据,都比较统一,或者是一串0,因此图像大量的细节被我们丢弃了,这时候,我们用无损压缩方式,比如lzma2算法(jpeg是rle + huffman)将这64个byte压缩起来,由于后面高频数据步长大,做了除法以后,这些值都比较小,而且比较靠近,甚至右下部分都是一串0,十分便于压缩。

JPEG图像有个问题就是低码率时 block边界比较严重,现代图片压缩技术往往要配合一些de-block算法,比如最简单的就是边界部分几个像素点和周围插值模糊一下。

做到这里我们实现了一个同 jpeg类似的静态图片有损压缩算法。在视频里面用来保存I帧数据。

……

阅读全文

多平台下录屏方式

随便记录下,想得起来的多少写多少:

Windows:

GDI 全屏:fullscreen desktop BitBlt 速度是 20ms / 帧  
GDI 窗口:Win7+可以录制游戏和非游戏,XP以前只能录制普通界面,截不到游戏窗口  
DirectDraw 全屏:full screen primary surface 异步到 offscreen-surface XP下闪电速度 1ms / 帧  
D3D9: render target -> offscreen surface XP下 16ms Win7 下 10ms  
D3D Hook: Hook Device::Present -> 保存 StateBlock -> 截屏 -> 恢复 StateBlock 极速!
……

阅读全文

Android Arm 编译优化选项评测

用不同测试用例具体测试 softfp, armv7-a, cortax 等优化选项,看选项不同性能差别多大。首先设计下面几个测试用例,包含字符串处理、复杂逻辑、整数运算、浮点运算几个方面:

  • compress:进行 LZO/LZW 大规模压缩,测试搜索,字符串匹配,复杂分支等性能
  • resample:进行一系列整数 DSP 运算,包括 resample 和 fir low pass
  • int add:一亿次整数加法
  • int mul:一亿次整数乘法
  • int div:一亿次整数除法
  • float add:一亿次浮点加法
  • float mul:一亿次浮点乘法
  • float div:一亿次浮点除法
  • const div:一亿次整数除以常数255
  • matrix:若干次矩阵乘法运算,同时考察浮点数乘法加法
  • normalize:若干次矢量归一化运算,同时考察浮点数乘法,除法,加法,sqrt
……

阅读全文

Android 命令行调试 C/C++ 程序

传统方式调试 NDK 开发的程序比较麻烦,先要编译成 JNI,又要导出 java接口,还要再写一个 java 工程,改一个地方又要连续改几处,这样效率是很低的。最频繁使用的关键工作路径(编译/调试环节)如果能极致简化,那么可以带来开发效率的成倍提升。其实安卓官方是提供了命令行调试方法的,将你需要调试的 C代码用 NDK直接编译成可执行,然后到设备上执行:

使用 NDK 导出独立工具链,方便以后使用,在 cygwin 下面,将 $NDK 环境变量代表的路径设置好,然后:

cd $NDK  
chmod -R 755 *  
build/tools/make-standalone-toolchain.sh --ndk-dir=$NDK --platform=android-9 --arch=arm --install-dir=/...../path-to-android-9
……

阅读全文

如何写一个软件渲染器?

实现个简单的固定渲染管线软渲染器不算复杂,差不多700行代码就可以搞定了。之所以很多人用 D3D用的很熟,写软渲染却坑坑洼洼,主要是现在大部分讲图形的书,讲到透视投影时就是分析一下透视变换矩阵如何生成,顶点如何计算就跳到其他讲模型或者光照的部分了。

因为今天基本上是直接用 D3D 或者 OGL,真正光栅化的部分不了解也不影响使用,所以大部分教材都直接跳过了一大段,摄像机坐标系如何转换?三角形如何生成?CVV边缘如何检测?四维坐标如何裁剪?边缘及步长如何计算?扫描线该如何绘制?透视纹理映射具体代码该怎么写?framebuffer zbuffer 到底该怎么用?z-test 到底是该 test z 还是 w 还是 1/z 还是 1/w ?这些都没讲。

早年培训学生时候,我花两天时间写的一个 DEMO,今天拿出来重新调整注释一下,性能和功能当然比不过高大上的软件渲染器。但一般来讲,工程类项目代码不容易阅读,太多边界情况和太多细节优化容易让初学者迷失,这个 mini3d 的项目不做任何优化,主要目的就是为了突出主干:

源代码:skywind3000/mini3d · GitHub
可执行:mini3d-src-binary

……

阅读全文

如何设计一个内存分配器?

通常工程里不推荐自己写内存分配器,因为你费力写一个出来99%可能性没有内置的好,且内存出bug难调试
不过看书之余,你也可以动手自己试试,当个玩具写写玩玩:

1. 实现教科书上的内存分配器:

做一个链表指向空闲内存,分配就是取出一块来,改写链表,返回,释放就是放回到链表里面,并做好归并。注意做好标记和保护,避免二次释放,还可以花点力气在如何查找最适合大小的内存快的搜索上,减少内存碎片,有空你了还可以把链表换成伙伴算法,写着玩嘛。

2. 实现固定内存分配器:

即实现一个 FreeList,每个 FreeList 用于分配固定大小的内存块,比如用于分配 32字节对象的固定内存分配器,之类的。每个固定内存分配器里面有两个链表,OpenList 用于存储未分配的空闲对象,CloseList用于存储已分配的内存对象,那么所谓的分配就是从 OpenList 中取出一个对象放到 CloseList 里并且返回给用户,释放又是从 CloseList 移回到 OpenList。分配时如果不够,那么就需要增长 OpenList:申请一个大一点的内存块,切割成比如 64 个相同大小的对象添加到 OpenList中。这个固定内存分配器回收的时候,统一把先前向系统申请的内存块全部还给系统。

……

阅读全文

FlashDevelop 好用

好几年没碰过 Flash 了,最近需要给 ActionScript 导出一些 C 接口,又抽空捡起来。项目大了以后 Flash Builder 卡的要死,经常是一个构建你就可以休息了,按一下 “.“ 它就开始搜索补全提示,你的符号多了以后,有时候 Flash Builder 近乎假死了。实在难以忍受,咨询了一些正在做页游的朋友,是否还在用 Flash Builder 。得到答案是:早就投奔 Flash Develop 了。

试了一下 FlashDevelop 果然腰也不酸了,腿也不疼了,十分流畅,界面类似 Visual Studio,同时还是免费的,可以彻底和笨重的 Flash Builder 说再见了。再次感叹 AS3 写起来真爽之余,记录一下安装配置过程:

image

……

阅读全文

网络程序计时器通常用啥实现?

通常来讲,就是利用 select 的空余时间,来进行时钟检查,不管是 select / poll / epoll/ kevent,以下统称 select,它有一个等待时间作为参数,即没有事件时,最多 wait 多少时间,我们把这个作为网络库的基准频率,比如 10MS,或者 20MS, 25MS, 50MS,都是常用的几个值。

就是说网络库调用 select 等待事件时如果没有事件,那么最长等待 10MS 就返回了,这时再处理完所有网络事件后,就可以来处理时钟数据了。事件处理函数就是这样:

def update_events(milisec = 10):  
	result = selector.select(milisec)  
	for fd, event in result:  
		do something with socket event  
	current = time.time()  
	update_timer(current)

while 1:  
	WAIT_MILLISEC = 10  
	update_events(WAIT_MILLISEC)

关键就是这个两次 select 中间 update_timer 的任务:集合中检查需要唤醒的时钟,并且调用它们的回调函数,来驱动整个服务器的时钟运行,以最简单的扫描法为例:

def update_timer (current):  
	for timer in available_timers:  
	while current >= timer.expires:  
		timer.callback(current)  
		timer.expires += timer.period
……

阅读全文

如何提高编程的手速

可以使用经典的 TT 来测试你的打字速度,注意是包含数字和符号的文章(Menu->Test->All key) 软件很简单,按照箭头指着的位置,快速输入上面的单词即可,输入完会有评分的。 TT是比较好的打字练习程序,直到今天,公司内都用作给新人练习打字速度用。不当能测试,还有比较详细的课程,教你从纯单词打起,逐步到数字,标点符号等。 我做过一个 DOSBOX版本的 TT (tt.exe 是 DOS下的程序),双击 TT.BAT 即可在 Win7/8 启动 http://www.skywind.me/mw/images/e/eb/TT-Dosbox.7z 我当时用 TT 测试 All Key 的时候,已经写过好多年程序了,自己觉得自己打字不慢,英文可以流利盲打,数字和符号需要看一下,结果 TT 测试下来,打字速度只有 31 WPM ,属于垫底的角色,丢死人了。 于是开始用 TT的课程进行练习,每天练习的不多,每天两次,每次15分钟,主要是先打10分钟的课程,然后做5分钟的测验,不能练多,练多了手麻,但是要坚持每天都练习两次左右。一开始纯英文的指法挺简单乏味,觉得没意思,因为我本身可以流利的盲打英文,觉得自己主要是被数字和符号拖累了。但是坚持了一段时间练习纯英文速度后,我发现纯英文速度从 42WPM提高到 55WPM。 后来开始继续练习数字和符号,这就比较痛苦了,就是单调乏味的重复练习,这部分手指动作跨度大,即便记得住在哪里,经常手指过去就按错了,但是一旦数字和符号分实现了盲打,整体 WPM的提高是非常大的。 TT 有专门练习数字和符号的的课程: 几个星期后,All Key的打字速度 WPM 终于从31 提高到 45 了,通过了打字测验。这时候我回来输入程序,我发现对自己编程帮助确实比较大。以前碰到数字和符号经常要低下头去看在哪里,指法也不标准,比如按shift组合键的时候是比较山寨的打发,现在纠正了一下实现全键盘盲打了,感觉写程序顺畅了很多。 很多人觉得程序主要是思考,你思路对了,打字只是小事,经常听到说某某高手打字是二指禅。以前我也这么认为,但是专门抽时间练习了以后,发现真正打字流畅的人,打字的 cpu占用很低,不会因为打错字而影响思考,更不会因为要低下头去看数字符号打断你的思路。 到了 45的 WPM 以后,再往下提高就比较难了,经常一个多星期没啥提高,我后来发现我的指速不够,就是即便全部盲打,手指不够熟练,灵巧度不高,再怎么盲打也是白打。因此买了一个机械键盘来专门练习指速。 先单纯练习从 A打到 Z,最开始是15秒,慢慢熟练到10秒,最后到5秒。又从最基本的单词开始,不断的重复同一节 TT的课程,同一个动作重复 300次,肌肉就会有记忆,而同一个动作重复600次,脊柱就会有记忆。别人打字要经过大脑,大脑一个字母一个字母,一根手指一根手指的指挥,是比较费脑的,而你打字如果真正进入了脊柱反应的条件反射时代,那么看到一个单词或者一串数字,大脑只需要下一个指令,脊柱这个协处理器就自动完成了,根本不需要过大脑。 所以实现全盲打后,又经过一段时间的指速练习,从 45 WPM 不断的提高到 50 WPM,55 WPM,60 WPM 最后到 65WPM (TT的 All Key模式),单纯打英文的速度也从原来的 35 WPM提高到 71 WPM,整整翻了一倍: 虽然 TT 是 DOS 时代的软件了,现在市面上出过很多打字练习软件,但是说道科学性和系统性,还是没有超过 TT的。现在偶尔抽空还是会拿出 tt 来练习一下,就当打游戏。但是自己手指的灵巧度感觉还是不够,可能这是先天的,因为即便我达到了 65 WPM,公司内还是有很多70,75以上的同事打的比我快多了。 现在再看到那些诸如某大牛只用二指禅来敲代码的言论,我都会嗤之以鼻了,字都打不好还写啥程序呀?不管有多牛,即便写代码的思路再好,但是手速跟不上,打字还要费大脑的话,还是有所局限的。 只有很流畅的打字了,让打字变为一种不经大脑的条件反射,你的注意力才能完全集中在思考你的程序上。我把自己的打字速度从 30 WPM 提高到 65WPM的过程中,对这一点感触是比较深的。因为先天手指笨拙,我好像要比同事们练习的更多才能有他们同样的提高,但是我还是能够感受到提高手速对编程的改变,所以有空我都还是会拿出 TT 来练习一下,全当打游戏了,希望有一天 All Key 的 WPM能上 70。 -———————— PS: TT 下载地址(DosBox版本)http://www.skywind.me/mw/images/e/eb/TT-Dosbox.7z 整合了 DOSBOX + TT,运行 “TT.BAT” 就可以在 Win7/Win8下启动,运行”TT放大版.BAT”,就可以设置 DOSBOX放大两倍显示(如今高分屏看起以前低分辨率的文字有点累,放大两倍后效果比较好)。 Ubuntu / OS X下,先安装 DOSBOX,把上面的 tt 下载下来,再用 ubuntu的 dosbox 启动 tt.exe 即可。 -———————— 不要用《金山打字通》: 金山打字通测试文章中,符号和数字偏弱,标点就是逗号和句号,比起TT的 All Key (每篇文章至少包含一定数量的电话号码,门牌号码,各种符号) 金山打字通测试是:每秒钟敲下多少字母,不是 WPM(每分钟敲下多少个单词),比字母的话,我最快5秒钟可以从 A->Z,应该是 312字母/分,没啥意义呀。 编辑于 2015-07-03……

阅读全文