Welcome to Programming’s documentation!

Contents:

目标

  1. 快速地定位与解决问题

结构

  1. 编程语言是什么以及语言的发展
  2. 如何学习编程语言
  3. 用过语言总结
  4. 如何阅读代码与理解代码
  5. 编译以及ABI原理
  6. 如何调试
  7. 如何profiling

开篇基础

感悟

经常被人问,为什么你不学这个语言?为什么你们要采用这个语言而不用那个语言?哪个语言最好?我应该学哪门语言呢?看看自己这几年编程之路一些心得。

其实26个字母,每一个字母背后至少有一门语言?为什么会这么多种编程语言呢?因为没有一门语言是万能的。编程语言也是不断向前演化的。随着人们对计算机需求的发展而不断向前发展,没有最好,只有更好。 程序语言是最重要的思维工具,它们首先让我们能够明确地传递表达自己的想法,以至于即使是计算机也能理解,如果有人发明一种编程语言即符合他们的思维,又能解决当下的问题。此时,选哪种语言都无关紧要了。

每一门语言都它自己特有的应用环境。但是编程语言也有共性:所有的语言都会由这些基本元素组成:变量,循环,分析,顺序,函数。以及常规数学运算以及逻辑支算。如果说你的需求是这种大众化的。任何一门语言都能满足你。如倮你的需求不是这么的简单是特定领域的。:就找到在这方面应用比较广泛的语言。例如你的应用环境是办公软件(ms的office),学习办公软件支持的脚本语言VBA,以及相关的API.是最好的选择。如果你要文本处理,并且需求简单,常用的shell语言都能实现,复杂的用perl,tcl来处理。对速度有要求用java,C++还处理。如果是做嵌入式那么C语言是更好的选择。同时也要看你的应用环境支持哪些语言。对于是同一系列的语言,后出来的语言会之前的语言要强大一些,并且易用性会好一些。如果没有特殊的需求,只是为了学语言而学,那就学习C/C++或者scheme,python吧,因为这两种语言计算模型是不一样的,学会这种两种计算模型,对于你学习其他语言都会有莫大的帮助。

根据个人编程经验分三个方面的总结希望对大家有帮助:

编程语言的组成,

语言学习发展曲线,分三个阶段吧,

以及自己这几年用过的编程语言一些体会吧。 作为一名经验丰富的程序员,回过头再重新学习上述一门编程语言则是获取新感想的最有效方式:这些语言所涵盖的概念提醒着我们,这都是些我们现在看来理所当然的概念,但是初学者却需要在某个阶段学习它们。当我们这么做的时候,我们与学生以及其他的初学者的沟通合作将会更加有效率,因为我们更容易回想起整个逻辑链条,包括那些我们之前认为理所当然而省略的思维步骤。

变量

从编码的脚度变量本质就是替换,一般变量赋值就是一次替换,指针类型变量的赋 值是二次替换,动态语言经常会用到三次替换。但是大部分情况下,用不到三次以 上替换,这也就是什么一般编程语言对二次以下替换支持很好。三次以上替换需要 靠自己去实现了。(3这个数字是特殊的,俗话事不过三,这个是有原因的,根据 一般人的思维能力的来的,就拿最简单的yes/no的逻辑判断来说,连续三次 ,就是八种情况,但是一般人思维能力同时关注最多九种情况,再多了人的思维就 会混乱容易出错了,这也就是为什么,一般人下栱只看二三步的原因,很公司的编 码规范会要求禁用或者少用三层以上if else/ for嵌套。并且学编程 最头疼的内存管理也是三层管理,这样的例子到处都是不多举了)。

从物理的存储角度看:变量又为简单标量,复杂变量列表,哈希。再复杂一些树, 栈,表等数据结构。一般的编程语言都对简单标量,复杂点的列表,哈希支持的很 好。对于标量来说,常见的应用就是字符串的处理,与数值变量各种数学逻辑运算 的。而对于树,栈,表都复杂的数据结构都自己去实现。并且数据量再大一些数据 结构就需要专门的工具数据库来管理了。

变量的另一个作用,那就是信息的传递,以及的分配与释放。难点是变量的存活时间 。也就有了各种变量作用域。都是为提高内存的复用机制。通过分析app的数据的分布 就可以知道其内存的基本使用情况了。同时引用计数是一种最常用的机制。以及各种 的强引用,软引用,弱引用都是这种出于这种目的。 变量复一次值,就多一次引用计数。 每一个系统中引用计数加减原则也是不一样的。

强引用,就是内存不用,也不回收,直接报错,而软引用就是内存够,就不回收。 经常那些somtimes 的问题都是由于软引用与弱存用引起的。因为他的变量回收时间不定 并且你的测试环境,经常内存是充足的,但是实际环境就未必了。

流控与表达式

程序代码的本质是思维与知识的固化。所以程序的结构是根据人的思维习惯来的。 三种最基本结构:顺序,条件分支,循环。就拿人走路来说吧,人沿着路一直走到 下,这就是顺序结构,走到一个交叉路口,要选择走哪条路,这个选择的过程就是 条件分支。选好了路继续走前走,来到一个大山的脚下,然后顺着盘山公路,一圈 一圈的往上走,这就是循环。每一种编程语言都对三种基本结构最具有很好的支持 。但是每个领域都除了这些基本处理逻辑外,都会自己特殊的处理逻辑。人们对这 些处理逻辑进行抽象建模,然后再根据模型,开发出来的相应的语言。例如per l对文本处理的优化,为了测试集成电路而开发出来tcl语言,针对集成电路中 时序与状态转换处理逻辑,对于定时器与事件驱动的状态转换机制提供了很好的支 持。数据库对大规模数据的集合运算,以及并发操作,原子操作都提供了很好的支 持。Matlab对于大型数值计算支持与优化。以及coroutine机制支 持的erlang与GO语言。当然随着技术发展,每门编程语言应用范围越来越 广,同时随着人们的计算需求的发展,还会更多的语言被开发来应对这些新需求。

表达式

加,减,乘,除的常规运算。 离散位运算,与或非,移位运算 逻辑运算,比较

代码块的复用与扩展

每一种语言都会提供代码复用的机制,邮最初的汇编语言的那些中断函数开始。到 C语言等等函数的功能,以到C++,java等面向对象的机制。都是为了解决 代码的复用性与易读性问题。我们编程与在框架设计的时候原则,对于功能比较小 公用模块,例如一个计算公式就可以函数来实现。但是公用模块的复杂度提高,利 用面向对象类的机制,对其实现比较适合。对于更加复杂的共用模块,但又不适合 用函数与类实现,可以考虑用模板来实现,例如你会发现IDE工具生成各种各样 样版代码,这就要元编程,你可以用lex/yacc来实现,可以利用sche me来实现。为了进一步提高提高代码灵活性,把数据与函数是不区分了,也就产 生了函数式编程。对于代码动态调用:尤其是脚本语言,是灵活复用性,采用动态 生成代码,直接运行。对于整个过程自动化,很有帮助,例如每一个脚本语言都会 一个eval指令。基于不同的计算模型,编码的方式也是不一样的。

当代码的代码功能不足,有问题,但是你又不能直接修改其原始代码怎么办,这时 候,OOP的继承,重载就起到作用了,只写一个新类继承原来的类,并且只需要 把对应的代码改掉就行。 这样原来的运行的代码没有任何影响,需要新功能类, 就可以使用新就可以了。

对于编译型语言的好处,可以快速解决语法错误,其实节省了大量的时间。但是却 但是自身不能执行,并且添加了部署的时间。解释型语言来说,虽然节省了部署时 间,但是缺少事先的语法检查,增加了调试的时间。每一个错误的出现都有等到运 行的时候,才能知道其对错。但是能有一个事先的语法检查,那样就可以大大提高 开发的效率。另外一点,那就是相对于来说编译型语言的api水平太低了,需要太多 的细节来才处理,当然可以用一些成熟的库来实现脚本语言一样的简便功能。

新语言的产生一方面是应用场景的变化,另一方面那就是设计的改进了,C++ 改进了C, 而Java改进了C++,而C#又改进了Java. 可以参考http://coolshell.cn/articles/7992.html

面向对象的数据模型和面向数组的数据处理着眼于静态环境下构建数据模型,或是以带有标签属性 数据集合的形式,或是包含结构化数据组的形式。 而函数式编程强调动态地构建数据模型,通常是以计算流的形式。学习函数式编程的基础对数据转换操作 的结构大有裨益。 事件驱动编程,计算管道是处理数据转换各分析的一种出色方法。

函数

递归函数是悖论一个很好的表达形式。 同时构造悖论就像写递归函数一样容易了。同时利用状态机来环路 来就像一直悖论。同时悖论的打破要从外部来实现,并且也常常是系统的最优解位置。

计算模型的原型

编程语言最终反映了两个方便的东西,那就是资源的管理,以及基于资源实现的功 能。由于我们支持能力与认识不同。采用了分层模块的方法把难的东西变的简单可 实现。我们在硬件操作的加了操作系统,操作系统之上我们又加了各种库等。 从进程,线程,协程就是把资源的管理方式,一步步的提升。让从我们可以编程语 言层面就可以控制资源。从效率来说,那就是最好能够直接编程写2进码。或者让 我们编程尽可能靠近二制码。就是例如提供各种语言到汇编的直接转化。LLVM 现在就是这样的一个项目。例如CUDA对于python的支持都是这种原理。 corepy google支持的把python直接转化为汇编。 各个平台的通用化过程,其实了简单,源码或者中间码再加上一个JIT编译器就可以 了。pyasm 目前都是基于LLVM在玩。

另一个方向就是程序验证如何保证程序百分之百的正确呢。

编程语言的本质逻辑,逻辑的最好表达方式那就是图,这也正可视化编程的方向。 同样是不是可以在代码里直接加入表情字符,例如int用一个图形来表示,fl oat,array,dict各种的图形来表示,但是写代码本身文本就像可视 化html的开发一样。就像现在html写介面是一样的。有这些标准控件可以 搞定了。把注释变成事例这样会很方便。

Perhaps of all the creations of man. Language is the most astonishing.

一个语言的完备性也不是那难,一个语言需要原语并不多。只形成这样闭环。就能形成一个小的系统。 当然一旦形成这样一个闭环,也就很难自动突破。

The right language can make all the difference in how easy it is to write a program.

什么时候需要再创造一门新的语言呢。

当你发现在你要为一个小功能,写很多的代码的时候,或者你现有的语言来表达你的逻辑与流程非常麻烦的时候。 这个时候,就是要创造一门语言的时候,语言的大小,是根据要解决的事务本身的大小来决定。 当然语言发展都是根据计算模型来的。

例如那个printf函数簇那就是一个小符号系统。 正则表达式是也一个小符号系统。grep,awk,sed等等都是基于正则式。 巴斯特范式也是一个小符号系统。 protobuf也是一个小符号系统。 一个简化版本,那就是pack/unpack package的函数。

int pack_type1(unsigned char * buf, unsinged short count,unsigned char val, unsigned long data)
{
     unsigned char * bp;
     bp = buf;
     * bp++ = 0x01;
     * bp++ = count >> 8;
     * bp++ = count;
     * bp++ = val;
     ....
     return bp - buf;
}

#include <stdarg.h>

/* pack: pack binary item into buff return lengh */
int pack(unchar * buf, char * fmt, ...){
    va_list args;
    char ap;
    uchar * bp;
    ushort s;
    ulong l;

    bp = buf;
    va_start(args,fmt);
    for (p = fmt,* p !='\0'; p++) {
        switch(* p) {
           case 'c': //char
              * bp++ = var_arg(args,int);
                break;
           case 's': //short
              s = va_arg(args,int);
              * bp++ = s>>8;
              * bp++ = s;
              break;
           case 'l': //long
              l = va_arg(args,ulong);
              * bp++ = l >>24;
              * bp++ = l >>16;
              * bp++ = l >>8;
              * bp++ = l;
              break;
           default:  //illegal type character
             va_end(args);
             return -1;
        }
    }
    va_end(args);
    return bp - buf;

}
pack(buf,"cscl",0x01,count,val,data);

例如把可以log的输出直接写另外一门语言本身。 这样拿到log就直接执行,就可以分析出大量问题。

当你实现了新的符号系统,一个原来系统中复杂的事情,在新的系统中却是非常简单方便的。 对于正则表达式,用状态机来实现,然后breakdown一下也就简单了。当然如果看到通配符,就要往前看一步了。并且采取的是从取左往右,还返过来都是不一样的。 如果用列表来表达些,最通过的表达那就是x:xs 这种前缀表达式。

而最终的目标

  1. simplicity
  2. clarity
  3. Generality
  4. Evolution

复杂的问题,可以在同级采用设计模式来解决,在不同级采用元编程来解决。 元编程来实现循环。 生成代码->再用进程替换来不断的执行。通过进程替换来实现代码加载与切换,同时又通过环境变量来进行数据的传递。

ProgrammVerification.rst

framework

对于任何一门语言都会有自己特定的编程模型,以及编程接口,以及硬件实现,以 及优化。所谓的framework就是一个pipline而己。例如OGL, make,ant,等等都是一个pipline而己。同时decorator 也是pipeline的一种体现。 函数的调用也体现的是一种pipelin e。代码的执行顺序也是一种 pipeline. 如何快速设计一个pipl ine呢,最简单办法就利用现有的例如make,或者在现有的基础上进行二次 开发,ruffus,以及python http://www.ruffus.org.uk/. 这样的东东很多。 以及mapreduce也是一种pipline. 如何动态创建pipeline,其实最简单那就是列表,这也正是 scheme,haskwell,语言特别适合创建pipeline的原因。 因为它们返回值,要么是值,要么是一个列表或者列表还是可以嵌套的。那个是C 语言本身不具备的,C语言只能简单的返回值,如果用到列表,就要利用指针显示 分配内存不是很方便,

语言一般都会包含五大块, build,compile,debug,profiling,verifi cation.对于程序本身来说,语法,关键字,内建指令。以及基本教程。而 这些都已经有现成的工具。xxxdoc可以直接查询,加上的IDE工具,以及 相关的API查询工具。并且还可以vim集成。 不管是什么样framewo rk,其本质都是为建立一个pipeline, framework = p ipeline在不同的地方叫法不一样,我需要解决问题的时候,如何才能利用 这个呢,那就是根据pipline不断把问题breakdown.达到很容易 解决为止,这也是daniel他们的过人之处。而自己还是大部分时候依赖经验 。没有能够把问题与工作不断的breakdown.

如何同时多个工作的面前还能focus,一个办法,利用不同的时间跨度来解决 ,其实就是最终的办法,例如一段时间固定一两个sample,然后把的测试都 给过一变,就可以把当前的时间focus在一个事情上,同时也不会浪费时间, 最终会把所东东都搞定。

compiling 本身是为了解决代码的复用,以及各种格式之间转换,例如 不同的架构的支持,以及各种ABI的支持。这些都是传统的最基本的。另外一个 就是编译效率的问题。并且编译与profiling以及debug相关,它们 都需要特殊compiling配制。 为了加快编译的速度,并行编译,以及可以分段离线编译,例如直接现成的编译好 的汇编可以直接用,直接将作原文件来编译,这样的好像一个是离线编译加快速度 ,另一方便在做代码优化的时候,就可以使用在关键的地方使用汇编了来优化了。

而现在的新的趋势,就是分层模块化。模块自包含。对于代码也构建也是一样。除 了各种库的依赖。另一方面那就是代码的优化。或者达到像人工写的汇编,或者人 写的汇编更强的代码。这个就像代数公式先画简再求值。而现在LLVM就是研究 解决这个事情。而CUDA本身也是利用LLVM来实现的。

对于优化很大部分那就是根据硬件平台的不同,采用对应的库,这样来加速计算。

当然所有东西都不会那么完美,特别并不是每一个人每时每秒都会尽十分的力。要 接受不完美,对于编译#pragma warning(disable) 来 对一些编译选项的控制,http://baike.baidu.com/view/1451188.htm#2_5 哪些情况下会用到这些,一个定位编译 错误的,例如找的路径不对,当编译在此的时候打印信息,或者error对出就 知道到此来过。另外那就是每一个文件的个性化设置,之前还在想如何对编译实现 不同粒度的灵活控制,其实继承重载是最方便的灵活控制,只需要改动你需要的。 其中一种方式那就像unreal一样直接用C#来做编译系统来实现灵活的控制 ,或者使用gradle来实现灵活的控制。

build,主要是解决依赖,以及集成的问题。

profiling 是对程序的透明性的讽刺,优化就意味着要针对特别的结构 来适配。透明性是解决万能性的问题。profiling之后的事情,那就是重 构,设计模式是重构的出来的,但是如何快速重构,这些都是需要工具支持的。对 于文本处理来说这个比较难,可以利用cscope再加上vim的编辑功能,或 者直接使用structure editor,而各个语言的IDE正是工具。

verification 如何来保证的正确性。测试驱动开发是一种,但不是 全部。更重要的还是逻辑的正确,这个正是programming验证的事情。

interpreters,Compilers,Virtual machines

主要是效率的问题,但是网格计算应该用的虚拟机吧,这样才容易部署。才能有效的计算。

采用Compiler相当于一部分计算前置。

对于树形结构计算可视化

利用目录+文件来实现其可视化, 可以用目录与文件名来表示,变量名用文件名,内容就是值,再用一个特殊的文件当做代码来执行。

还有于图形计算可以化

直接把每一步过程进行screenshot,然后PIL来重新播放,这可以了。而在render时的可视化也这种实现方式。 例如每一步,每十步或者每一分钟生成一个screenshot,就实现了计算的可视化。

元编程

什么需要元编程,元编程一般都至少两级编程,如果能实现三级的编程就可以实现代码的演化。 A->B,B->C,C->A. 这样能不断演化。

具体点来说这些情况用元编程更简单
  1. 原来写代码实现特别复杂,这个时候就要考虑元编程了。把基本操作当做元,然后生成二次操作。 就像画电路板时,一层板太复杂时,就二层板或者多层板来实现,就会发现简单多了。

  2. 其实编译器就相当于是一种元编程

  3. 原来实现不是那么简练,使用元编程,采用二次的编程就会发现简练了很多。

  4. 那一种快速的流水线化,把产生的log格式规整一下直接变成python代码,再添加一些操作,就省去解析的工作。 当然这一步也可以手工做。 例如生成的log,然后再python 来解析log,那为什么不直接写成python的格式。 例如

    log(self,tag,content,time):
       printf "tag = { time: {0}, content:{0}}".format(time,content))
    

    这样出来不直接python 数据嘛。

    • 把一个目录大部分文件都删除,只留下一部分。 如果直接写是不是要一个循环,还得一堆的if else 来判断,挺麻烦呢。还是调试看看出错了没有。 这样呢,ls > del.sh 然后vim del.sh 把不必要删除文件,直接删除掉。 然后 :%s/^/rm -f/ 不就完成了脚本编写。并且不需要调试,基本不会出错。 再后 sh del.sh 一执行不就完了。 是不是简单多了,你所需要的知识也是一条 rm。 并且快速高效简单的完成任务了。

    把代码实现这个过程不就是一个元编程。

生成HTML 也是元编程表现之一。 用代码生成RST 也是元编程表现。 基本是生成结构化的机器友好的代码都代码都是元编程一种,与其中间产物,然后再解析,还不如直接用元编程来的简单高效。

特别是在测编译器的时候,就经常需要生成源码编译,然后再执行其结果然后再比较结果。

另一种情况就是,literate programming. 这是一种新的编码风格。

  1. C用Macro,以及C++用template等等都是一种体现。伴随着元编程的出现的技术那就是JIT. 这个最明显的例子是shader的编程。

同时采用元编程也可以问题分阶段执行,这样可能会减少最终的机器指令,而提高效率。

函数调用本质

就是一个stack状态机的过程,一个进栈与出栈的过程。 所以正常的函数调用,那就是进去多少,就要出来多少。 所以只要CPU本身支持栈操作硬件模型,或者后期软件一个栈模型,就可以实现函数调用的功能。 进栈出栈采用打包的操作,或者采用定界符的方法。然后再加上一些回调实现了执行。例如+ 对应 sub这样的元函数。

运行时候常见错误

#. unhandle exception memory address,access violation reading location: 都是因为使用到一个错误的指针地址,还有一些函数指针,逻辑错误导致指针达到 一个错误的值,例如直接00001之类的一般直接到指内核区域,还有代码区域 ,每一个区域都会不同的读写权限的。如果数据指针指错地,可能会改了别人的数 据,而如果函数指针指错了趣,结果就未知了,会把乱码当指令来执行的。

#. can’t hit there is no debug info in the so file. you could use nm to check debug symbols. C:nvpackandroid-ndk-r10toolchainsarm-linux-androideabi-4.8prebuiltwindowsbinarm-linux-androideabi-nm.exe

XXdoc

Language Usage remark
pydoc topics  
cpandoc/perldoc perlfunc/perl/perlcheat  

Build

解决依赖工具 例如报缺哪一个文件,可以快速查到哪一个package里有这个文件系统里的里 apt-fileapt-cache 都是用来解决这个问题的。 而这些都是包管理解决的问题,来解决这些依赖问题。

另外一个那就是准备编译环境,当然也可以采用预编译的方式来加快编译,也可以 用InrediBuild 来并行编译。无非那就是准备toolchain, 然后各种编译的选项以及环境依赖的准备,在linux下 .configur e来实现配制。对跨平台可以用 xpj.cmake 等等来实现。

对于编译错误,简单的语法错误很容易解决,另一点发现调用不对,或者说找到声 明或者头文件。只要看一下编译选项就知道了,对了gcc,make ,ant 以及msbuild都应该有debug 选项。

对于链接错误,找到对应的库,利用上面的依赖工具来到这些库,还有ingor e undefined symbols,以及循环依赖的使用。另一个那就是 toolchian的工具与库之间版本兼容性,例如linker就是有ld. golden ld 几个版本。可以试着换一下。

并且这些可以参考gentoo,kernel module以及Pentak ,Nexus,QuadD的编译。

COM
shell扩展

命令式程序设计、面向对象程序设计、函数式编程、面向侧面程序设计、泛型编程 多种编程范式。泛型、LINQ ` PLINQ <http://msdn.microsoft.com/en-us/magazine/cc163329.aspx>`_ 和 Futures 对于面象对象的语言,类的静态变量与静态代码是在类进行构造的时候,就已经要 执行了。是先于任何运行时代码。通过自己的debug来快速的深入底层。

什么是Dynamic Programming 与Linear Progr amming?这两个不是编程语言,一个是线性规划与是动态规划。

目前的cardhu 的板子已经做好了lua支持,并且已经有了这个解释器也 已经做进来了。

mparation with lua and C

See also

needing study

OOP

现在才OOP的复用有了更深的认识。 现在对于继承的好处,那就是按需修改。需要什么修改什么。继承与重载。中间插入一个最长匹配查找功能。 得到了非常灵活的应用,只要修改需要的部分代码就复用大部分代码。 跨平台的时候,再通过宏定义把抽象层与实现层的mapping对应上也就搞定的差不多了。 大部分的代码就可以直接复用了。 OOP一部分是事物本身的逻辑,另一块那就是事务的功能。在整个继承关系中,这个函数放在哪一层实现最平衡呢。 太接近基类,每个类的包袱有点大。 太接近低层,复用性得不到更更好的应用。 所以应用的分级分类也是类层次设计的参考基准之一。基类肯定是一些更基本 的东东。这样就可以利用不少的代码。 并且在调用函数的就要有一个不断查询虚表的过程。

函数式编程

函数的本质就是替换,再进一步步何时替换,这样就与变量的生命周期相关的。 而一般的函数变量只能是局部一次性的,所以也就无法惰性求值。惰性求值就是替换的变量的 生命周期。 它的基础 λ演算 Lambda_calculus ,但是它的原理还没有看 明白。这是一个例子 解释1 这个有点浅显了。

APIO讲稿——函数式编程 这个讲的比较浅显易懂,核心只有

三条采用BNF:

1. &lt;expression&gt; ::= &lt; l abel &gt; 1. &lt;expression &gt; ::= λ &lt; label + &gt; . &tl;expression &gt; 1. &lt;expression &gt; ::= (&lt;e xpression &gt;&lt; expression &gt;)

1,2 用于产生函数,第三条产生调用,同时还有两条替换,代入法则。另外还 有那就是部分求值(学名叫柯希求值,也就是自由变量的定义),就像复合函数一 样,每一次只看一个变量。这样就形成λ演算系统。对于递归,还有一个不动点。 不动点就相当于评介返回值。 再加一些基本规则,例如与或非,就构成了完整的 推理系统。而lisp,scheme正是基于此的。

并且函数式编程采用的惰性求值,所以你可以定义奇数,偶数这种抽象的定义,而 在之前的编程中是不存在抽象的定义,只能是一个具体的数。而这些正是符号计算 与证明的基础。

函数式编程方便并行计算。 λ演算 就只有替换与单参数的函数,就是进行替换然后进行基本运算。并且是左 结合的,这也是python里为什么可以连着写的原因。并且函数式编程实现变 量只定义一次,大大简化了后期编译优化工作。 同时从这里也提到停机问题,停机问题,那就是不是能够检测死循环。

63a6e.html>`_ * 形式语义学-Lambda演算 还没有完全看明摆。

通过对 pandocfilter 的python 接口的实现对于函数试编程有了进一步的理解,函数可以嵌套定义,动态构造函数,可以输入来定制函数, 而函数编程更是把函数的自由替换达到M4的水平,同时解决了M4 替换没有边界的问题。

来实现一个最简单的C语言版本的field吧。

static int func()
{
   static int i = 100;
   if (i >0)
   {
      i --;
   }
   return i;
}
思考

Coroutine and Contination IT is just like interrupt of the OS.

Actor、Coroutine和Continuation的概念澄清 Continuation 概念与协程(CoRoutine)

其本质也就是函数本身能够记录自己的状态。这方法多的事,对于python来 说,那就是函数直接当做对象。这像可以很多事情了。例如python中的yield的指令。

另外一个方法那就是函数内部直接使用static在C语言里,来直接记录函数 的状态来实现yield的功能。

– Main.GangweiLi - 16 Aug 2012

如何学习编程语言 每一门语言都有其优缺点,通过学习每一门语言来解决特定的问题,并且掌握每一 门语言的优点。没有一门通用的语言,所以要知道每一门语言的精华,同时对于算 法来说,是无所谓的什么语言的,只考虑功能的话,但是考虑功能与复用的话,这 时候每一门语言才有其不同。

– Main.GangweiLi - 28 Oct 2012

debug 在出了问题,最快的方法不是逐行debug,而是根据业务流 程,然后进行二分法,在函数调用问题上,看一下函数的调用链。其实就是定位问 题界限,是在函数范围内还是范围外。在调用路径上进行二分,这是最快的方法。

– Main.GangweiLi - 30 Oct 2012

加强对于编程语言理论的学习,来提高自己能够快速应用各种语言的能力。而不是 去学,而是去猜与查。

– Main.GangweiLi - 01 May 2013

重载 以前只是知道定义,现在才有了更深的认识,例如你有一个标准流程,后来有了有 改变,但是只有一个地方改变了,其他的都不变,怎么办呢,用一个新类来继承原 来的,只需要需要改变的那个地方重载一下,并且还可在其内部调其父类的内容。 这样机制大大简化了对于变化的应对。

– Main.GangweiLi - 29 Jul 2013

一般情况下,递归算法效率相对还是比较低的,例如我就只是求了,一个二次函数 的递归。发现超过了,20需要的时间就会大大增长了。递归算法的时间复杂度 分析 ,递归是会耗费栈的 ,递归的层数是不是有限制。递归算法,程序开始计算后无响应- CSDN论 坛- CSDN.NET

– Main.GangweiLi - 16 Aug 2013

类型转换 例如int -> short就会截断,截高位,低位保留。符号扩展。

编译 一般情况下,会采用每一条语句单独一段汇编代码。如果没有经过优化的话。但是 如果优化了。就不一定了。因为一般情况下,CPU是只支持四个字节的操作。l oad+ALU+store 模式。所在浮点数计算是汇编代码组合实现的,而 非直接对应出来的。

– Main.GangweiLi - 17 Aug 2013

闭包 也就是子函数可以直接访问父函数的局部分变量,类似于线性空间的闭 包运算。看来是时候把泛函这些东东好看看的时候。简单的东西都已经被实现软件 简化的差不多,下一个时代估计就那些理论了。例外闭包运算可以方便去解决三级 内存问题,你如GPU的多级内存速度不同,函数式可以更加接近算法本身的逻辑 结构,对于编译来说更加容易分析依赖关系。特别适合于自动计算__share __的memory的大小。

– Main.GangweiLi - 18 Sep 2013

反向工程 要充分利用语言的反射机制,与动态gdb的手段。例如动态加入断点。这样可以 大大加快自己的反向速度。

– Main.GangweiLi - 30 Oct 2013

进程的输入输出 以及working space,脚本本身的路径都是很 重要的属性,而二者往往是不一样的。今天所解决的%CD%的问题,就是由经引 起的了,如果没有设置的话,那就是继承父进程的working path当做 working space.

– Main.GangweiLi - 26 Nov 2013

dll 使用动态库方法,一种是头文件,知道这些symbol,在编译的 时候,加入链接库使其通过。所以要使链接能通过也很简单,只要知道让所有符号 能找到位置,但是另一个问题,那就是符号地址的分配问题。哪些符号分配到里。 并且函数根本定义是在哪里。 例外一种那就是要动态加载,这两者其实是一样,自己load这个库,然后取其 直接执行。现在python就可以直接调用.net是不是就样的机制。

直接在脚本语言中调用lib.so 这个在python中是可以直接调用 的,通过ctypes,这个如何实现呢,要么通过SWIG这样为tcl实现, tcl也可直接load dll , 实现方法估计就是把dlimport,dlsymol ,dlclose封装一样,例外就是如让CPU来执行它的问题。 是不是也需 要动态链接器。其本质就是控制CPU的指令执行,把它想像汇编代码就一样了。

当然也一种办法也就是现在JIT,这种动态编译,然后直接执行它。例如s

hell可以直接调用gcc来编译,然后直接执行。

– Main.GangweiLi - 28 Nov 2013

如何用函数编程实现并行 只需要在函数内部实现一个计数器,然后调用一个函数,直接用线程或者申请另外 计算单元直接执行它,并且把计数加1,当然这个计算单元完成之后,再把计数减 1,主函数然后等待或者定时查看计数器当计数为零的时候,就要说明函数调用完 全。当然也可以用C语言再加多线程与硬件驱动来实现并行计算,而cuda就是 这样一个例子。把pentak中多线程改成这种模型。

– Main.GangweiLi - 14 Jan 2014

弱引用 可以用来 实现缓存机制,原理那就是什么时候删除那些不用数据。这个是垃圾回收策略,同时也对用过的数据处理模式。 之前所谈都是预存取,对将要用到数据如何处理。垃圾回收是对用过的数据如何处理。两者很重要。

– Main.GangweiLi - 26 Apr 2014

loop vs recursive 到底是哪种效率高,这个是要看环境与具体实现的,在C 语言是循环,而在函数 式语言里是recursive,另外还要看最终实现是采用栈的方式还是直接j ump的方式来实现,这个是编译有关的,另外在并行环境是如何实现的。与实现 有关。loop 与递归哪一个运算效率高,这是由硬件来决定的,那就是硬件的 每条指令的周期是不样的,循环依赖跳转,而循环依赖call指令,但是cal l也可以由跳转实现的。就看硬件是如何实现了。另外递归的深度与内存大小有关 。

– Main.GangweiLi - 08 Jun 2014

LINQ,Parallel LINQ Language-Integr ated Query 这个就像numpy的那些功能一样,而在C#中它这些 都集成到编程语言了,其实就是相当于把一些通用底层功能直接变成元编程并且编 译器层面去实现。用一定模式然后用LLVM直接直接做掉的。这也就是谓的分层 编译技术。也就是在C#具有一部分SQL的功能。 http://baike.baidu.com/view/965131.htm http://blog.csharplearners.com/tag/directory-enumeratefiles/ http://msdn.microsoft.com/en-us/magazine/cc163329.aspx Running Queries On Multi-Core Processors

Element of programming

程序的设计就是一种迭代过程,研究有用的问题,发现处理它们的高效的算法,精炼出算法背后的概念,再讲这些概念和算法组织为完满协调的数学理论

语言的抽像模型从 汇编->过程->OOP->逻辑->数学模型 asm->C->C++/Python-> prologic/FP -> Haskell等等。 转化的基础,对比python 与C++ OO的实现原理。 如何把OOP还编译成ELF呢。

语言学习发展曲线的三个阶段

阶段一,厚积薄发

写代码,就像写小说,要想写好小说,不读个百八十本小说,肯定也是写不出来的。写软件也是一样,不去研读几个一些经典软件代码,想学好软件开发是不可能的。通过读别人的优秀的代码来学习编程是直接高效的。现在优秀开源代码多的是,你可以根据自己的能力与兴趣来研究那些源码小说,首先是运行一下,看看它长什么样,了解其运行原理,然后再理解其软件架构,好的开源代码,都会相关的文档来讲述其架构呢。有问题去找度娘与谷姐。同时手边放本编程语言的书籍,开始你的代码阅读之旅。根据个人经历,如果对你操作系统比较感兴趣,可以去linux内核与minix内核源码(个人感觉minux是会更具启发性)。如果你对网站比较兴趣,去读一个wiki的源码去看看吧。如果没有特别感觉兴趣的,那就去读coreutils吧,这里面包含操作系统最基本的cd,ls,sort,cat,find,grep之类的源码。了解这些最常用命令的实现原理,对你使用这些命令会更加得心应手,并且在开发大的系统的时候,都会需要这些需要这些常用的功能。你可以容易把这些功能移值进来。

这样的学习好处,在学会了一门编程语言的同时也学习一种构架设计与一个好代码风格。通过研究这些经典代码,对你自己编程技巧也会有很大的提高。你会熟悉常用的一些算法,以及经典设计模式。以后遇到复杂的算法,也知道如何把它拆分几个子算法用这些常用算法来实现。此外也能通过此对一个软件熟练精通,知其然,且其所以然。可以说是一举多得。

阶段二:提高效率

到时候,就会感觉只要给时间与精力,老子什么都能实现,但是现实往往是没有那么多时间与经历,慢慢你会感觉到效率的重要性。并且开始修炼自己的效率。

文本处理处理的能力你与计算机交互大部分都是通过文本与编辑器来实现的。所以文本处理效率高低,直接决定了你工作效率。如何实现批量处理呢。对于文本处理,核心的技术就是正则式,并正则式是在编程武器排行老大:长生剑。对于正则式,光知道是不行的,必须熟练才行,熟才能生巧,才能出效率。所以熟练使用一种支持正则式的编辑器。两大编辑器中的神器,vim与emacs.两者都对正则式具有完美的支持。至于选择两个根据自己偏好(本人在纠结了一年后,最终选择了vim).对工具本身也达到能做二次开发,例如写个语法文件呢,随时写些小的插件,来尽可能使一些重复文本编辑工作自动化。

数据处理能力两组数据的集合集合运算.最常用的交并补。别小看这些东西,经常见一些在分析log,定位问题的时候,人为了找到两组数据差异,就纯粹用两眼一行行对比的。心里在想如果两种数据在数据库里,可以用数据库多方便了。其实那些常用的交并补运算,完全可以用sort,uniq,diff,再加上正则式做一些格式化来实现。三两行就完成了,要比你用眼一行一行对比,快千倍了。同时也要熟练一些小型数据库技术,例如sqlite(因为如果用mysql,postgres用于临时分析用的话overhead太高),因为在定位问题时候,数据量比较大的情况下,你可以用正则式把原数据进行格式化处理,导到sqlite中,利用sql来进行分析。

Debug能力代码不是写出来的,而是调出来的,调试快慢基本上决定写代码的速度。你如果达不到下面Debug合格水平,就要好好修炼了。标准如下:

  1. 批量给添加断点:例如在所有与ABC开头的函数前面添加一个断点。
  2. 自动检测常个变量或命令的状态变动,并保存相应的log,(对于一些复杂的,sometimes ,不容易重现的问题,log是对于定位问题是关重要的)。而不是靠人眼去盯着。
  3. 半自动化调试,在定位关键问题的前期一些步骤是不是能够自动化,执行到关键自动停下来。

Template与元编程:走到遇到大的一些软件功能,有些功能具有通用性,但是有点复杂又很难抽象成函数或类。这时候就要用template与样板代码。例如对于共性比较大,IDE开发环境,会自动为你生成代码模板。但是对通用性还没有达到你让IDE开发工具提供商帮助实现这些模板代码。但是这些功能对于你说,很通用,经常遇到。特别生成配置文件的时候。这个时候,template与元编程就很有用了。如果功能只需要一个静态的template,那么一般template技术就好可以搞定了(建议去研究一下,perl toolkit template库)。如果需要动态的template就用到元编程了(这个建议去研究一下,scheme语言,它做这个比较擅长)。

在这个阶段你要经常到开源社会区去转一转,加入一些邮件组,看看有什么时最新的发展,例如我个加入vim,fowiki,graphviz的邮件组。定期去sourceforge上去看看当前有什么新的软件与技术出现。

阶段三:创造自己的语言

等你走过了第二个阶段,你的手指在键盘键指如飞,能做到指随心动。走到这个时候会感觉效率进一步提升遇到瓶颈了,并且慢慢发现原来编程语言以及工具的一些不足了。会有一种改进这门语言,甚至是重新造一门语言的冲动了。

到了这个时候,你已经对一个领域有了深入透彻的认识。你还需要学习巴斯特范式,编译原理以及lex/yacc做一些词法/语法分析,内存数据库(在构建符号表的时候会用到)。经常用google学术看看最新论文找一些启发与灵感。等这些东西准备好了,就可以对这个领域抽象建模了,基于模型去创建一门小的DSL语言。并为之开发出相应debug工具等。并在实践的应用中不断完善它。

计算机模型发展->计算机硬件体系结构的发展-> 计算语言的发展->逻辑思维的发展。

软件发展这么多年,常规的逻辑都已经有了各种各样的优良的代码库放在里。基本上但反是有规律的,都是可以用计算机来做的,简单的问题,有一些简单的编程模型,复杂的都可以用设计模式的,都用元编程写DSL来实现。 当然各种规律与人的思维是不全面的,是有局限的。这反映在代码上,那不是那么灵活与高效了。 所以常规有规律的事情可以代码来做。而让人来解决灵活与高效。 这一块,也正是人与机器的区别。也是机器短期内取代不了人的地方。当然重复无差错执行有规律的事情是人不如机器的地方。 就像电路板一样,人的作用是重构与架设飞线的那部分。

然后再把重构与架设飞线的那部分,规律化->理论化->转化代码让机器来执行。这样不断的循环往复,来探索更高层级的知识与智慧。以及至于达到代码的自动演化功能。其实就像递归。并且如递归在某些条件下不反回值,直接跳转。这就实现演化。或者递归转化循环。

(三) 自己这几年用过的编程语言一些体会

C语言,最经典的高级编译型语言言之一,性能是脚本语言所无法企及的(只有手工调优的汇编程序的性能才能超过它)。操作系统和设备驱动程序以及嵌入式系统中只能使用编译代码来高效地实现。实际上,当软件和硬件需要进行无缝地连接操作时,程序员本能地就会选择C编译器:灵活的指针应用并且可以直接操作内存,距离“原始金属材料非常近” ——即可以操作硬件的很多特性——并且C的表现力非常强大,可以提供高级编程结构,例如结构、循环、命名变量和作用域。虽然目前随着嵌入式性能的提升也开始对C++,java的支持。但是支持最好的还是C.在这方面你会找到大量的C库来供你使用而避免你重复造轮子的工作。

java语言可以一次编写可以到处运行,如果你的应用环境需要跨平台的,可能你的java是你最好的选择。并且在金融行业,以及ERP软件,以及互联网行业对java的应用库多一些。不过本人只是学生时代学过,没有项目应用经验就不做评论。

C#语言是与面向对象,与java相抗行一门语言,这门语言的产生根源于商业竞争。如果你是windows平台的应用程序,C#,C#.net就是一个巨无霸。可以在windows平台做任何事情。

perl文本处理之王由于本人擅长文本处理。对于perl的了解深入一些。那就谈个人愚见了。

  1. 对正则式的完善支持。对于文本处理,正则表达式是核心。一般语言正则表达式只能硬编码或者对动态生成正则式支持很弱,而perl的正则式是完全可以动态生成。这对于复杂的文本处理极其有用。
  2. .对于perl的诡异符号的解读。每一个诡异符号的背后都是一对常见问题抽象。当你学会一个诡异符号,你就可以简单用一行命令来解决这一类抽象的问题。由于perl自身历史较长,一些常用符号已经常用功能给占用了。所以才看到这诡异符号。当你解决问题时,遇到perl的这些诡异符号,这说明你的遇到问题,已经被历史上的大牛们给完美解决了,简化为几个符号了,而你不必要重复造轮子了。而在别的编程语言中你可能为这个功能再写一坨代码。所以爱上这些诡异符号吧,当然有人会说代码不易读,其实perl有pod文档,让你他随时随地写文档来说明它。
  3. 对于报表格式的支持。如果报表与图表话,你使用perl format指令就知道会有多方便.对于更复杂一些paper工作,例如一些user manual的编写排版。perl的template toolkit模板系统,让你消除排版与多种格式(html,pdf,xml)输出问题。
  4. pod文档系统。大家经常代码没有注释,不易读。另外在编程语言中对没有什么排版支持。那么你来看看perl中,perl语法的灵活性再加上pod文档系统。让你感觉不是在写代码,而是在写文档。把代码嵌在文档中,而不是注释写在代码中。编程界一个倾象文学化编码。可能对它是天生的支持吧。把代码嵌在文章中。并且将来生成多种格式(html,txt,pdf).
  5. web编程。perl是最早用于web编程语言之一。如果你对perl的web编程还是停留在CGI编程,就像你对C语言的认识只是能写hello Word!阶段。perl Catalyst框架,就像MFC之于VC. AWP可以轻松实现web client端编程。
  6. CPAN,windows编程之所以强大是因为有MSDN库。而CPAN就是perl的”MSDN”.

tcl/tk自己的工作语言。工业自动化的标准.以及Expect对于命令行交互的终极武器。

  1. 交互式协作,特别是关于时序与状态的编程是天生的支持。因为tcl/tk发明它的目的就是为了测试电路的。
  2. 所有的事物可以重新定义和重载,所有的数据类型都可以看作字符串。
  3. 可以容易嵌入其他语言中,在大型系统中作为二次开发的脚本语言。
  4. 通用SWIG很容易用C/C+
  5. tcl扩展Expect利用Unix伪终端包装其子进程,允许任意程序通过终端接入进行自动化控制;也可利用Tk工具,将交互程序包装在图形用户界面中。

Scheme语言 作为元编程中主力语言,本身是一种函数式范型语言。Emacs扩展脚本语言,(scheme是Lisp主要方言之一)。DocBook的样式表DSSSL语言。

Lex/yacc 如果想自己实现一门小的语言,那么就来研究lex/yacc吧。也许你会奇怪linux下面的小语言为什么那么多,awk,sed都有自己的语言,就连bc这个小算器都有自己的语言。是不是感觉老外实现一门语言就像家常便饭一样,而对于国人,操作系统还有人敢说自己动手做,但是对动手实现一门语言,还是就充满了神秘与敬畏。其实设计一门语言也没有那么难。如果你想打破这个设计语言这个神秘的面纱,就来看看lex/yacc吧。如果实现多语言的翻译与转换,也可以参考一下antlr.

python, 最近开始使用做代替matlab做一些计算,由于是比较新的语言,集成了之前的语言的各种优点,并且再接近人的自然语言。并且有google这样的大公司的支持。前途不可限量。

厚积薄发

算法基础

算法分析

算法缺失一个公共的表达方法,很难有一个通用衡量,但是LLVM IR却可以补充这个空白。 可以充分利用 LLVM 的分析功能来实现算法的精确分析。 由于IR是通用的,并且IR指令对应到机器执行时间也都是固这的。 所以完全可以拿到这样一样计算模型啊。 差不多精确的知道其需要执行多少时间。 并且用一个树形依赖来计算出来。 所以生一个函数复杂度是能够精确的计算的。 这样解决了采样的精度不足的问题,指令值收集速度,需要的资源太多,太慢的问题。 算法的时间复杂度和空间复杂度-总结

算法的维度计算

时间复杂度并不是表示一个程序解决问题需要花多少时间,而是当问题规模扩大后,程序需要的时间长度增长得有多快。 http://www.matrix67.com/blog/archives/105

the design of a program is rooted in the layout of its data. The data structures don’t define every detail. but they do shape the overall solution.

http://blog.csdn.net/zolalad/article/details/11848739, 常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!) NP问题,就是基于复杂的度来说的。

O(1)表示基本语言的执行次数是一个常数,一般来说,只要算法中不存在循环语句,其时间复杂度就是O(1). 其中 O(log2^n) O(n),O(nlog2n),O(n2)O(n3)称为多项式时间。

而O(2^n)与0(n!)称为指数时间,计算机科学家普遍认为多项式时间复杂度的算法是有效算法,称为P(Polynomial)类问题,而后指数时间复杂的算法称为NP(Non-Deterministic Polynomial 非确定多项式) 问题。

一般来说多项式级的复杂度是可以接受的,很多问题都有多项式级的解——也就是说,这样的问题,对于一个规模是n的输入,在n^k的时间内得到结果,称为P问题。有些问题要复杂些,没有多项式时间的解,但是可以在多项式时间里验证某个猜测是不是正确。比如问4294967297是不是质数?如果要直接入手的话,那么要把小于4294967297的平方根的所有素数都拿出来,看看能不能整除。还好欧拉告诉我们,这个数等于641和6700417的乘积,不是素数,很好验证的,顺便麻烦转告费马他的猜想不成立。大数分解、Hamilton回路之类的问题,都是可以多项式时间内验证一个“解”是否正确,这类问题叫做NP问题

二叉树, 红叉树

树形结构深度不平衡,导致搜索的效率不稳定,所以为提高效率人们开始研究平衡二叉树,而红黑树就是平衡树的一种。使搜索的效率趋于稳定。

变量->数组->链表->树->图->拓扑

树形结构是嵌套结构的一种,嵌套结构其实就是像分形无穷,其实数据结构来说,链表就是表示各种各样的嵌套结构。对应的语言模式那就是递归。

递归函数可以全局变量来记录深度,可以用函数内部的static变量来记录,或也就是所谓的静态变量。 总之这一段空间,就看你怎么样用与规划分配了。

树型结构是最常见的数据结构,例如文档目录,各种协义,以及html,xml等等都是树型结构。遍历方法分为深度优先,还是广度优先。 所以在扁历生成一个列表的序列会大有不同。同时对于各种文档的解析来实现。 也都是从上到下,从前往后。采用的递归式解析。 一般用状态机+ vistor 模式来进行解析。

class Node:
    def __init__(self):
         self.parent
         self.children = []

    def tranverse(self):
         for child,in self.children:
            tranverse(child)

    def parse(self):
        Root = Node(none)

        for line in readline():
            state = state(line)
            newRoot =  New Node
            NewRoot.parent = Root
            Root.children.append(NewBoot)

环的实现

简单用array实现的环只需要 处理 指针最大值时,然后到首。

if index == max
   index = 0

index = value% max

硬件到逻辑变量的对应

这个是基础,基本的硬件单位有bit,byte,WORD,DWORD。 逻辑单位有各种int, short int,long int, 各种float,32bit float以及64bit 的float. 以及char,string 等等。

然后是各种复杂逻辑结构的表示。

array,vector,list,matrix,tuple,map/dict等等。

再往后复杂的tree,图,class之间是可以建立的关系的。

结构化对比的实现

最简单一种遍历,从一个之中,从查找另一个。 效率是n*n.

再好的一点,如果有序的话,就可以不回头。也就是最常匹配算法。就像现在diff算法一样。

但是如果再有一些结构的话,可以把key值或者路径还是最常匹配来得。具体到每一个最具体的项的再用简单的方法。 关键是key map成list是不是有重复的,顺序无关的。这些会影响算法如何实现。

如何进行tree-based structured diff.

例如 http://diffxml.sourceforge.net/

另外一种做法,那是把结构化的变成 linebased. 这就需要先把结构flat化。 例如https://en.wikipedia.org/wiki/Canonical_XML。就是这样的一种。也可以叫做正交化。 现在已经有做的成熟的商业化工具diffDog. http://www.altova.com/diffdog/xml-diff.html

http://archiv.infsec.ethz.ch/education/projects/archive/XMLDiffSlides.pdf.

结构化的对比,难点是检测移动。

另外一种那就是tree2tree的对比算法. https://www.ietf.org/rfc/rfc2803.txt DomHash的算法。

编辑距离的计算,可以采用路径+ node本身hash等等。需要两个信息。 一个是自身的信息。另外一个那就是它的位置移动。 编辑距离同时还可以看到一个人在一个系统中移动轨迹。

X-Diff: An Effective Change Detection Algorithm for XML Documents. http://www.inf.unibz.it/~nutt/Teaching/XMLDM1112/XMLDM1112Coursework/WangEtAl-ICDE2003.pdf node signature + hash的做法。 A Semantical Change Detection Algorithm for XML http://www.inf.ufpr.br/carmem/pub/seke07.pdf,这个方法比较接近自己的算法。

基于xml的一种混合结构化数据对比方法。

看来我的这个东东也是可以发表的。

可以采用样式表的方法,决定对比方法。 看一下html中样式表是如何添加的。就可以实现了。或者采用xpath的方式。

KFIFO

linux kernel是一个大宝藏,如果想找各种实现,去kernel的source tree 里找一找吧。 例如ring buffer一个实现。ring buffer 实现的原点,如何实现下标的循环,但是由于自计算机整数的溢出来实现,再加取模计算,再把大小变成2的n次幂, 这样取模就又变成了取与计算。 http://www.cnblogs.com/Anker/p/3481373.html

quicksort

这个是其实分段排序方法,与二分法是对应的。如果上千万排序怎么的办。 直接发分段,然后再逐段的拼接呢。 中间再字符串搜索功能。

Practice.of.Programming at Page 46.

基本结构对比

初级结构

int, float, string,enum

中级结构

array,list,hash,tree

#.array, 固定,但是存储效率高,采用动态的数据,可能会引起大量的数据搬运,所以初始空间的设置,以及增长方式是要考虑的重点。 #. list 最灵活,但是只能顺序用link来存取,所有二分法,排序算法等等基本上没有什么效果,因为其只能知道与其相关的信息。

对其profiling就要操作的效率。例如每一个查询,修改花了多久。 例如在STL的时候,例如把deque, 换成list的效率的明显变化。
  1. hash 把结合array,list的优点,也是优化空间最大的地方,就像一个矩形,面积恒定。但是如何分配长宽才能达到高效。而决定长宽分配是与存储对象本身的特性以及hash函数 共同决定的。使其存储上更像array. 所以对其性能分析,就要查看其结构利用率。
  2. tree 结合list,array,使其更像list,但是操作效率尽可能像array. 因为在树的排序,就可以用二叉树,平衡树,来加速寻找的过程。 用于分树的key,相当于array中index.
高级结构
  1. struct, 可以根据需求来定制,但是结构固定,也是为什么python的对象中固定元数据部分要struct表示。而动态部分用class来表示。
    同时也可以把相应的操作函数相联起来,这个是比中级结构更强一些点,
  2. class, 添加了数据本身的存取进行权限定义,另外通过继承可以添加,重写原来的struct.

而所有的这些变化点都是根据需要来的。

当把你的问题搞清楚了,采取的数据结构也搞清楚了。这个时候采用什么样语言与库就一目了然了。

队列

Queue, 先入先出的队列, LioQueue,PriorityQueue,Qeueue,deque,heapq. 以及 namedtuple, Counter,OrderedDict,defaultDict.

粒子群算法

都是GA的一种,它简化一些,去掉了交叉与变异。 模拟鸟群找食的过程。它根据自己当前最优值与群体中最优值来进行更新。

蚁群算法

蚁群算法,还是根据蚁群,每一个蚂蚁也向外传播信息。每一个蚂蚁根据自身的精况来决定是否接受全局的信息。 通过触角,其实就是人类交流中的局部信息,完成一个任务 传递是相互遇见的频率,这个是代表什么? 不同的激活个数,导致大脑的差异,这里就是提取信息的不同 在环境密集情况下,如果探测到危险就停止。 ​http://open.163.com/movie/2015/1/6/H/MAFCPCJCV_MAFDA5K6H.html 计算概率密度,通过局部的计算。我想这可能是新的算法

计算模型

  1. reduce 模式
  2. scan 模式
  3. map 模式
  4. count_if 模式
  5. match_if 模式
  6. filter
  7. group
  8. 约束求解,z3等等,并且在excel中也带有大量的solver,其实不需要从零开始写。

执行的模型

在需要并行执行的操作提供了,进程,线程,协程的基本模型。

当然在使用协程是最近才提供出来,可以在编程语言内部支持,相对于线程来不需要那些各种数据的锁机制。

同时实现异步的操作,现在async,await,yield,yield from的等等的支持,在实现各种复杂的建模的时候就会特别方便。

例如javascript系统event的机制实现了多进程打进了自己的语言机制.

而python 现在也支持async,await,yield等支持并行。

这样对于这个系统比较完备了。

进程通信

从课本上讲的 socket,信号量机制。 到后面JSON,以及 protobuf , apache thrift socket 由简单的 点对点到 n:n 的 zeromq , 这个由我想起了expect 实现的n:n 的通信。

quick start 给了一非常好玩的入门。

经过几分钟试用。Racket 集成了传统语言与符号语言的优势。 符号语言的特点那就是非常的简练。把替换用到极致有M4那种功效,但时具有传统语言作用范围的概念,弥补了m4这个不足。 继承了 lisp 与scheme 简单,直接用函数语言的() 来搞定一切。 以及看函数式编程比较别扭,现在是越看越顺了。 并且用起来也非常的简练。

并且还有对象,列表,map等等。

Racket 可以用直接处理图,并且有人拿来画图与做动画。

用racket 当做脚本使用 http://docs.racket-lang.org/guide/intro.html#%28tech._repl%29

当做系统脚本就得像bash那样的的清爽。同时具有python的强大。现在看来racket还是不错的。 这里有全面的 racket-lang reference

代码最简单的方式那就是像bash一样,然后可以对输入与输出进行控制,并用管道,并且语法也要简单。这个正是函数式编程要达到目的。

(cmd para) 不正是bash的语法格式吗,并且直接用()执行一次替换,bash也正是这么来的。

并且直接直接系统命令,并且接近bash一样的简练。 http://rosettacode.org/wiki/Execute_a_System_Command#Racket

#lang racket
(system "ls")

;; capture output
(string-split (with-output-to-string (λ() (system "ls"))) "\n")

计算机程序的构造和解释

通过这本书真正明白计算的构造过程,并且只要支持局部或者全局的静态变量,就可以实现各种复杂的计算。

可以用let 实现不断的替换,从而实现电路的模拟。函数具有内部的状态,可以实现各种复杂的模拟,这个内部状态可以用python yield来实现。

parallel-execute过程。直接实现函数体的并行执行。

关键的就是这种符号替换执行。这个是难点是如何实现的。代码执行像数学公式一样。

对于现实仿真的问题,那就是如实现tick函数,同时也要保持依赖的问题。 后台调用的是 phyx car 来模拟的, 采用最简单的迭代做法,每一个基本过程,然后不断迭代的过程。 就是每一个tick函数如何写的问题。 可以用recket 把迭代与传递链结合起来。

这也就是微分与与数值计算的模型。

racket的发展史。 https://www.zhihu.com/question/22785256

列表的模式匹配

原来没有明白,scheme中,cd,cdr用途,也就是头:余下的。 就是各种模式匹配的基本模式啊。

curry 理论

传统函数理论,就是参数的处理,但是如何curry的处理,函数输入可以是无限的。就像一个递归一样。 这样就把函数输入处理统一化了。其背后那就是个lambda理论。

程序的三大基础

以及其特珠性,

变量,这里的变量就是符号,各种符号定义。 只是基本原语不一样。

所以这个语言基础,那就是lambda calculus,就是不断的替换,直到原语为止。 数据结构也就是符号 以及符号的集合,也就是列表两种。

其语法解释就像shell一样,shell 以行为单位,然后以空格作为分隔符。并且第一个符号为命令。 其余的为参数。关键是什么时候发生替换。

在racket 中用单引号表示符号,不对其替换,只有let 中才对符号进行替换。

在clips中符号定义很宽泛,用任意可以打算ASCII码, 作为符号,而非打印字符找了几个当做定界符。 同时拿;来当comments,

符号语言与一般语言的区别,那就在于多一种符号的定义。 而在Clips中可以用?,$?开头当做变量。

并且也匹配的原语,匹配单个符号,多个符号。不同语言里不是不同的。 在 clips中,?是单个符号,而在$?是多个符号。

ruler,有自己的属性,以及前置条件,以及推导条件。 然后根据前置条件来决定下一步是谁。 不确的因素就是下一个要问的问题。

函数度编程特点

  1. 所有的过程都是函数,并且严格地区输入与输出。
  2. 没有变量或赋值 ,变量被参数替代
  3. 没有循环(递归代替)
  4. 函数的值只取于它的参数的值与求得参数值先后或者调用函数的路径我关。
  5. 函数是一类值

introduction

haskell offical web

debug and profiling

ghci 是可以直接以 :break line 类似于vim的命令直接在解释器下断点。 并且是集成一起的。 ghci-debugger . profiling 见此 haskell profiling .

同时还得安装 apt-get install ghc-dynamic ghc-prof

库的管理

cabal 类似于 perl 的cpan,以及python的pip.

映射与函数的表示

haskell是与数学最接近的编程语言,haskell可以求解哪些隐式函数。它应该是用笛卡尔基再加过虑来实现的。 并且是从数理的脚度来进编程的。

Monad是从另一个角度来分函数进行区分,那就是有限响应与无限响应滤波器,那就是有没有全局变量在函数内部,所谓纯虚函数,就是我们平时编程中最常见的函数,输出只与输入有关。没有记忆状态。而haskell把这个出来进行深入区分,就提出Monad的机制 http://zhuoqiang.me/what-is-monad.html

http://www.zhihu.com/question/19635359

简单的语法

where 相当于 python中 context中with功能。

type C语言的中typedef 的功能。

Hoogle 在线的帮助文档引擎 xs 可变列表的剩余部分。

如何快速读haskell 代码

How_to_read_Haskell .

Thinking

Higher Order Functions 这个其实不是什么新东西,在perl里都有例如sort 排序,你可以使用各种方法传递给它。这个要用函数指针,并且能够动态生成代码最好。但是在这里支持会更好。在这里要习惯,函数内部调用函数。 函数可以相互组合。

更加接近数学定义。用Haskell摆弄函数确实就像用Perl摆弄字符串那么简单。特别适合公式的推导。

– Main.GangweiLi - 19 Sep 2013

偏函数 可以预置一些参数的参数。

– Main.GangweiLi - 19 Sep 2013

lazy evaluate 这样能够把多层的循环压在一层去实现。并且采用了值不变的方式。

– Main.GangweiLi - 20 Sep 2013

前缀,中缀,后缀 表达式 以前没有注意它,在hackell中,这几种是可以转换的,一般函数调用采用是前缀表达,操作符采用的中缀表达,那后缀在什么时候用呢

– Main.GangweiLi - 20 Sep 2013

表表操作 haskell的list类似于tcl中列表,可以嵌套,但是操作符不一样。

– Main.GangweiLi - 20 Sep 2013

产生列表 是不是可以集合,例如数列产生会很方便,但是它的列表可是无限长的,这更加适合公式的证明了。你可以用cycle,repeat等等来得到。

– Main.GangweiLi - 20 Sep 2013

函数式编程的一般思路 先取一个初始的集合并将其变形,执行过滤条件,最终取得正确的结果

– Main.GangweiLi - 20 Sep 2013

利用模式匹配来取代switch。

– Main.GangweiLi - 20 Sep 2013

特殊变量_类似于perl 的$_.

– Main.GangweiLi - 20 Sep 2013

*函数*本质就是种映射,这个ghci中最能体现,你可以指定其定义域与值域,以及这个这个映谢,函数的原型就这个。

– Main.GangweiLi - 21 Sep 2013

同时也需要注意算法定义的动词为”是”什么而非”做”这个,”做”那个,再”做”那个…这便是函数式编程之美!

– Main.GangweiLi - 21 Sep 2013

二分法更加普适化的做法就是快速排序法,不断求不动点。

– Main.GangweiLi - 21 Sep 2013

使用递归来解决问题时应当先考虑递归会在什么样的条件下不可用, 然后再找出它的边界条件和单位元, 考虑参数应该在何时切开(如对List使用模式匹配), 以及在何处执行递归.

– Main.GangweiLi - 21 Sep 2013

%RED%高阶函数部分求值,还是没有讲明白,是不是类似于求偏导时,把别的值当做常量%ENDCOLOR%

– Main.GangweiLi - 21 Sep 2013

map,filter 与perl中map,grep是一样的,这样的东西对于集合运算不是非常的方便,另如图形的形态学操作,是不是可以利用map与filter来操作。

– Main.GangweiLi - 21 Sep 2013

以前我们函数调用,是从内到外,而haskell是从外到内的。 例如求找出所有小于10000的奇数的平方和。sum (takeWhile (<10000) (filter odd (map (^2) [1..]))) 这个是利用惰性求值的特性。来实现的。

– Main.GangweiLi - 21 Sep 2013

fold 的功能就是map与reduce中reduce的功能。不过它分从左还是从右。不过其更方便的是它还有scan这个功能更加方面。做无限长滤波器一样。特别是我们想知道fold的过程的时候,就可以用scan.

循环看做是linear Recurrences,看成数列的计算。不同的你要是数据求和,还是数据相加不变形。 从数列的角度来看循环就变容易很多。

利用C++的模板,很容易数学试的计算,而解决纠结于实现细节。 – Main.GangweiLi - 21 Sep 2013

*$ 函数调用符*它产生的效果是右结合,而一般的函数调用左结合。右结合有什么好处呢,那是在复用函数就会很方便。同时也可以产生python中那种不断调用的 “.”组合了。

– Main.GangweiLi - 21 Sep 2013

模块 更多的类似于perl的语法,并且类与结构体的定义。但是就是没有OO了。另外还有C中typedef的功能。

– Main.GangweiLi - 21 Sep 2013

程序验证与证明,haskell还可以做这个事情。看来把原来的东东都关联起来了。

– Main.GangweiLi - 21 Sep 2013

范畴论,type theory是什么。 domain theory.

Element of programming

程序的设计就是一种迭代过程,研究有用的问题,发现处理它们的高效的算法,精炼出算法背后的概念,再讲这些概念和算法组织为完满协调的数学理论

这本书里讲差不多就是C++的haskwell的实现,从数学理论角度来理计算语言。

value就是内存中一段01序列,而object只决定了如何解决这种序列,每一个变量类型与数据结构都是对这一段01序列的解读。 并且完备性,看来只有bool类型是完备的。其他只是数学表达子集,例如整型等等。

对于函数过程可以分为四类

  1. 只是简单输入与输出的关系。输出只与输入相关。
  2. Local state, 局部的临时变量。

Associativity 操作,min,max,conjunction,disjuntion,set,union,set intersection. #. Global state,用到的一些全局变量 #. own state 只有函数过程自己用到变量,例如函数中static变量。

另外把函数输入当做定义域,而把输出当做值域。 通过这些东东研究,可以函数过程本身做些验证。可以离散数据表达式来表达函数。这样就可以程序验证的方式 来方便验证了。例如任一,存在等等条件。

函数的化简,就变成寻找最短路径的问题。从定义域到值域的一种最简单路径。

递归

递归的overhead太高,我把他变成尾递归,这样变成A^n=A*A^(n-1)的问题。这样可以变成循环的问题。 递归本质是之间通过函数输入输出,动态的传递参数。

优化计算

在本质是数学的表达式的切换,恒等变型就变成方程的推导,变的适合硬件发展。所以在做算法优化的时候,一种就是恒等变型。 例如转化二进制操作。例如移位。 先从数学上解释。然后再到硬件实现。

对于近似计算,不是随便的把9或7变成8完了。而是极数或者变换域的方式在减少计算量在保证误差的情况下。 来减少计算量,例如时域与频域的变换等。

把计算模型->数学模型->计算模型

例如用卷积来进行子串搜索。

iterator

就是把各种遍历非装到一个接口下。只需要根据iterator这个接口来操作,而不用担心下层的实现。这种是基于一维地址的,多维的方法那就是坐标了。

例如对于树的两种遍历,基于只有next的函数的区别了。或者successor(i)的区别。

这种遍历是哪一种呢: #. readable range #. increasing range #. Forward range #. indexed Iterator #. Bidrectional iterator #. Random-Access Iterator

Copying

解决是信息传递的问题。

rearrrange

重排的,或者过虑的机制。以及变形的操作。

Partition and Mergeing

分片与合并。

c++的模板,起到泛化,符号推导的功效。

Composite objects

组合问题,有静态与动态之分。

同时解决动态序列的分配方式,以及内存的分配方式。 而不结构类型,就像一个窗口来改变查看内存的方式。以及用castXXX等等来切换这个窗口。

序的概念

通过在集合的序的重要性。https://en.wikipedia.org/wiki/Total_order

模式匹配

一个列表的模式匹配,来自然的实现语法分析。另一个那就是多态。来实现运行的状态转移,也就解了goto的用途。

介绍

Qt 最主要那就是跨平台。其扩平台性主要是由于自身休息的实现都是元编程与c++自身的语法实现的。

Qt 在windows一下基于WindowsAPI,只是对WindowsAPI进行一层封装。因为windows下GUI编程都是如此。 所以都绕不过去WinProc的,其实有桌面控制都是一个矩形加点图片,然后一个消息的处理。QT也不例外。 也是对其一层封装。

从本质是QT的GUI与message处理是分开的。

GUI -> Widows GUI -> Window message -> QT message process.

跨平台

对于 .so的调用,无非是加载库,得到函数地址,然后预声明一个函数,然后释放库。

对于Windows:

digraph wf {
   LoadLibrary->GetProcAddress->FreeLibrary;
}

对于linux:

digraph wf {
    dlopen->dlsym->dlclose;
}

另外一个问题,那就是调用约定的问题。 qtlibrary_XX.cpp 正是通过添加一层,来调用不同平台的 .so 函数。例如三套相同接口,以三个文件命名。 然后通过宝预编译来加载不同的文件从而保证调用的一致性。

并不是各个平台库都是OOP或者怎么样的,只是人类的OOP处理一下,按照OOP的方式来处理了。 再加上程序的透明性,就更加隐藏这一问题。

slot

一般现在GUI都会用到多线程,不然就有可能每一步非常的慢。一般都是采用并行的操作方法。

所谓的slot,signal,就是宏定义相当于元编程实现了消息队列,再应用程序Winpro得到系统的消息处理之后。 然后我们内部这个消息队列。 并且通过队列实现了跨线程的调用。 利用宏来实现不同于OOP对象树形结构元编程 在各个个大平台上都可以看到。 从表面上看就像callback一样,只不过,需要元编程,自己建立一个mapping的链表。 http://blog.csdn.net/m6830098/article/details/13058459, 而所有 signal, slot都是被处理掉了。

同时通过元编程实现背后的一套逻辑。

但是避免了MFC那种采用宏定义的方式。当然背后也是这些东东了。 这个有点类似于unreal中实现的机制了。connect,disconnect,来维护这个链表。

开发流程

  1. 最简单的GUI,还是可以延用标准c的开发,加入头文件,链接库,编译链接就行了。
  2. 如果对QT类进行扩展,在标准c的编译之前,还需要利用Moc对源代码进行扫描一遍生成QObject等等一堆东东生成代码。
  3. 对于QTDesigner 生成变量,则还需要用uic 为 xml.ui 生成对应的头文件。
  4. QML的开发,如果没有扩展,再标准的c编译之前,还需要额外链接一个带有QObject的Javascript的引擎。
  5. mobile 的开发,把main变成.so 并把所需要 QT .so都放在本地,其实这种干法也就是回到了现在Windows下软件开发的模式。

hellworld

#incude <QtCore>
int main( void) {
     QApplication a(args,argv);
     QLabel label = new label();
     label.text = "helloWorld";
     label.show();
     a.exec();
}

Compile Sample

  1. install QTCreator, install Android Plugin

  2. Chose platform, and slect Plannet Example

  3. configure projects.

    • which toolchain, and and api.

    • Projects>ManageKits>Add

      • Device type
      • Compiler
      • Debugger
      • gdb server
      • QT version

build steps

digraph build {
   qmake -> make-> package2apk;
}
shadow build

就是同一份源码编译到不同平台。

新建一个目录,然后用configure.exe -xplatform 指定平台来进行编译。 http://doc.qt.io/qt-5/shadow.html

No shadow: F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsplanets shadow: F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsbuild-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug

  1. qmake qmake.exe F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsplanetsplanets.pro -r -spec android-g++ "CONFIG+=debug" "CONFIG+=declarative_debug" "CONFIG+=qml_debug"
  2. make mingw32-make.exe in F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsbuild-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug
  3. package Android build sdk: android-23. QtDevelopment: Bundle Qt library in APK use androiddeployqt.exe generate a package.

Qt for Android 部署流程分析

Qmake tutorial 是支持VS project,就像 gnu autoconf,以及CMAKE的功能一样。

通过compile log可以快速得到编译脚本。

F:\Qt5\5.5\android_armv7\bin\qmake.exe" F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\planets\planets.pro -r -spec android-g++ "CONFIG+=debug" "CONFIG+=declarative_debug" "CONFIG+=qml_debug"
"F:\Qt5\Tools\mingw492_32\bin\mingw32-make.exe" -C F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug`
"F:\Qt5\5.5\android_armv7\bin\androiddeployqt.exe" --input F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-libplanets.so-deployment-settings.json --output F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-build --deployment bundled --android-platform android-23 --jdk C:/NVPACK/jdk1.7.0_71 --verbose --ant C:/NVPACK/apache-ant-1.8.2/bin/ant.bat

How to setup Nsight Tegra with Qt

  1. Download QtCreator from http://www.qt.io/download/

  2. Intall it to <your QT path>. for example F:\Qt5

  3. Install android plugin

    • Open Maintain tool by startMenu>Qt>Qt MaintennanceTool
    • Select Add or remove
    • Select Qt component you want. for example( Qt>Qt 5.4>Android armv7).
    • Click next until finish.
  4. Get an android samples

    • Open Qt Creator
    • Click examples
    • select right platform and the sample name
      we use (Qt 5.5.1 for android armv7, sample name: planet)
    • double click open the sample
  5. get build cmd from the project configuration.

    • qmake

      qmake.exe F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsplanetsplanets.pro -r -spec android-g++ "CONFIG+=debug" "CONFIG+=declarative_debug" "CONFIG+=qml_debug"

    • make

      mingw32-make.exe -C F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsbuild-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug

    • package

      • Android build sdk: android-23.
      • QtDevelopment: Bundle Qt library in APK
      • Use androiddeployqt.exe generate a package.

      "F:Qt55.5android_armv7binandroiddeployqt.exe" --input F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-libplanets.so-deployment-settings.json --output F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-build --deployment bundled --android-platform android-23 --jdk C:/NVPACK/jdk1.7.0_71 --verbose --ant C:/NVPACK/apache-ant-1.8.2/bin/ant.bat

    • put these build cmd into windows .bat. for example compile.bat

    ..code-block:: python

    “F:Qt55.5android_armv7binqmake.exe” F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsplanetsplanets.pro -r -spec android-g++ “CONFIG+=debug” “CONFIG+=declarative_debug” “CONFIG+=qml_debug” “F:Qt5Toolsmingw492_32binmingw32-make.exe” -C F:Qt5ExamplesQt-5.5canvas3dcanvas3dthreejsbuild-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug` “F:Qt55.5android_armv7binandroiddeployqt.exe” –input F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-libplanets.so-deployment-settings.json –output F:/Qt5/Examples/Qt-5.5/canvas3d/canvas3d/threejs/build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug/android-build –deployment bundled –android-platform android-23 –jdk C:/NVPACK/jdk1.7.0_71 –verbose –ant C:/NVPACK/apache-ant-1.8.2/bin/ant.bat

  6. Open VS and Create external build system for the project.

    • Additional C/C++ source Directories: F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\planets
    • Additional Library Symbols Directories: F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug\android-build\libs\armeabi-v7a
    • GDB Working: F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug\android-build\
    • Java Source Directories: F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug\android-build\src
    • Java Classes Directories: F:\Qt5\Examples\Qt-5.5\canvas3d\canvas3d\threejs\build-planets-Android_for_armeabi_GCC_4_9_Qt_5_4_2_0c4ce3-Debug\android-build\libs

QML

QT meta language, 就像tk一样,内嵌javascripts的解析器,界面就像HTML一样,不过不是标记语言。采用描述语言。 需要扩展都通过QtDeclarative来注册实现。有点像androidSDK使用XML来写界面。 http://www.digia.com/Global/Images/Qt/Files/Qt_Developer_Day_China_2013_Presentations/Qt%20Quick%20and%20Qt%20Quick%20Controls%20intro.-%E5%A4%8F%E6%98%A5%E8%90%8C%204-5%20PM%20-%20Qt%20Dev%20Day%20China%202013.pdf

现在的一种resource 编译方式,直接生成数组,就像自己平时构造python数组是一样的。QT的resource把资源直接编译成字节数组了。

原来方式是一个个control来放,现在直接

viewer.engine(().addImport()
viewer.setSource(QUrl(grc:/planets.qml"))

采用类似于Unreal的组件开发,由c++实现组件,而Javascript再上层做界面的操作。交互是Javascript有QT的对象接口可以直接访问。就像Squish中, 可以使用各种脚本来进行操作组件。 http://brionas.github.io/2014/08/15/How-to-integrate-qml-with-C++/ 深入解析QML引擎, 第1部分:QML文件加载

窗体的创建

http://blog.csdn.net/tingsking18/article/details/5528666

用调试断点,就可以直接查看其效果,其本质还是对Windows class 的封装,实现了一套自己的窗口管理体系。 而这个窗口体系维护了一个数据结构,button本身不具有什么深浅关系的。

对于问题的调查,

QDateTime

会用到系统 Locale设置,不匹配时就会出现。

moc(Meta-Object Compiler)

就是元编程中,先把meta-object 生成目标源码,这种做法与Unreal的 UBT是一样的。 例如在代码中有

Q_OBJECT

就会生成代码,这样是一种变相解决编译语言非动态特性,并且把语言进行了二次调度。编程语言本身的灵活性。

元语言则提供了语言本身的编程。 所以元语言编程,特别是任何对现有语言进行二次开发,为其添加特定的数据结构,例如MFC,QT等的消息循环,以及内存管理机制。但是又不减少语言本身的灵活性,只是为其添加了额外的功能。

UIC(User Interface Compiler)

类似于Moc,就是读取QT Designer 生成*.ui, 然后生成对应的C++头文件。

http://doc.qt.io/qt-4.8/uic.html

自从Android之后,直接用XML来生成变面的方式,很流行。用XML来生成页面是从html学来的, 再往前走一步那不是QML这种方式,只是XML这种可读性更好了,把定界符给改了。 再加一个brower的引擎。

Python 编程

Introduction

Until now, the python is very concise language. It have all the metrics of previous language. You can the shadow of every language in it. it use the space indent for delimiter. it adapt the function programming technique. it use the file name as module.

It has the OOP and basic procedure language. the data structure : list, array, dict(hash table). the data has constant meta-list just the C/C++. it use =global= instrument is just like the tcl’s “global”. there is import is mix namespace import and export and source. the class just like the java, the first parameter of method is self point. there is also exec,eval assert raiseError just like tcl. and lambda in Scheme.

os and sys 对于系统的操作

可以考虑用python脚本来代替shell脚本。并且要考虑并行的问题。

#. can-i-use-python-as-a-bash-replacement 这个讨论已经写的很详细了。 there is two core module: sys and os. just like info in tcl. sys is most about python interpreter. for example, the sys.path is object search path for lib and module. here

并且以后要把print都要换成`logging <http://victorlin.me/posts/2012/08/26/good-logging-practice-in-python>`_来全用。 用python popen处理一些系统的命令它会返回一个对象,

logging

import os
print os.popen('ping g.cn')
#!/usr/bin/env python
import sys
import os
topdir = os.path.dirname(os.path.abspath(__file__))
if sys.platform == 'cygwin':
     topdir = os.popen('cygpath -a -m %s' % (topdir), 'r').read().strip()
sys.path.append(topdir+"/lib")
from nose import main

if __name__ == '__main__':
    main()

它返回一个文件对象,你可以对这个文件对象进行相关的操作。

但是如果你想能够直接看到运行结果的话,那就要用到python os.system,用了以后,立竿见影! 还是上面的问题:

import os
print os.system('ping g.cn')
输出的结果是:
64 bytes from 203.208.37.99: icmp_seq=0 ttl=245 time=36.798 ms
64 bytes from 203.208.37.99: icmp_seq=1 ttl=245 time=37.161 ms

直接使用VS2015 PSVT的功能,可以条件断点等等功能,非常的的方便。

Profiling

python -m cProfiler XX.py

epydoc

生成callgraph

Python 内在函数

builtin namespace
dir() __name__   __file__
built-in command http://docs.python.org/2/library/functions.html    
Tuple 介绍 不可变的list    
SymbolPy 符号计算    
MatPlotlib 画图    
NumPy 矩阵与线性代数    

Python 包管理与开发环境

Python的包管理就像perl 的CPAN一样。 easy_install 与pip 新替 就是python 的CPAN。http://jiayanjujyj.iteye.com/blog/1409819 egg文件就是python的打包文件。 可以用setup.py 打包,同时也可以pbr 生成setup.cfg的配置文件。这个在包本身比较复杂的情况下会非常有用,就像gnu libtool一样。

  1. 打包机制 .egg文件
  2. perlbrew python virtual environment and multi-version perl的虚拟环境。ruby也有。
  3. pythonbrew online document
  4. VirtualEnv 和Pip 构建Python的虚拟工作环境 这个写的不错,virtualEnv 解决的就是不同库依赖之间的问题。并且有实例。而pythonbrew主要解决了不同引擎之间切换。同是兼容了,virtualenv这样的样的环境。这样就可以在版本与库之间进行选择了。就像pyrobot一样,就可以选择环境,选择brain.
  5. 用pythonbrew指是采用哪一个python, 而virtualEnv 指的在哪一个环境下使用python. 其本质是与linux的chroot是一样的道理。
  6. python configparser.py 以后配制文件,可以使用它,而不用自己在写分析了,有了一个标准的分析库。它采用的是windows INI 文件格式。
  7. 用 twine 可以把自己包提交到PyPI上。

包的开发与目录结构 import 可以是整个包也可以只是变量,函数。但是python把命名空间与import并且source的功能混在一起了。看起来有一些不舒服。 for install and manipulate the package of python, just like pkgIndex in tcl. there is distutils.core. which manage the preprocess, compiler,linker, verification, install.here has some useful command:

cmd
setup                    
distutils.ccompiler set_libraries add_library_dir add_runtime_library_dir define_macro dir_utils file_utils (mkdir rm copy_tree) ` distutils-simple-example <http://docs.python.org/2/distutils/introduction.html#distutils-simple-example>`_ this is helpful when you are writing more code.
when you install some extention module written from C/C++. you and build environment. you can gcc or MSBUILD. setup.py compiler options manual                    
如果要`打包成可执行文件 原理 <http://wiki.woodpecker.org.cn/moin/LeoJay/PyPackage>`_,                    
pyInstaller http://www.pyinstaller.org/ticket/512 opencv 好像还没有支持                  
cx-freeze                    
py2app mac 平台                  

还有setup的编译环境,其实就是一个makefile,就是类似于ndk的东东,只是写了一个类与配制文件,把这些手工的步骤给封装了起来,对于C/C++编译就是那几步了,同时会把编译,链接的库的路径等等都会设置好,系统默认值一些值都会自动搜索系统目录,例如对于VC就会使用注册表信息去找这些。而对于linux gcc,windows cygwin,mingw等等都是这样的。 对于python 自己distutil 包setup 相当于python的grudle一样,在里面把所的配置信息写好,扩展的化就类似distutil.compiler类来做了。 并且theano也是采用这样的方式来封装nvcc的。

以及各种开发模式,插件式与模块化的区别与连系是什么。 例如python ETS插件式开发,http://code.enthought.com/projects/ .

包管理的难点,在一个单一环境是容易的,难点各种包管理模式之间的冲突,但是apt-get 与pip 如何兼容的,包管理本身也需要一定的信息结构。例如依赖关系,linux讲究的相互共享,这就造成了,系统升级之后,就莫名其妙的不能用了,而windows采用的是自包含,所以现在windows会变非常大。包管理的特点 依赖关系,方便的查询操作,以及编译环境的准备。这个有perl,python,以及gentoo的包管理,都非常熟悉。

pip 现在支持一次安装列表 pip -r requirements.txt 同时还支持zip,以及从git,或者svn直接安装。 而不需要每次手工来一条条来做了。

https://pip.readthedocs.org/en/1.1/requirements.html

namespace

  1. python学习笔记——模块和命名空间 在python中是一切对象,这个与lisp一切结数据的模式是很像的,现在还不知道hackshell的编程模型是什么。python的几种命名空间,对于简单函数调用,python可以像传统的面过过程一样,直接调用其函数,也可以采用面向对象方式。python面向对象机制是不是有一点像perl,但是它的面过过程的调用是通过静态函数来变通的。还是本来两种方式都是可以的。
  2. build-in name space
  3. global name space
  4. local name space

关系的表达就是最直接的方式之一,那就是指针,类中各种关系其实都类,都是一种指针 在数据库那就可以叫做外键。

对于静态变量可以当做是空间变量的一种吧。其本质还是变量的作用域不同。现在其提供了多种粒度的变量 全局变量,例如环境变量,以及python自己的全局变量。可以供包之间共享信息与通信的。 包变量,用于包内子包或者类之间的通信。 类静态变量用于,所以所有实例之间需要通信的变量。 类变量,同一个实例各个成员函数之间的通信变量。 函数静态变量,这个在C中有,用于多次调用这个函数之间的通信。 特别是在神经网络进行优画的时候这个用的最多。当然也可以把这些拿到其他的方式来实现。

string,list,dict/hash and tuple

String is Object itself, so when you manipulate string. you do it like this “XXXX”.append(“XXX”); one of important is regular expression. for python you use `re <http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html>`_

+=============+====================================================================================+ | u’a string’ | prefix u stand for unicode character | +=============+====================================================================================+ | r’a string’ | prefix r stand for original string means regular expression is object too. | +=============+====================================================================================+

pattern = re.compile(r'hello')
match = pattern.match('hello world!')
match.group()

match 必须是从头开始匹配,search是不必的。

dictionary{}必须是key-value对,核心是哈希,内容可以使任何元素,可是实现删除,del and d.clear()。里面的key是虚幻的。

list中是有顺序的,因此可以insert, append, extend. list中就是数组操作,比如插入,remove,她的所有操作都是基于index的。里面的index是顺序排列的,比如123.。 应该讲的有条理些,如果我现在不做,就找不到工作。

tuple ()就是不可以改变的。

为了能够确定对象的属性,python使用一些系统参数比如 str, callable, dir:

str 主要是字符串操作,可以帮我找到 modulate的位置,其他的有么用不太清楚。

callable 主要从对象中找出函数。

dir列出所有的方法的列表。

getattr()得到对象的属性。

doc string 可以打印方法函数的document。

Python vs.C/ Matlab

其智能化主要体现在 “+”可以同时实现 字符串连接和算术运算。

多变量赋值,简化操作,就像perl一样。

逻辑运算:and,or 相比更加容易理解。

很多格式都是规范的,比如 indent,list.

Python 中 的class

什么是类,我想就是分情况,然后需要的初始化__init_,一个class定义一种__init__就是初始化函数,里面的self就是参数赋值,然后就是def各种方法,利用参数值。

另外python中class 中各种特殊的属性,可以class具有各种功能,例如__call__这样就可以把class变成了函数,并且可以有各种状态。另外还有各种操作符。

python 本身没有抽象类的概念,实现这些约束:如果子类不实现父类的_getBaiduHeaders方法,则抛出TypeError: Can’t instantiate abstract class BaiduHeaders with abstract methods 异常 则需要from abc import ABCMeta, abstractmethod 来添加这些依赖。 主要是实现 抽象方法与抽象属性这两个关键字。

各种字符串之间的转换(dictionary->str,list->str)

string ->list split,splitlines list ->string, join list -> dict, dict((key,value)….) or dict(key=value,,,,,)

list->str 可以通过”“.join(li)实现, 但是不要通过str(),这种属于硬转换(只是在外面加了一个“”).

str-》list, bd.split(“,”)好像不行,因为split适用于把有一定界限的str分离。

dict-》str, str(dict) 我觉得不太行,还是硬转换。??

str->dict, eval(str)很多网站说这个是字符串转换,但是我觉得并不能成为字符串转换吧。原意是evaluate。

rspr,这个用来反回对象的文本显示。

python comments

comments is important part of an programming language. most of the document is generated from the comments in code. One orient, is putting document into code, which can be easier to maintain and update. so structure and format is important for an programming language. take compare several language.

+========+=================================================================+=================================================+ | perl | has pod document system, and << STRING, and format report | pod2tex,pod2man,pod2pdf | +========+=================================================================+=================================================+ | java | javadoc | | +========+=================================================================+=================================================+ | c /C++ | if you adopt the C/C++ syntax, you can use doxygen to generate | | +========+=================================================================+=================================================+ | python | __doc__ ,__docformat__,reStructuredText | python has puts comments as variable of python | +========+=================================================================+=================================================+

you can access the comments from in the code of __doc__. one usage for this is just like CAS testcase steps:

def tounicode(s):
    """Converts a string to a unicode string. Accepts two types or arguments. An UTF-8 encoded
    byte string or a unicode string (in the latter case, no conversion is performed).

    :Parameters:
      s : str or unicode
        String to convert to unicode.

    :return: A unicode string being the result of the conversion.
    :rtype: unicode
    """
    if isinstance(s, unicode):
        return s
    return str(s).decode('utf-8')

http://docutils.sourceforge.net/docs/peps/pep-0257.html 也就是基本的原则,语法还可以用markdown以及sphinx,只是函数模块类等的一第一段注释会被处理成文档。并且支持中文用u”“”就可以了,以及r”“” 256,224,216,这几篇都看一下。

command line

for python, you can process comand line options in three way:

  1. sys.argv
  2. getOption
  3. plac module Parsing the Command Line the Easy Way
  4. argparse this one looks good for me, it just like getOption, but stronger than her.
  5. Command module

mutli-thread of python

多线程与进程一样,可以动态的加载与实现,而不必须是静态。并且可以是瞬间的,还是是长时间的。之前的理解是片面的,这个受以前学习的影响,一个线程或者线程就像一个函数根据其功能的来,不是说是线程就要有线程同步。可以是简单的做一件事就完的。例如实现异步回调呢,就可以是这样的,把回调函数放在另一个线程里。用完释放掉就行了。C#线程篇==-Windows调度线程准则(3) 如何让自己的程序更快的跑完,其中在不同提高算法性能的情况下,那就是占一些CPU的时间片,优先级调高一些,就像我们现在做事一样,总是先做重要的事情。然后按照轻重缓级来做。就像找人给干活的时候,你总经常会说把我的事情优无级高一些。先把我的事情做完。 这个应该可以用转中断来实现。 Lib/threading.py

*Python threads synchronization: Locks, RLocks, Semaphores, Conditions, Events and Queues <http://docs.python.org/2/library/threading.html>`_

例如以前的,我都是利用傻等的方式,还有时间片或者用sleep,其实异度等待的机制可以用`线程事件来高效实现 <http://blog.csdn.net/made_in_chn/article/details/5471524>`_

协程的实现原理

协程本质相当于软中断,能够在函数中暂停,并且还能返回继续执行, C语言的函数内部的static变量的模型, 语言的调用模型ABI模型直接采用了原始的CPU的内存模型,C语言就是这个么干的,但是python就却是自己直接用在堆上实现的。 相当于hack了一把function frame 并且没有只是原来first in,last out原型而在原来的stack 模型上又加上了一个引用计数的模型。 http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html 有详细讲解了其模型。

把这些东西优化到编程语言这一层那就是协程了,python 中 yield就是这样的功能。通过协程就可以原来循环顺序执行的事情,变成并行了,并且协程的过程隐藏了数据的依赖关系。 对于编程语言中循环就是意味着顺序执行。如何提高效率,实别的计算中数据依赖问题,把不相关的代码提升起来用并行,采用协程就是这样的原理。 这也就是什么时候采用协同。什么时候采用协程了。这个优化是基于实现的优化是基于你的资源多少来的。所以在python对于循环进行了优化。所以写循环的时候就不要再以前的方式了,采用计算器了,要用使用yield的功能。来进行简化。coroutine, 线程就是它什么时候执行,什么开始都是由内核说了算的。你就控制不了。coroutine就是提供了在应用程序层来实现直接的资源调度,如果更直接控制调度,另一个就是采用CUDA这样更加直接去操作硬件资源。 yield 可以相当于 C语言中函数内static, 但是 yield有点类于return 但是yield 之后的代码也可以继续执行。 相当于 last PC pointer https://hackernoon.com/the-magic-behind-python-generator-functions-bc8eeea54220

并且yield 实现一个 callback. 也是非常方便的。这个实现反包。被调用函数反包调用。 就是一个中断的机制,并且方便实现一个二分,或者多分。并且每一次yield都可以有返回值。 例如 teardown 与setup 写在一个函数里。 yield相当于中断的简单化,例如用switch + yield就可以不同的信号量,就像 raise一样。

def test_tearupdown():
    make tempdir
    yield tempdir
    clean tempdir

def test_boday( temp_dir):
    print("in test")
    print("aa,bb")


def exec1_test(test_func,tearupdownfun):
    fun_fear = tearupdownfun
    test_boday(fun_fear.next())
    fun_fear.next()

@exec1_test(tearupdown)
def test_boday2(tempdir):
    print("test body3")

对于状态进度的更新有了一个更好的方法,注册一个时间片的中断函数,每一次当一个时间片用完之后,就来打印一个进度信息就不行了。这样就可以实时的知道进度了。 Linux环境进程间通信 目前看来需要在进度的SWap时来做的,需要内核调度函数提供这样一个接口。那就是在线程切换的时候,可以运行自定义的函数。其实这个就是profiling的过程。在编译的时候,在每一个函数调有的前后都会加上一段hook函数。我们需要做的事情,把切换的过程也要给hook一下。这个就需要系统的支持了。coroutine的实现 linux下可以有libstack库来支持,当然 了可以直接在C语言中嵌入汇编来实现。用汇编代码来切换寄存器来实现。

现在对于C语言可以直接操作硬件,这种说法的错误。同为一种语言凭什么说C可以操作硬件。原因在于好多的硬件直接C语言的编译器而己尽可能复用以前的劳动成果而己。只要你能把perl,python,各种shell变成汇编都能直接操作硬件的。

现代语法

List comprehensions 也开始发展perl的各种符号功能

Ilterators generators

a = [expression for i in xxx if condition]   //list comprehensions
a = (expression for i in xxx if condition)   //list generator
a = [(x,y) for x in a for y in b] 这个不同于双层循环
a = [expression for x in a for y in b ]这个相当于双层循环

再加上 http://stackoverflow.com/questions/14029245/python-putting-an-if-elif-else-statement-on-one-line 对了可以使用lamba来实现

Python yield 使用浅析 原理也简单,既然可以lamba 可以部分求值。yield的机制也就是执行变成半执行。参加的功能那就是计录了前当前的状态。当下一次调用时候,就可以直接恢复当前环境。执行下一步了。yield的功能其实就是中断恢复与保存机制。每一次遇到就这样保存退出。并且也保证了兼容性。下面的例子也就说明了问题。其实就是集合的表达方式问题。我们采用列举式还是公式表达式。 数据的表达方式就是集合表现方式。研究明白了集合也就把如何存储数据研究明白了。列表相当于我们数据采用列举式,而生成式我们采用是公式表示。

部分求值,现在发现在其实也很简单,函数就是一个替换的过程,部分求值,什么时候替换的过程。难点在于传统的函数值是要释放的,而部分求值,反回来另一个函数,并且这部分求值当做参数传出来。这样实现部分求值。另一个那就是变量在函数中不同作用域,不能随着函数的消失而消失。直接引用全局变量或者static变量都可以达到这个目换。并且本身支持函数对象化。更容易做到了。

range(6)  [1,2,3,4,5,6]
xrange(6)   相当于定义了类,最大值是6,最小值是0,步长为1,当前值为0.每调用一次,更新一下当前。当然利用这个是不是可以产生更多数更加复杂表达方式。同时也解决了以前在CAS的那sendMutliCmd中循环,无法记录自身当前值问题,必须使用global去使上一层变量的方法,现在通过这个yield方法就会非常方便。这个其实编程语言中闭包问题,就是在子函数中调用复函数中局部变量,在tcl中可以使用upvar来实现。使用动态代码实现一个子函数来进行调用。而在python这里可以直接yield来产生。同样也可以自己实现。
class repeater {
  init;
 step;
  current:
  next: 调用一次method
  reset:
  set:
  method{ output=current+step;current=output}

}

这样就用计算代替了存储。并且解决吃内存的问题。

而对于tcl 中的foreach的功能可以利用zip + for 来实现

for x,y,z in zip(x_list,y_list,z_list):

`65285-looping-through-multiple-lists <http://code.activestate.com/recipes/65285-looping-through-multiple-lists/>`_  可以使用map,zip以及list来实现。
`yield与labmda实现流式计算 <http://www.cnblogs.com/skabyy/p/3451780.html>`_

itertools 更多的迭代器可以采用这些,这些采纳了haskell中一些语法。

Descriptors properites

Decorators

  • Python装饰器与面向切面编程 %IF{” ‘这个其实是perl那些符号进化版本’ = ‘’ ” then=”” else=”- “}%这个其实是perl那些符号进化版本

其实本质采用语法糖方式 ,其实宏处理另一种方式。在C语言采用宏,而现代语言把这一功能都溶合在语言本身里了。decorator直接采用嵌套函数定义来实现的。最背后是用lamba来实现 的。 其本质就是宏函数的一种实现,并且把函数调用给融合进来了。本质还是 函数管道的实现。 本质就是闭包计算,这个语法糖就是多级闭包的实现。 https://foofish.net/python-decorator.html 而多参数的语法糖,是靠两级闭包来实现的。

@wraper
def fn():
    do something

a().b().c()

a() | b() |c()
$a bc $ a bcd $c (in haskwell)

它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于

使用 decorator 的好处,实现函数的原名替换,同样的函数名却添加了实现。有类似于Nsight 中 LD_PRELOAD 中那API函数一样的做法。 任于参数如何传递就是简单函数传递。

至于变长修饰变长函数 也是同样的道理。 http://blog.csdn.net/meichuntao/article/details/35780557 其实就是直接全用args就行了,就传了进去了,只是一个参数传递的过程,这个pentak中automation到处在用了。 把要wrapper的参数传递进行去。 http://blog.csdn.net/songrongu111/article/details/4409022 其本质还是闭包运算一种实现,基本原理还是利用函数对象以及各自的命名空间来实现。 而不用知道函数要有固定的参数,修饰变长函数。这个直接看源码的函数调用那一张,采用的命名空间嵌套的用法,原则最里优先。

functools 提供了对于原有函数进行封装改变的方便方式。也就是各种样的设计模式加到语言本身中。

python对于循环进行了优化。所以写循环的时候就不要再以前的方式了,采用计算器了,要用使用yield的功能。来进行简化。 yield就相当于部分的C函数中static变量的功能。并且 比他还强的功能。另外也可以global的机制来实现。 map,reduce机制,例如NP就经常有这样的操作,例如

reduce,map与函数只是构造计算中的apply函数一种。 例如自己实现那个累乘也是一样的。

reduce,只一次只取列表两个值,而map每一次只能取一个值。对于取多值的,可以用ireduce,imap

def reduce(function,iterable,initialzer=None):
    it = iter(iterable)
    if initialzer is None:
        try :
          initialzer = next(it)
        except:
     for x in it:
         accum_value = function(accum_value,x)

其实这样的函数就相于一个神经元。 python iteral_tool 就相于一个个神经元。

x,y,z=np.random.random((3,10) 每一个一行。

并行处理

以后要把for循环升级到map,reduce这个水平,两个概念是把循环分成有记忆与无记忆,map就是无记忆,reduce是有记忆。 Python函数式编程——map()、reduce() 就是为了并行计算,但是内置的这两个函数并不是并行的, 可以使用 multiprocessing 来进行。

python中动态代码的实现

一种实现方式,自己手工做一个函数表 hash dict,key就是对应的字符串,其实完全没有这个必要,动态创建本来就是为减少维护与编码,这样写我一直用if,else 有什么区别呢。

可以利用sys.modules[‘__main__’] 再加getattr来实现。同时也可以用locals,globals等等hashtable直接可以用。而不必自己手工再做一套。

cmd = "update_{}".format(product_list[productIndex])
cmd = getattr(sys.modules['__main__'],cmd)
cmd()

C extending Python

对象机制的基石——PyObject PyObject 本质就是结构体指针加一个引用计数。

shutil

学见的文件操作,copy,move都在这里有,另外打包函数也是有, make_archive,基于 zipfile,tarfile来实现的。而这些后台都是调用zlib,或者bz2.

简单的创建目录,os.makedirs 都有。删除文件都有。对于目录操作。shutils. 但是对 shuttil.copytree一个问题那就dst 目录必须存在,用distutils.dirutil.copy_tree就可个问题。

如何想更灵活,就只能用os.walk自己写一个。一般都是判断一个目录与文件,另外那就是符号链接了。

读写二进制文件可以用,struct,以及unpack,pack函数。

difflib

python 有现成的diff库可以用,所以也可以在ipython 调用 difflib来当做命令来用。

python的标准库比较全面,有点类似于libc,网络,tty,以及系统管理等等相应的库。 http://python.usyiyi.cn/python_278/library/index.html

test framework of python

Data structure

embeded dict. what-is-the-best-way-to-implement-nested-dictionaries-in-python 其中一个方法hook __getItem__ 来实现,但是有一个效率问题,其实那种树型结构最适合用mongodb来实现了。并且搜索的时候可以直接使用MapReduce来直接加快计算。

High-performance container datatypes 同时还支持 ordered Dictionary 同时支持对基本数据结构进行扩展,利用继承

如果让dict 像一个类样http://goodcode.io/articles/python-dict-object/, 一种是采用self.__dict__ 来实现,另外一种采用__setattr__,__getattr__,__delattr__的方法来实现。

要想高效的利用内存分配还得是C/C++这样,自己进行内存的管理。管理原理无非是链表与数组。并由其排列组合出多结构。

python data analysis

python主要用于大数据分析的比较多,大的数据分析主要包括三个方面: 数据本身的存储,分析,批量处理,以及可视化的问题

数据存储,关键是效率

  1. csv 最简单直接,并且方便扩展
  2. xml 机器交互性强,但是不算太方便
  3. npz 最简单直接
  4. python 本身的串行化,效率不高。
  5. pyData/pyTable 对大数据的存储
  6. h5py 这个压缩存储

best way to preserve numpy arrays on disk

分析计算 #.numpy,pandas,`blaze 下一代的numpy,总结pyData,pyTable,pandas <http://blaze.pydata.org/docs/latest/overview.html>`_ 例如优化算法,以及优化求解等,同样可以pyomo等之类的库来实现。

可视化: pylab,VTX以及直接利用opengl来计进行。 以及reportLib 对于pdf的直接读写。以及使用pyplot来进行二维以及三维的画图。pandas plotting .

正是由于python的一切对象机制,使其把投象与具体结合起来,可以很方便应用到各个学科与领域,其实这个本身就是一个知识库。现在需要一个快速推理管理工具。

专业领域的应用

例如对编程本身的支持,

但是python本身也自身的缺点,一个方面那就是GIL,并且他的效率是依赖C或者其他。不过python的一切皆对象方式不是错。可以把python当做一个描述语言。 具体让编译器来做翻译。 一个软件好用不好用的关键,是不是大量相关的库,在科学计算领域python是无能比了。自己尽可能用高阶函数来表达核心的东东,而不必纠结实现细节,其实道理都是一样的。 对于python的扩展这里提到cffi来扩展。以及bitey. 以及用distutils功能完全可以用来实现gradle所具有一切功能。 例如强大的 c++ boost库,同样也有python的接口 见 http://www.boost.org/doc/libs/1_55_0/libs/python/doc/

下一代了 pypy .

ipython notebook

其实就相是CDF的一种形式,可计算文档的结构。特别适合写paper来用。并且也实现了文学编程的模式。

并且可以直接保存在github上然后直接用http://nbviewer.ipython.org/ 直接在线的显示,是非常的方便,自己只需要用就行了。然后干自己的主业就行了。并且其支持与sphinx的之间格式的转化。

但是与CDF还有一定的区别,reader本身也要执行计算功能。

https://github.com/csurfer/pyheatmagic 可以用pyheat来进行优化。这样可以用热力图来显示代码的执行频度。

python as shell

http://pyist.diandian.com/?tag=ipython 现在看来,自己想要常用功能都有,只要把find,与grep简单的整一下,再结合%sx,与%sc,就无敌了,并且也不需要每一次都写到文件里,可以放在python 的变量里,因为python的变量要bash的变量功能要强大的多。 支持用iptyhon,尽可能,只要离开就要提出一个bug.这样就可以大大的提速了。直接继承一个magic class就可以简单,然后直接loadext就可以了,实现起来简单。自己也慢慢往里边添加自己的东东。可以参考在python里直接执行c的插件。看来这个扩展还是很容易的,把知识代码化,而不再只是文本描述。

并且ipython提供了类似于tcl中多解释器的方式,来实现多进程与kernel的并行,可以让并行计算随手可得,并且解决了GIL的问题,并且能够与MPI直接集成。%px 这个插件,看来是要升级自己的shell从bash到ipython了。

`if expand("%") == r"|browse confirm w|else|confirm w|endif"`

在ipython 中使用vim mode其实也很简单,直接配置readline这个库就行,正是因为linux的这种共享性,只要改了readline的配置文件,那么所有用到它的地方都会改变,一般情况下,默认的文件放在/usr/lib里或者/etc/下面。这里是全局的。 http://stackoverflow.com/questions/10394302/how-do-i-use-vi-keys-in-ipython-under-nix http://www.linuxfromscratch.org/lfs/view/6.2/chapter07/inputrc.html

减少与() 的使用就是 可以用 %autocall 来控制这个命令的解析的方式,或者直接 / 开头就可以了,在这一点上, haskell 吸收了这个每一点。把函数调用与 管道 统一了起来。在用python中是用点当管道使用了,bash 中通用的结构是 file而在 baskell中通用的是 list,其实就是矩阵相乘,只要首尾可以互认就可以了。 在haskell 中我们采用 $ 来指定这些事情。

配色同样也是支持的可以查看 %color_info 以及 %colors.

同时为了把python变成shell, 还有一个专门的库,plumbum 来做了这件事。 https://plumbum.readthedocs.io/en/latest/,但是还没有shell本身简练。

See also

  1. flask %IF{” ‘Flask is a microframework for Python based on Werkzeug,Jinja 2 and good intentions.’ = ‘’ ” then=”” else=”- “}%Flask is a microframework for Python based on Werkzeug,Jinja 2 and good intentions.
  2. A Byte of Python %IF{” ‘an introduction tutorial’ = ‘’ ” then=”” else=”- “}%an introduction tutorial
    1. data structure list, metalist, dict,class,module
  3. python PEP %IF{” ‘what is PEP’ = ‘’ ” then=”” else=”- “}%what is PEP
  4. 在应用中嵌入Python %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  5. Python on java %IF{” ‘’ = ‘’ ” then=”” else=”- “}%*Commute between Python and java* JythonUtils.java there use hash table to mapping the basic data element between java and python.
  6. org.python.core %IF{” ‘the online manual’ = ‘’ ” then=”” else=”- “}%the online manual
  7. jython offical web %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  8. install sciPy on linux %IF{” ‘科学计算’ = ‘’ ” then=”” else=”- “}%科学计算
  9. python and openCV %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  10. ipython %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  11. python for .net CLR Just like Java for JPython, anything in .net you can use via clr.
  12. Python之函数的嵌套 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  13. 简明 Python 教程 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  14. Python 中的元类编程,这才是python 所特有的东西。 元类是什么,就是生成类的类。
  15. 五分钟理解元类 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  16. Python 描述符简介 %IF{” ‘还是不太懂’ = ‘’ ” then=”” else=”- “}%还是不太懂
  17. Python 自省指南 如何监视您的 Python 对象 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  18. 可爱的 Python: Decorator 简化元编程 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  19. Python的可变长参数 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  20. cuda support python %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  21. cuda python %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  22. 欢迎使用“编程之道”主文档! %IF{” ‘基于python更接近于自然语言’ = ‘’ ” then=”” else=”- “}%基于python更接近于自然语言
  23. how-to-install-pil-on-64-bit-ubuntu-1204 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  24. marshal 对象的序列化 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  25. python PIL %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  26. sorted %IF{” ‘key 与cmp到底有什么区别’ = ‘’ ” then=”” else=”- “}%key 与cmp到底有什么区别
  27. python-convert-list-to-tuple %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  28. pygame %IF{” ‘在研究游戏的时候来看一下’ = ‘’ ” then=”” else=”- “}%在研究游戏的时候来看一下
  29. python 图像应用实例 %IF{” ‘里面有很多代码,有空的时候要看一下’ = ‘’ ” then=”” else=”- “}%里面有很多代码,有空的时候要看一下
  30. python 多继承 %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  31. ` windows7下使用py2exe把python打包程序为exe文件 <http://blog.csdn.net/xtx1990/article/details/7185289>`_ %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  32. ` 函数迭代工具 <http://www.cnblogs.com/huxi/archive/2011/07/01/2095931.html>`_ %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  33. python 字节码文件(.pyc)的作用与生成 %IF{” ‘python 可以把pyc 当做二进制发布,当然可以也可以自己加密使用’ = ‘’ ” then=”” else=”- “}%python 可以把pyc 当做二进制发布,当然可以也可以自己加密使用
  34. python-with-statement %IF{” ‘这个要求你的类,自己有enter,exit函数,with 会自动调用这些。’ = ‘’ ” then=”” else=”- “}%这个要求你的类,自己有enter,exit函数,with 会自动调用这些。

thinking

Jython embedded and extension with java just like right diagram, you there are three way call the jython, there an other way is extend the jython with the java. there are some interface to follow. and there is mapping between your jython data type and java data type. they provided some converting function. java can use the jython installed on the PC. androidRobot reference the example monkeyrunner.JythonUtils.java robot run on its base.

@MonkeyRunnerExported is used to generate _doc_ for python method, _doc_ is built-in string for documentation. JLineConsole(); Just support single line command? PythonInterpreter source code

at ScriptRunner.java, via run.  bind the robot->RobotDevice.
 public static int run(String executablePath, String scriptfilename, Collection<String> args, Map<String, Predicate<PythonInterpreter>> plugins,Object object)
/*     */   {
/*  79 */     File f = new File(scriptfilename);
/*     */
/*  82 */     Collection classpath = Lists.newArrayList(new String[] { f.getParent() });
/*  83 */     classpath.addAll(plugins.keySet());
/*     */
/*  85 */     String[] argv = new String[args.size() + 1];
/*  86 */     argv[0] = f.getAbsolutePath();
/*  87 */     int x = 1;
/*  88 */     for (String arg : args) {
/*  89 */       argv[(x++)] = arg;
/*     */     }
/*     */
/*  92 */     initPython(executablePath, classpath, argv);
/*     */
/*  94 */     PythonInterpreter python = new PythonInterpreter();
/*     */
/*  97 */     for (Map.Entry entry : plugins.entrySet()) {
/*     */       boolean success;
/*     */       try {
                                     success = ((Predicate)entry.getValue()).apply(python);
/*     */       } catch (Exception e) {
/* 102 */         LOG.log(Level.SEVERE, "Plugin Main through an exception.", e);
/* 103 */       }

                             continue;

                             /*if (!success) {
                                     LOG.severe("Plugin Main returned error for: " + (String)entry.getKey());
                             }*/
/*     */     }
/*     */
/* 111 */     python.set("__name__", "__main__");
/*     */
/* 113 */     python.set("__file__", scriptfilename);
                       python.set("robot", object);
/*     */     try
/*     */     {
/* 116 */       python.execfile(scriptfilename);
/*     */     } catch (PyException e) {

=Extendting= see 9.4 P223. Jython for Java Programmers.

== Main.GangweiLi - 29 Oct 2012

pprint pretty print is better than print has more control and smart

== Main.GangweiLi - 02 Jul 2013

怎样在python 中添加路径?

== Main.GegeZhang - 19 Jul 2013

python 中怎样实现程序复用,我想很多文件人家都已经写好了,??

== Main.GegeZhang - 16 Aug 2013

安装python子包

目录 到某个目录下: 首先是 D: 然后是 cd /d D:Program Files (x86)imageAirport

然后是 python setup.py install

== Main.GegeZhang - 11 Jan 2014

python 逐层构成: list->array->matrix

== Main.GegeZhang - 14 Jan 2014

对于集合运算支持 python 有一个专门的 set 与frozenset类型来进行集合运算,本质是通过哈希作为基础来实现的。例如交并补差还对称差集等等,都是可以计算的。既然有了这样的数据结构来支持这样的运算,对于blender,以及GIMP中图形的交并补差也就容易了很多了。首先是顶点交并补差,然后是线最后是面。

多进程与管道

现在于进程有了更深入的认识,虽然在c#自己也已经这么用了,但是python还是没有认真的用明白,原来subprocess就是 process, Popen接口给出详细的定义,并且在windows下的实现就是调用了createProcess这个api,并且shell后台就是调用cmd.exe来实现的。

其输入参数,一个就是 其参数,其buffersize指的就in,out,err的缓冲区的大小,是不是通过shell来调用,以及相关environment,以及前导与后导hook,以及working path等等都是可以指定的,并且其输入与输出都是可以指定的。默认是没有。并且是可以通过communciate一次性的得到,输入与输出的。 当然复杂的就可以用pexpect来做,管理就直接使用管做来操作了, 如果用python来写后台程序 可以参考 ndk-gdb.py 中的background Running. 其实写起来很容易,就是in,out,err的重定向问题。可以线程Thread或者subprocess.communicate等待退出并读取输出。

而线程的实现就不需要些东东。 并且知道了如何使用 subprocess 来实现管道,或者直接使用 pipes 来实现。更加的方便。

并且python也封装了spawn 这个API,其本质就是execv,execvpl,等等API的实现。 并且还可以调用os.write,os.read,os.pipes来直接实现。对于os.read. os.exec 可以直接执行任何程序,以及对于 os.fdopen,以及os.dup2这些算是有更深的认识。文件描述符用途就是通过中间机制,来对行硬盘文件的一种map机制。 并且os.path.split 实现了一种head,tailer的机制。

对了head,tailer这样的机制,也可list 的slice机制来实现。
head,tailer = list[0],list[1:]
相当于这还有更的实现方法
i = iter(l), first=next(i),rest=list(i) 以后会有 first *rest = list

看来python 会支持一些更现代的语法。

这样的写法有没有更简单的写法呢。

在bash里开一个进程很简单, 直接spawn,或者fork,或者 (),就可直接启一个新的进程了,同时bash 来说直接把一段代码 {} 然后重定向就相当于重启了进程。 现在把线程与进程搞明白了。 就可以灵活的应用了。 http://ubuntuforums.org/showthread.php?t=943664 https://jeremykao.wordpress.com/2014/09/29/use-sudo-with-python-shell-scripts/

http://ubuntuforums.org/showthread.php?t=1893870 python communitcate应该是工用的,因为gdb也用的这个 同样的sudo 也是可以这样的。 这样的方法才是最通用与简单的,并且就是直接利用进程本身的概念。看来自己还需要把这个要信给补一下了。

  1. os android my be use this module. and subprocess which just like system call of Perl or expect? which one?

GIL 这里有两篇文章写的不错, http://fosschef.com/2011/02/python-3-2-and-a-better-gil/ http://zhuoqiang.me/python-thread-gil-and-ctypes.html

欲练神功,挥刀自宫, 就算自宫,也未必成功, 不用自宫,也一样能成功!

这三句用到这里简值太精典了,由于GIL限制多线程,要解决这个问题就必须自宫了,但是20多年的发展有太多的库依赖此,也就是就算自宫,也未必成功,但不是没有办法了,直接利用c扩展来做,也直接解决了这个问题,把多线程的东东都放在C语言里,并用ctype来引用就行了。也就是解决问题的思路了。

python的缺点,那就是对多线程以及效率本身不高,但是结构清晰简单。Go语言天生对并行应该支持的非常好。但是一些新的编程范式支持还不错,并且是除了perl之外库非常的语言了。

python 与 asm <http://wdicc.com/asm-and-python/> 非常生动解读了,各个层级的执行效果,为了通用性,人们写出各种各样的执行框架,其实所谓的ABI就是汇编二进制指令之间的复用机制。所谓的dll,以及elf,各个段的机制,其实与代码层面的机制是一样的。并且在elf为了尽可能节省空间,把程序所有重复的字符串symbol都直接全用符号表来压缩,当然如果小于指令地址长度的符号就没有意思了,一个 地址要32位是4字节,64就是8字节来表示的。所以就看你理解的深入程度,深入才能浅出。

pygments 支持各个应用平台,wiki,web,html以及latex,console等等。这样非常方便配色,尤其是代码与log的分析的时候就非常的方便了。为了各种利用生成语法文件是非常的方便。

与自己写一个语法文件一样,其实也是一个词法与语法分析,然后给出配色,并且我们还可以利用语法树直接做一些别的操作,因为它已经支持大部分的语言了。可以省去自己很大一部分时间。可以只加入一个hook就可以了。

python 非常适合做一个interface语言,在于它的简单与精练。然后是各种场景的应用,现在感觉python是可以与perl有一拼了。各种各样库非常全。以后的编程可能都会多层编程同时存在的问题。用来解决灵活性与效率的问题。

LINQ in Python

http://www.cnblogs.com/cython/articles/2169009.html http://blog.csdn.net/largetalk/article/details/6905378 http://blog.csdn.net/largetalk/article/details/6905378 http://wklken.me/posts/2013/08/20/python-extra-itertools.html http://stackoverflow.com/questions/5695208/group-list-by-values 并且数学上排列组合都实现了。 原来都是为实现http://blog.jobbole.com/66097/ 无穷序列,随机过程,递归关系,组合结构。 都是源于yield. http://radimrehurek.com/2014/03/data-streaming-in-python-generators-iterators-iterables/

这种懒惰性求值,都是利用不yield这种方式产生,并且具有不回退性,那就不能求长度等操作了。每两次的调用不是同一个值了。 这个直接利用语言的高阶特性会非常的简单,例如列表推导,以及filter,map,reduce再加上lambda 的使用,以及sorted 再加上itetools中groupby,

另一大块那就是vector化的索引计算。其实就相当于数据组的sql语言了。

文件操作

文件是可以直接当做列表操作。

fd = open("xxx.txt")
for line in fd:
    print fd:

subprocess.check_output(["ifconfig"," |grep ip|sort"],shell=True)

with

是不是就相当于 racket 中let 的功能。

lazy evluation

gen = (x/2 for x in range(200))

这是相当于yield,了,有点相当于管道了。

列式推导 直接加map,filter 会更有效.http://www.ibm.com/developerworks/cn/linux/sdk/python/charm-17/index.html 这样会更有效

currying, Partial Argument, 可以用lambda 来实现,或者使用 from functools import partial;add_five=partial(add_numbers,5)

其本质就是又封装了一层函数。也就是alias 的一种实现而己。在函数调用之前添加了一次的简单替换,或者再一次wrap函数就行了。 什么时候用呢,修改与扩展原来的库函数的特别方便。 另外在参数固定的情况下,这可以用偏函数来减少参数的传递。 高阶函数,利用基本函数形成复杂的功能。http://blog.windrunner.info/python/functools.html。 因为在python里的一切皆为对象,而在C语言里,函数就是一个函数指针,只要把指针地址改了就行了,在python里,只要直接复值 就行了,a.fooc = new_fooc 就搞定了,只要在其后面加载module都会使用new_fooc

参数的传递

可以是固定的,位置的,也可以是字典式的,还可以是列表,并且是不定长的,*list,**kwd, 这种做法可以到处用。

例如

self.env.set_warfunc(lambda * args:warnings.append(args))

__getattr__ 用法

这个特别适合用于封装一些现有API使其具有 python的形式,一个简单做法,就像GTL用template生成一堆的IDL接口函数文件。 另一个办法那就是利用 python 的这些内建接口,来实现简单高效。 例子可以参考 fogbugz.py的用法。 核心是那参数为什么可以那样定义。

StringIO 的实现原理

直接使用一个buffer列表来实现,所谓的buffer最简单的理解那就是一个连续数组空间,并且每一次有一个大小等信息的记录。 然后每一次进行查询也就行了。实现一下那些接口,read,write,tell,seek等等。

string format

https://docs.python.org/2/library/string.html,支持对齐与任意字符的填充。这个可以在vim 里用嘛。

getpass

可以用于密码的login处理等等。

shlex

如果想实现一个简单的语法分析,用shlex就足够了。

读取大文件的某几行

seek到位,然后反向搜索。 换行符来实现。 http://chenqx.github.io/2014/10/29/Python-fastest-way-to-read-a-large-file/

单行命令

python 的module有两种模式,一种是当做module来调用,另一种是当做脚本来使用。 这个主要是通过

if __name__ == "__main__":
     #do someting

由于python有众多的库,可以直接搭建各种server. 例如: HTTP python -m SimpleHTTPServer python -m SimpleXMLRPCServer

from simpleXMLRPCServerr import SimpleXMLRPCServerr
s = SimpleXMLRPCServer(("",4242))
def twice(x):
return x*x

s.register_function(twice) #向服务器添加功能
s.serve_forever() #启动服务器
然后在启动一个命令行,进入pyhon。 输入:
from xmlrpclib import ServerProxy s = ServerProxy('http://localhost:4242') s.twice(2) #通过ServerProxy调用远程的方法,

同时python支持从标准输入直接读入代码: 例如

echo "print 'helloworld'" |python -

这样就可以动态的生成各种代码组合来执行了。

最具C的格式,scheme一切皆对象的形式。再加异步编程的模型,可以随意的用event来进行function call. 每一个函数的执行都可以callback. 应该是每一个对象都有一个callback. 并且还有prototype机制,暂且当做反射来用吧。

  1. 最好用的IDE online/offfline
  2. framework

快速入门:https://www.nodebeginner.org/index-zh-cn.html,https://github.com/mbeaudru/modern-js-cheatsheet

由于nodejs 本身采用异步并行机制,里面就会出大量的event以及相关的trigger 可以用hack. 进行定制化。并且nodejs是应用框架与语言相互融合。 https://github.com/muicss/sentineljs 利用 @keyframes的动态rule来检测DOM中新加的结点。

关键是要理解浏览器的render机制。现在的机制你只管取数据,然后扔给浏览器的engine来进行rendering.

如何hacking这个rendering过程呢。

数组

https://github.com/waterlink/Challenge-Build-Your-Own-Array-In-Js,一个JS Array的测试集。

当前最流行的前沿技术,都有哪些各有什么特点
  1. single page application

Java script buffer,stream的概念不错,特别接近真实的数据结构。

http://v3.bootcss.com/ 用来生成UI的布局。 facebook 前端UI开发框架:React Angular.js 介绍及实践教程 主要GUI的控件与数据的绑定。

HTML5 下一代的HTML的规范,会有不少新特性。

如果对性能有更高的要求的可以用 https://infernojs.org/benchmarks

用nodejs来开发,就充分利用了html的灵活性来设计界面,同时nodejs又可以像传统编程来实现逻辑部分。

来实现各种 html的template 并且就像类一样,简版的react: https://github.com/mikeal/rza

PWA(Progressive Web Apps)

用来解决适配不同的终端以及不同网速的设计结构。 把原生应用与网络融合起来,即有原生应用的快速与灵活,又有联网的好处,就像各种新闻客户端一样。 并且已经有一些好用的框架。 例如 preactjs.com 在DOM封装了一层,同时体积非常小与快速。 https://preactjs.com/ 3kb atlternative to React.

快速原型建模打桩

API Mocker 接口管理系统 https://github.com/DXY-F2E/api-mocker, Graphql 接口管理采用基于字典对象的型式,可以方便的进行扩展与演化。 http://graphql.cn/ https://github.com/atulmy/fullstack-graphql

module 开发结构

清理临时文件的工具: https://github.com/tj/node-prune

Vue.js

浏览器的工作原理 对比原来的dom,找到最小编辑距离,然后修改,并进行重绘。 #. cdn url

  1. v-bind:PROPERTY -> :Property v-on:Click -> @EVENT

    v-if v-for

<script src="https://unpkg.com/vue"<script>
<div id="app">
    {{ name }}
</div>

New Vue ({
   el: "#app",
   data: {
     name : "max"
   }
})

Vue.Component
  1. npm install -g vue-cli

webpack 相当于打包编译工具。 并且可优化代码。

React

利用用类对来对应网页,并且函数就是render()函数。利用面向对象的方式来构造网页。

npm -g create-react-app

react 的常用的一些设计模式 https://github.com/kentcdodds/advanced-react-patterns https://github.com/Heydon/inclusive-design-checklist

MEAN stack

Mongo,Express,Augular, Nodejs.

ReactX rxjs 异步处理库,类似于ajax.

storybook 不错 UI galaxy demo. https://storybook.js.org/examples/ https://github.com/storybooks/storybook 开源好用图标库 https://feathericons.com/

快速fix 避免半夜加班改,还是失败 所以deploy的代码最好也要用版本控制,关键是数据也可以回退:https://github.com/maxchehab/quickfix

数据的回退: https://github.com/Meituan-Dianping/MyFlash

Angular 2

npm install -g @angular/cli
ng new <new project>
  1. Single Page
  2. Update DOM
  3. Handles Routing(of visual Parts)
  4. Very reactive user experiences

ng-app,ng-xxx to binding things. .. image:: /content/Stage_1/JavascriptAndNodeJS.Angular2_cs.png

Javascript 的promise机制

生成二次回调机制,只有上一个调用成功,然后利用生成调用代码,然后再传给回调。 主要也就是MessageQeqeue再加上一个执行代码。特别适合建立异步的模拟机。是不是也特别适合区块链的 合约系统的开发。

对于半静态的event call,promise是一个不错的机制。

同时比回调函数更进了一步。就像有点像gl之类的操作。

var promise = getAsyncPromise("fileA.txt");
promise.then(function(result){}).catch(function(error){});

这些并不是执行顺序,这一点与一般的编程语言不同的点,代码的输写顺序 与执行顺序是不一致的。

css

随着HTML的发展,css也从原来静态的模式匹配,发展到变量等动态有sass,再到支持对象模板的less等。 同时网页动画,从最简单的css动画,到gl动画。有各种各样的库http://www.css88.com/archives/7389 轻量的渐变库,https://github.com/LiikeJS/Liike

直接利用scss来生成各种效果图,例如各种有机的效果图。https://github.com/picturepan2/devices.css

一种自定义的动态转场动化,可以基于地图位置的转场: https://github.com/codrops/AnimatedFrameSlideshow

https://github.com/Flaque/merchant.js 可以做无聊动画的框架类似于doodle.

正是由于 nodejs这种异步的机制,只要给出一个总量,以后异步计算增量就可以真实反映进步了。 https://github.com/sindresorhus/p-progress

如何快速描述一个掌握的技能,准备一个面试宝典,过一遍,就能完全理解。 例如 https://github.com/Pau1fitz/react-interview React 就是在DOM上面又封装了一层,VirtualDOM,并且这个DOM,对象化的,并且其rendering过程都是显式可控的。

node debug

直接采用的remote debug模式,node + chrome:inspect的模式。

在线调试器有 jsfiddle,codePen,以及各种动画的galxy等等可以用,https://www.zhihu.com/question/31731104

https://github.com/fhinkel/type-profile, 充分利用V8的特性,这样可以有效的提高troubleshoot的效率。

快速原型的方法

https://github.com/renatorib/react-powerplug ,采用Render Props的设计模式。

类型检查

https://github.com/sindresorhus/is, 可以进行类型检查,基于类型检测好处,就是量纲法可以有效的减少错误。 benchmark =========

对于各种framework,到底采用哪一个,最实用的标准之一,那就是性能对比。 https://github.com/krausest/js-framework-benchmark,performance的对比。

显示系统

PPT 可以采用 nodeppt来做, https://github.com/DracoBlue/markdown-papers

写出可以nodejs + asciidoc 可以参考 https://github.com/liubin/promises-book/

一些非常有用的转换工具

把代码转化成图片,主要是用于ppt 的显示。

https://github.com/mplewis/src2png

Test

javascript的自动化测试框架: https://github.com/jest-community/jest-runner-eslint

同时还有商业化的控件库http://www.grapecity.com.cn/developer/wijmojs#price

浏览器引擎

https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/webkitflow.png

webkit

https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/image008.jpg

Mozilla 的 Gecko 呈现引擎主流程

浏览器的工作原理

渲染引擎会遍历渲染树,由用户界面后端层将每个节点绘制出来

按照合理的顺序合并图层然后显示到屏幕上。

浏览器刷新的频率大概是60次/秒, 也就是说刷新一次大概时间为16ms

如果浏览器对每一帧的渲染工作超过了这个时间, 页面的渲染就会出现卡顿的现象。

以上过程是渐进的,并不一定严格按照顺序执行的,为了更快将内容呈现在不屏幕中, 不会等到HTML全部解析完成之后才开始构建渲染树和layout,它会在不断接收和处理其他网络资源的同时,就开始部分内容的解析和渲染

渲染完成之后会触发 ready事件

什么情况下会引起 reflow repaint 当render tree (元素尺寸) 发生变化时则会重新layout 则会因此reflow.

浏览器首先下载html、css、js。 接着解析生成dom tree、rule tree和rendering tree。 再通过layout后渲染页面.

浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程

https://pic4.zhimg.com/80/e8704374ae3d80ab1de47f2cb6899a1a_hd.jpg

webkit

如何动画

动画的性能优化 https://www.w3cplus.com/animation/animation-performance.html

<div style="width:75%">
         <canvas id="canvas"></canvas>
</div>
<script>
         var color = Chart.helpers.color;
         var scatterChartData = {
             datasets: [{
                 label: 'My First dataset',
                 xAxisID: 'x-axis-1',
                 yAxisID: 'y-axis-1',
                 borderColor: window.chartColors.red,
                 backgroundColor: color(window.chartColors.red).alpha(0.2).rgbString(),
                 data: [{
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }]
             }, {
                 label: 'My Second dataset',
                 xAxisID: 'x-axis-1',
                 yAxisID: 'y-axis-2',
                 borderColor: window.chartColors.blue,
                 backgroundColor: color(window.chartColors.blue).alpha(0.2).rgbString(),
                 data: [{
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }, {
                     x: randomScalingFactor(),
                     y: randomScalingFactor(),
                 }]
             }]
         };

         window.onload = function() {
             var ctx = document.getElementById('canvas').getContext('2d');
             window.myScatter = Chart.Scatter(ctx, {
                 data: scatterChartData,
                 options: {
                     responsive: true,
                     hoverMode: 'nearest',
                     intersect: true,
                     title: {
                         display: true,
                         text: 'Chart.js Scatter Chart - Multi Axis'
                     },
                     scales: {
                         xAxes: [{
                             position: 'bottom',
                             gridLines: {
                                 zeroLineColor: 'rgba(0,0,0,1)'
                             }
                         }],
                         yAxes: [{
                             type: 'linear',
                             // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
                             display: true,
                             position: 'left',
                             id: 'y-axis-1',
                         }, {
                             type: 'linear',
                             // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
                             display: true,
                             position: 'right',
                             reverse: true,
                             id: 'y-axis-2',

                             // grid line settings
                             gridLines: {
                                 drawOnChartArea: false,
                                 // only want the grid lines for one axis to show up
                             },
                         }],
                     }
                 }
             });
         }
         ;

         document.getElementById('randomizeData').addEventListener('click', function() {
             scatterChartData.datasets.forEach(function(dataset) {
                 dataset.data = dataset.data.map(function() {
                     return {
                         x: randomScalingFactor(),
                         y: randomScalingFactor()
                     };
                 });
             });
             window.myScatter.update();
         });
 </script>

每一个语言都是不断的向前发展的,C++ 已经有了11,14,17等版本。 慢慢就会把那些需要库的一些东东都吸收进来。 有点类假于C#的模式,会把设计模式的东东也都给融合进语言中来。 并且各个编译器的支持,可以参考 http://en.cppreference.com/w/cpp/compiler_support

语言娈量与指针以及各种作用域的定义,都是对内存管理的定义。而内存管理基本上上都是对引用技术的管理模式。 各种管理模型都是引用计数对种模型的适配。 看来计数是管理的一个开始。

类型转换

这个是各个语言,最灵活也是最难的部分。从最简单的说起。

  1. 长短整型转换。
  2. 整型与浮点型的转换。
  3. 字符串与数字之间转换
  4. 指针类型的转换。
  5. 类的的类型转换

最后两者也是最复杂的。也是各种反射机制的基础。 http://www.cnblogs.com/chio/archive/2007/07/18/822389.html

类的转换并且还有一定的规则。可以用强制转换,来实现一些hook的功能,例如hook某一个类的调用。这可以这么用。 动态链接库,就有了中间的搜索查找的过程,就有了Injector的空间。

C++ 现在支持一定的类型推导了,decltype 来得到目标的类型。

前置定义用途

与header的用途是一样的,都是为了编译器在编译的时候可以不用搜索全局,就知道所需要函数的原型是什么。头文件只是为欺骗编译器局部并行编译的方法。编译是可以并行,而目前链接可能是不行的。

一方面是用强耦合问题。 主要是用来解决编译的问题,由于走到当前,要求所有接口信息都要已知。但是现在需要东东,现在现在还没有编译怎么办。 这个怎么办呢,提前放一个stub。 这样就骗过编译。 但是骗不过linker. 因为linker是全局搜索的。如果连linker都骗过去,也就得再准备一分空函数库了。 然后在实际的运行的时候,再加上真实的库。 交叉编译就可以。 头文件就是这样一个作用。

从programming element来看的类,copy就是信息的传递修改的问题。

对于于不需要改变的变量,只需要传递引用就行了。没有必要生成一片内存,再copy就一样。 其实与文件的copy是一样的。 对于复杂的变量例如struct,以及class,这种大家伙来说,采用一刀切的方式显然效率不同。变样把结构分为变化问题,不变部分。 或者增量变量的部分。 面像对象的继承机制,可以看做这样的一种体现。例如基类基本上都是不变的部分。或者接口纯虚函数 无非函数指针。对于新增部分采用继承的方式。当然没有问题,对于override的也没有问题,有重载就行了。

而难点在于同一状态变量,要同时保存几个版本。例如Unreal中,Thread/Render中对象是共用的,但是为了加速,采用的异步机制。 至少double object的机制。计算当前的版本snapshoot一下,留给render,自己继续前进。 对于复杂结构更是如此。如何解决object的copy问题是个难点,如何又省内存,又高效呢。当然再简单的方法全部copy,也就是所谓的浅copy与深 copy的区别。

所以变量结构设计的时候,变量是可读,可写,以及读写方式与范围建模好好定义。这样方便后面的内存的管理,以及实现copy的问题,例如static相当于只读, 而moveable,相当于是可写。

对于一个简单的变量是没有这样大的区别的。像Unreal中这些object不一样的。后面那些pipline的优化也主要是基于static,moveable的能够区分开来为基础的。

对于 STL中实现的,写时copy也是基于这样认识。其实也很判断。凡是从内存读到寄存品上时,不需要变化,而要把内容写回内存的时候,就要换一个地址来写了。 这于部分的优化,那如何move,load指令的操作优化。可以统计move,load指令的做统计分析。move到内存寄存器的多的,说明只读的多,反过来,那就是只写的多。

同样是一段01序列,经过那么多层的传递,真的就需要那么多重复操作吗。

面象对象

friend class
是为方便松耦合,方便合作类之间的相调用。例如正常情况下,comp 函数是不能访问私有变量的,但是frind之内就可能了。也解决全局函数与类之间的调用。
Class Test{
   private:
     int id;
     string name;
   public:
     void print(){
        cout << id << ":" << name << endl;
     }
   friend bool comp(const Test &a,const Test &b);
}
bool comp(const Test &a,const Test &b){
   return a.name < b.name;
}

int main() {
    vector<Test>  tests;
    sort(Tests.begin(),tests.begin()+2,comp);
}

C++ 11 新特性

  1. typeid()

  2. 支持了lambda 表达式

  3. 类型推导关键字 auto,decltype

  4. 模板的大量改进 - 模板别名 - 外部模板实例

  5. nullptr 解决原来C++中NULL的二义性。

  6. 序列for 循环,有点类似于foreach.

    for(auto number: numbers){
          cout << number << endl;
    }
    
  7. 变长参数的模板,tuple.

  8. 可以用{}来进行各种各样的初始化

  9. default/delete 函数声明。https://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/index.html

  10. lambda

    auto pFunc=[]()->double {};
    [](){}(); //call the lam
    
    int main() {
      int one =1;
      int two =2;
      int three =3;
      [one,two](){cout <<one<<","<<two<<endl;}{};
      [=](){cout <<one<<","<<two<<endl;}{};
      [=,&three](){cout <<one<<","<<two<<endl;}{};
      [&](){cout <<one<<","<<two<<endl;}{};
      [&,one](){cout <<one<<","<<two<<endl;}{};
    }
    
  11. functional class

    class Check {
      public:
         bool operation()(string &test){
    
         }
    } check1;
    
  12. lambda mutable

  13. Elision -fcopy-elision http://en.cppreference.com/w/cpp/language/copy_elision

  14. 构造函数可以相互调用。

  15. rvalue and Rvalue &&Lvalaue

C++14 特性

  1. constexpr 表达式,可以把计算提前在编译阶段。
  2. 但是这样就会加长编译的时间
  3. 越来越有分段计算的能力, 计算现在能算的,不能算的放在以后算。

profiling

  1. 最简单的高精度计时
#include <chrono>

chrono::steady_clock::time_point t1= chrono::steady_clock::now();
// do something
chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
chrono::duration<dobule> time_used = chrono::duration_cast<chrono::duration<double>>(t2 -t1);
cout << "used:" << time_used.count() << "sec" << endl;

new/delete 与malloc/free

new /delete 在后台也是调用的malloc,free,但是多一些封装与检查。 https://github.com/lattera/glibc/blob/a2f34833b1042d5d8eeb263b4cf4caaea138c4ad/malloc/malloc.c glibc的实现。 主要是内存管理方式的不同。 http://blog.csdn.net/hzhzh007/article/details/6424638 #. 分配的速度。 #. 回收的速度。 #. 有线程的环境的行为。 #. 内存将要被用光时的行为。 #. 局部缓存。 #. 簿记(Bookkeeping)内存开销。 #. 虚拟内存环境中的行为。 #. 小的或者大的对象。 #. 实时保证。

著名的内存管理方式

  Doug Lea Malloc:Doug Lea Malloc 实际上是完整的一组分配程序,其中包括 Doug Lea 的原始分配程序,GNU libc 分配程序和 ptmalloc。 Doug Lea 的分配程序有着与我们的版本非常类似的基本结构,但是它加入了索引,这使得搜索速度更快,并且可以将多个没有被使用的块组合为一个大的块。它还支持缓存, 以便更快地再次使用最近释放的内存。 ptmalloc 是 Doug Lea Malloc 的一个扩展版本,支持多线程。在本文后面的 参考资料 部分中,有一篇描述 Doug Lea 的 Malloc 实现的文章。   BSD Malloc:BSD Malloc 是随 4.2 BSD 发行的实现,包含在 FreeBSD 之中,这个分配程序可以从预先确实大小的对象构成的池中分配对象。它有一些用于对象大小的 size 类,这些对象的大小为 2 的若干次幂减去某一常数。所以,如果您请求给定大小的一个对象,它就简单地分配一个与之匹配的 size 类。这样就提供了一个快速的实现,但是可能会浪费内存。在 参考资料部分中,有一篇描述该实现的文章。   Hoard:编写 Hoard 的目标是使内存分配在多线程环境中进行得非常快。因此,它的构造以锁的使用为中心,从而使所有进程不必等待分配内存。它可以显著地加快那些进行很多分配和回收的多线程进程的速度。在 参考资料部分中,有一篇描述该实现的文章。

函数调用实现

对于结构化的传统语言,背后的堆栈的建立,参数排列,返回地址,堆栈消除等机制。

base class subobject 在derived class的原样性。也就是保证其内存结构一致性。包括填充位也要保留。

http://glgjing.github.io/blog/2015/01/03/c-plus-plus-xu-han-shu-qian-xi/ 当子类继承父类的虚函数时,子类会有自己的vtbl,如果子类只覆盖父类的一两个虚函数接口,子类vtbl的其余部分内容会与父类重复。这在如果存在大量的子类继承,且重写父类的虚函数接口只占总数的一小部分的情况下,会造成大量地址空间浪费。在一些GUI库上这种大量子类继承自同一父类且只覆盖其中一两个虚函数的情况是经常有的,这样就导致UI库的占用内存明显变大。 由于虚函数指针vptr的存在,虚函数也会增加该类的每个对象的体积。在单继承或没有继承的情况下,类的每个对象会多一个vptr指针的体积,也就是4个字节;在多继承的情况下,类的每个对象会多N个(N=包含虚函数的父类个数)vptr的体积,也就是4N个字节。当一个类的对象体积较大时,这个代价不是很明显,但当一个类的对象很轻量的时候,如成员变量只有4个字节,那么再加上4(或4N)个字节的vptr,对象的体积相当于翻了1(或N)倍,这个代价是非常大的。

对于不同抽象程度,存取的效率也是有区别,其实也还是用多少条指令。 额外的间接性会降低”把所有的处理都移到缓存器中执行”的优化能力。

inline in inline有可能失败。

C语言经典在于传统硬件模型与逻辑模型的分界线上。包括LLVM都是拿C语言的形式做为标准语言。

而C++实现把数据与操作bind在一起的功能,但是底层还是与C一样,用同样的ABI。但是通过编译器实现实现一些相当于元语言的操作,再加上编译器内部的结构。同时自动类的内存结构,来方便继承与修改。 而在C里,所有结构都要自己手工基于硬件模型来构造。 而c++则是基于逻辑模型来构造,然后由编译器当你构造出对应内存struct来,再加一些额外的overhead.c++自动给利用链表给添加不少东东。而在 C中这些都是自己明确实现的。

另外c++的成员函数指针,都是基于对象的偏移量,所以指针要加上类的类型。

C++的原理自己想实现的DSL的原理是一样,只是更加复杂了。高级语言要解决的问题,即要能保持高级语言的灵活与逻辑概念。同时又不产生的垃圾overhead代码到下一层的语义中。并且尽可能智能的化简。 或者可视化的理解让人们半手工来进行优化。C++是目前之这方面最好的。一个重要原因,就是基于C演化过来的。而C语言是对硬件抽象的最好,并且也是优化的效率最高的语言。 然而但C语言的这一点,慢慢就可以被LLVM来取代,所以目标,把DSL语言翻译成LLVM原语,然后再LLVM来进行优化,以及进行到硬件级别的优化。

明白每级语言向下翻译的基本原理,利用编译器+半手工调优,来实现性能与灵活性平衡。

不能在元函数中使用变量,编译期显然只可能接受静态定义的常量。

内存结构

http://www.cnblogs.com/kekec/archive/2013/01/27/2822872.html, c++的结构主要也是通过链表来实现。 并且也是多级,如果你只是用到一个类的很少一部分功能,但是还是要继承这个类,这样是很浪费内存空间的。 类型的改变只是改变了如果读那一段内存结构。

c++的内存结构解析类似于TCP/IP协议包的解析结构,都是采用头尾添加方式,root class就相当于最上长层协议包。 继承就是不断添加包头与包尾的方式。

泛型编程

http://blog.csdn.net/lightlater/article/details/5796719

泛化编程,相当于在编译当做运行了,只过其输出是代码,还需要进一步编译。 其实简单就像现在自己经常写的log,格式规整一点,直接就是另一种语言。 相当于让编译器帮你写代码的过程。 也就是进一步的符号编程。 变量/对象 -> 类/类型-> 符号

其实是大数据分析时,采用泛化编程就可以实现自我演化的图灵机了。通过聚类得到一些属性,然后自动组成生成代码,进一步的执行。这样不断的演化就可以了。

泛化编程是虽然图灵完备的。 但是由于当初发明模板时根本没想过基于它来编程。在实践中,泛型编程一般用于库级别的开发, 框架级的应用比较我少,应用级尽量少用。这样可以软件的管理复杂度。

泛化编程不单是可以只类型,可以任意你要替换的对象。

主要用来实现代码的排列组合。

模板本身,具有自变量的推导,但是不同类型参数的返回值是无法推导的。只能明确的给出。 同时支持模板多态的。但是这些选择都是编译的时候完成的,另一个模板的嵌套,等等。 以及模板的偏化。 同时支持 Typname具有subtpye.

模板核心就是特化匹配,并且就像M4一样,不断迭代替换,直到停机为止。 特别像haskell的模式匹配。

STL 还只是小儿科,而BOOST则是高级篇。

最灵活的模板那就是class的继承功能,只需要改动你需要改动的。

最低层的编码,就是编码,例如那些状态位,每一个位是都是有意义的。

模板的编译

也是类似于C的宏吗,还是编译自身的支持。 #. 包含模板编译模式。(这个是主流)。 #. 分离模板编译模式。

flow

  1. C++ source code
  2. Template Compiler
  3. c++ Compiler
  4. MachineCode

模板元编程

另一个那就是模板元编程,特别是模板的递归,它利用模板特化的能力。可以参考haskell的模式匹配,利用多态加模式匹配写状态机,不要太爽,用模式匹配解决了goto的问题,并且更加灵活,同时又解决避免了函数调用,有去有回的问题。 http://blog.csdn.net/mfcing/article/details/8819856,其实TypeList 也是一种模板元编程。 当然编译的是会限制递归的深度的,通用-ftemplate-depth来控制。

元编程模型也采用的函数式编程范式。 这里有框图http://www.cnblogs.com/liangliangh/p/4219879.html

  1. metainfo
    • Member Traits
    • Traits templates
    • Traits Classes
    • Lists and Trees as nested templates
  2. Metafunction
    • Computing Numbers
    • Computing Types IF<>,SWITCH<>,WHILE<>,DO<>,FOR<>.
    • Computing Code EWHILE<>,EDO<>,EFOR<>
  3. Expression Template
作用
  1. 编译时数值计算
  2. 解开循环
  3. 类型处理
    • 类型分析选择
    • 类型的数据结构
    • Typelist
    • 提取Typelist中的类型

# 自动生代码

多态的重载

多态调用的过程就是一个模式匹配的过程。 函数指针也就是指定了匹配模式。

非类型模板参数

所谓的模板也就是变量替换,不过在这个替换的条件,做出了更加细分的规则。 可以简单理解为一个全局常量的角色,只不过是在编译时计算出来的。经过这几天搜索,又一步一步的走到代码的演化。

TypeList

采用的函数式的定义,具有添加听说生成一个类型列表计算。 可以添加与替换其默认值。 并且在编译期间提供了一般list的绝大部分基本功能。 可以结合元编程理解这些东东。

如果你真的想不到typelist的用途,那是因为确实没有用到的需求,你知道有这个东西的存在就好了。有一天你碰到某个问题抓耳挠腮的时候,忽然想到typelist,马上就会用到火星的生产力耶。

http://blog.csdn.net/win2ks/article/details/6737587

对于模板参数也像位置参数一样,具有自变量推导(argument deducation)机制。

type_traits

http://blog.csdn.net/hpghy123456/article/details/7370522, 用了管理模板参数,往往参数之间会相一定的依赖有关系。可以相互的推导依赖,而根据这些信息可以生成更高效,更有针对性的代码。

STL库

容器通过内存分配器分配空间,容器与算法分离。算法通过迭代器访问容器,仿函数协助算法完成不同的策略变化。适配器套接仿函数。

所以在初化时候,例如调整内存分配策略来实现代码的优化。

如何添加汇编代码

如何手工写一个汇编函数, 只需要写个函数直接调用gcc来生成片断,直接直接插入就行。 其实也不需要只要掌握转换规则,直接利用LLVM 来进行代码分析。来优化生成汇编。

Functors

struct MatchTest{
     bool operator()(string &text) {
         return == "lion";
     }
}


int main() {
    MatchTet Pred;
    string value = "lion";
    cout << pred(value) << endl;  // output 1
}

模板实例化

隐式实例化时,成员只有被引用到才进行实例化。

template argument deduction/substition failed

test@devtools-vm:/opt/libcvd$ make
g++ -O3 -I. -I.  -INONE/include -g  -Wall -Wextra -pipe -std=c++14 -ggdb -fPIC -mmmx -msse -msse -msse2 -msse3 -c cvd_src/convolution.cc -o cvd_src/convolution.o
cvd_src/convolution.cc: In function ‘void CVD::compute_van_vliet_scaled_d(double, double*)’:
cvd_src/convolution.cc:155:22: error: no matching function for call to ‘abs(double&)if (abs<double>(step) < 1e-6)
                      ^
In file included from /usr/include/c++/5/random:38:0,
                 from /usr/include/c++/5/bits/stl_algo.h:66,
                 from /usr/include/c++/5/algorithm:62,
                 from ./cvd/convolution.h:8,
                 from cvd_src/convolution.cc:1:
/usr/include/c++/5/cmath:99:5: note: candidate: template<class _Tp> constexpr typename __gnu_cxx::__enable_if<std::__is_integer<_Tp>::__value, double>::__type std::abs(_Tp)
     abs(_Tp __x)
     ^
/usr/include/c++/5/cmath:99:5: note:   template argument deduction/substitution failed:
/usr/include/c++/5/cmath: In substitution of ‘template<class _Tp> constexpr typename __gnu_cxx::__enable_if<std::__is_integer<_Tp>::__value, double>::__type std::abs(_Tp) [with _Tp = double]’:
cvd_src/convolution.cc:155:22:   required from here
/usr/include/c++/5/cmath:99:5: error: no type named ‘__type’ in ‘struct __gnu_cxx::__enable_if<false, double>’
Makefile:329: recipe for target 'cvd_src/convolution.o' failed
make: *** [cvd_src/convolution.o] Error 1
test@devtools-vm:/opt/libcvd$

解决办法,直接去cppreference.com中查找对应的库函数,并且找到example. 并且快速形成一个切面,进行troubleshoot. http://en.cppreference.com/w/cpp/language/template_argument_deduction

C/C++ 互调的方法

http://www.jianshu.com/p/8d3eb96e142a,主要是c++的函数名的特殊格式,利用extern C以及 #ifdef __cplusplus 来搞定。

IO模型

Stage_1/iostream.png

多线程

  1. pthread_create 创建线程

  2. pthread_setname_np 指定线程的名字

  3. pthread_join 用来等待另一个另一个线程结束。 join相当于加入排队中。一个线程可以等多个。

    pthread_create(tid1...)
    pthread_create(tid2...)
    pthread_create(tid3...)
    pthread_join(tid1)
    pthread_join(tid2)
    pthread_join(tid3)
    
  4. pthread_detach

多线程的模型,主要与进程的状态相关

Stage_2/images/threadState.gif

同步有机制有

  1. 互锁机制,主要用于共享内存的应用, 最经典例子就是火车的上洗手间。 http://pages.mtu.edu/~shene/NSF-3/e-Book/MUTEX/locks.html 其核心是使用计数与线程状态的操作。 主要是线程队列的policy规则,队列与进程之间最好的讲解那就是排队论。 但互斥锁应当仅由持有该锁的线程来解除锁定

    • pthread_mute_lock
    • pthread_mute_unlock
    • pthread_mute_destroy
  2. 条件变量,更多用于流水线,stream上的应用更多的像通知。相当于银行VIP的排号。 VIP接待室时锁相当于mute_lock. 而条件变量就相当于那个排号。

    • pthread_cond_t
    • pthread_cond_wait 解销互斥量并停止线程。
    • pthread_cond_signal, 如果一个线程对另一个条件变量调用pthread_cond_signals,
    • pthread_cond_broadcast, 所有在排队的号信号都会被唤醒。
  3. 信号量,可以IPC也可以ITC。 只用计数来实现上数两个功能。铁路的道口。 https://docs.oracle.com/cd/E19253-01/819-7051/sync-95982/index.html 二进制信号量相当于mute_lock.

    信号量是一个非负整数计数。信号量通常用来协调对资源的访问,其中信号计数会初始化为可用资源的数目。然后,线程在资源增加时会增加计数,在删除资源时会减小计数,这些操作都以原子方式执行。 如果信号计数变为零,则表明已无可用资源。计数为零时,尝试减小信号的线程会被阻塞,直到计数大于零为止. 线程池实现利用这个就会比较方便。同时可用于异步事件通知。

    • sem_init(sem_t * _sem,int _pthsared,unsigned int _value))
    • sem_post V增加引加引用计数
    • sem_wait P操给信号量S的值减1,若结果不为负则P(S),否则等待。 执行V操作V(S)时,S的值加1,若结果不大于0则释放一个因执行P(S)而等待的线程。
    • sem_destroy
    void producer(buffer * b ,char item){
        sem_wait(&b->empty);
        sem_wait(&b->pmut);
    
        b->buf[b->nextin]=item;
        b->nextin++;
        b->nextin %=BSIZE;
        sem_post(&b->pmut);
        sem_post(&b->occupied);
    
    
    }
    void consumer(buffer_t * b){
        char item;
        sem_wait(&b->occupied);
        sem_wait(&b->cmut);
    
        item = b->buf[b->nextout];
        b->nextout++;
        b->nextout %= BSIZE;
    
        sem_post(&b->cmut);
        sem_post(&b->empty);
        return (item);
    }
    
  4. 为了进一步提高效率,又分出读写锁的机制。读可以同时,写就必须是异步。

VHDL本身可以不是什么语言,只是硬件电路的描述语言,相当于FGPA的配置文件。描述了你何配置这些硬件资源。

FPGA 本质的并行性,大部分的操作都是并行的,主要是资源分配然后是其并行,基本处理流程就是信号也是电压的上升与下降来触发的时序。

流程

合成器, 相当于编译器的链接器的功能。把一个模块链接合成。

基本的数据类型

在FPGA中只有0,1而不同的数据类型,我可以让我们快速建模也来解释这些0,1,例如整 型,浮点型。

Signals Vriable Constants Variable Files

每一个数据类型代表一种数据常度,用于bit来记,并且同时定义了编码类型,这些代表了各种标准。

downto, upto

三种设计模式

  1. 结构化
  2. 行为模式
  3. 数据流

news

Efinix可编程芯片:可进一步推动人工智能技术发展 http://www.kejilie.com/leiphone/article/nIvmyq.html

效率篇

基本问题

工欲善其事,必先利其器。知其然,并知所以然。 然而加速那个过程。达到快速高效。 而代码的本身的技巧与工作量应该越小越好,把时间留给思考。

每一个事情都是可以有周期分阶段.

digraph G {
   rankdir="LR";
   Run->Trace->Profiling->Debug_extend->Deploy->Run;
}
  1. 快速的运行起来,看到其整体的运行框架。
    1. 通过可视化的trace 来理解运行过程
  2. 能够从源码编译。
    1. 添加各种profiling的参数
    2. 如何优化
  3. 能够快速debug,
  4. 能够扩展,并且快速测试
  5. 快速的部署
  6. 如何快速的写代码
  7. 如何build 与优化 跨平台
  8. 如何调试

快速运行

这里最耗时的地方。

  1. 环境的搭建
  2. 资源的下载
  3. 安装
  4. 配置

源码编译

  1. 各种依赖库的寻找与安装
  2. 编译本身,并行编译
  3. 编译除错, 各种编译的选项的设置 由于编译环境与原始环境不同,经常需要各种调试
  4. 源码的重要性,在出现问题之后,并且通过google也找不到的答案的时候,这时候最高兴的莫过于,编译一个debug版本,条件断点,一次命中的检查环境。 如果是脚本语言,那则更加方便的,直接利用解释器的断点指令来进行调试更加的方便。

调试

如何加快自己能够快速的速度。 最浪费的时间的地方,那就是一遍遍重复调试,因为只要复杂一些工程,整个环境搭建可能就需要很长的时间。减少重复调试的时间。 另外是充分利用中间产物,这样代码完成之时,也是第一次的整个流程完成时间。这样也就可以 保证增量开发。不然的话,等你开发完成,你需要完成的工作还没有开始。离真正的完成,之前,你还得修一轮bug,然后是运行完成所对应的任务。 基本是三倍的时间。如何将其降为一倍的生产率。

  1. 弄清楚整个流程
  2. 哪一个部分最耗时与资源,分配最多的资源。同时使自己的工程能够断点续传的功能。主要是添加一些log与status的存储的功能。 每一步要考虑模化化,例如下载的功能完成之后,就可以让其开始下载资源,而非等到最后最开始下载。

扩展开发

  1. 如何加快自己的实现编码速度。
    • 生成代码
    • 自己实现代码就要利用IDE工具的拼写检查,语法检查,以及自动补齐功能,来加快自己的速度。
    • 找到一个template快速的运行起来,这个要不了多少时间。
    • 而真正花时间的,是自己代码的逻辑设计,例如一个简单的数据迁移从trac到fogbugz,原理简单,但是用了4-5小时才完成。 主要是因为mapping关系的设计,这个需要拿到准确的数据。 原来只会用命令行的数据库来试,浪费了不少时间。没什么比可以视化的工具能更加方便的上手开始工作了。
  2. 参数环境的配置
    • 设置一个 ROOT,TOP dir. 然后所有的文件路径以此为基础。并且这个路径也又可以命令行参数来改。
    • log 即要有console,logfile。 log的分级标准
      • 能够显示系统流程的用info,通过info信息可以一眼看出,现在执行到哪里,可以把log来代替注释。
      • 把log当做测试。 把info当做testcase级别的信息,而把debug当做step级别的信息。
  3. 快速建立一个开发与测试环境。利用agile这样,实现一下快速开发的教程。最好是开发与测试能够并行。这样能快速发布一个版本。 这样可以大大地提高开发,测试与部署的并行化。 在文章与各种资料的不全的情况下,没有什么什么比一个可视化的debugger连接上去,给你一个快速的验证调试环境。 这个的速度可以的一遍一遍的print+log要高3倍以上。 在debugger的环境下,可以一遍一遍的重复,而不用害怕crash的问题,并且弄清楚API的使用方法。
  4. 生成代码的时候
    • 尽可能使用IDE减少语法错误,与拼写错误。
    • 使用一种命名规则,长短不重要,关键是统一。这样可以减少大量的错误,尽可能避免同一类变量使用不同的命名规则与缩写。 这样会引起大量的莫名其妙的错误。

IDE

最浪费时间的事情,莫过于,等10分钟以上,只是发现了一个拼写错误,并且一天的时间也只是解决了一些基本的语法错误。

所以在不同平台,就要使用最优化的工具来提搞效率。

对于编辑器,可视化与结构化,并且尽可能所写即所得。来加快编码的速度。 例如lighttable,以及visual code online的功能。

  1. 支持语法检查
  2. 自动补齐
  3. 拼写检查
  4. 支持重构

要想在每一个阶段达到用到最好的功具,那就需要各种灵活的配合,对于linux平台的开发,最要命的那就IDE的缺乏。现在 VS2017已经支持在linux在windows编辑,直接在linux直接编译。但是导入的模板不不是很好。不过现在github已经有一个 vclinux来帮助我们生成 vc 的工程,另一个快速的方法,那就是利用VSCODE 直接打开目录。 https://github.com/gwli/vclinux 其实这种工程的文件的生成也很简单。直接拿一个生成好的工程,然后把不变部分当做 template,可变部分,变量控制生成就行了。用一个template应该是在15分钟之内就可以完成的工程。

$ ./genvcxproj.sh ~/repos/preciouscode/ preciouscode.vcxproj
# set build command
cd ~/repos/preciouscode/; make
$ ./genfilters.sh ~/repos/preciouscode/ preciouscode.vcxproj.filters

另一个特别好用的工具那就是visualGDB 的VS 的插件,非常的周到全面。

对比工具目前最强beyond compare.

直接目录对比,双击文件,再自动转入文件对比。这样可以大大减少overhead.

另一个对比的办法,那就是利用 git. 把基准当做一个版本,然后另一个当做改动来进行对比。

VS for Linux

http://hongbomin.com/2017/03/10/using-visual-studio-as-linux-develop-IDE/

#. vs不会自动把addtional include中的头文件复制到本地来做代码补全的提示,需要手动将linux下面的/usr/include、/usr/local/include等目录复制到vs的linux header path(如:C:Program Files (x86)Microsoft Visual Studio2017CommunityCommon7IDEVCLinuxincludeusr) 头文件包含目录和库文件包含目录均为linux下的绝对路径

Build tool

build 本质,一个是翻译,一个链接,然后就是各种格式输出生成。当然在这个过程中也就大量的依赖树的生成。最简单一条指指令,到几条指令组成的指令块。 然后是这些依赖关系。再加上CPU的存取机制。 都是可以精确的建立一个模型。

build的难点在于各种依赖的管理,另一个那就是并行化的加速。 在需求与硬件之间的一个平衡那就是pool.

依赖的管理现在都是参考包管理的机制来进行的。现在基本是至少一个 group,componmentname,version 再加上依赖,以及一些auother,maintainer等等元信息就够了。

如何才能使翻译速度做到1分种之内就能够完成呢。

然后就是指定一个cache这样不用每一次都从云端开始下载。 在编译的过程,可能是一遍扫描,也可能是多遍扫描来实现的。 如果只用一遍扫描的话,那就要求大部分的信息都是局部化的。 多步扫描就可以利用全局的各信息。 而LLVM的优化就是采用这种多遍扫描,特别是优化这一块,可以是任意多次的扫描。

另外build的 pipeline的定义以最基本的有两个project,task. 这样每一个project与task之间都可以依赖关系。并且随时可以从中间重启。

工具的意义,管理文件本身的依赖。管理内部的库依赖。 并且能够并行编译,大大的减少编译的时间。大的工程编译也是特别耗时的。 特别是修改,编译,调试。 这样的循环中,会非常的浪费时间。

快速的修改,快速的编译,快速的调试。

ant 的语法没有make 那样直白。

如何用ant 给aapt 传递参数呢

https://code.google.com/p/android/issues/detail?id=23894

一个问题是自己为什么没有找到这个答案呢, ant/build.xml自己也读了。一个原因那就是没有问题提炼出来,compress 这个关键词,如果知道了这个。这个本问题简单的。

把问题 提炼成 一句话,甚至是关键词。

而java的编译选项主要是 Java.source与Java.target 详细的文档在http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html

所谓的build,除了编译本身,还有其他的做准备工作,例如把相关的资源也copy到对应的目录。同时做一些预处理。 编译本身,也就是指定一下头文件,以及搜索路径,同样对于库也是如此,另一个循环依赖的问题,那就只能前置声明来解决了。 没有什么特殊的东东。

其本质就是

build {
  set-evn;
  for * build { do each item}
}
copy something
process the resource
generate final package

对于MSBUILD例如一些库,你只是需要copy到输入目录,直接添加一个reference就直接copy过去了。 只在对应的类里添加一个文件名,或者新建一个task,并且指定一下,前后的依赖关系。 或者http://stackoverflow.com/questions/1292351/including-content-files-in-csproj-that-are-outside-the-project-cone http://stackoverflow.com/questions/7643615/how-can-i-get-msbuild-to-copy-all-files-marked-as-content-to-a-folder-preservin

代码阅读

如何快速理解,大的代码框架。 最好的方法,猜想+验证。 通过读代码来找到框架这个方法是最笨的方法。 通过文档,以及理论原理,就可以把框架猜的差不多。具体的操作,可以通过工具来加速。 例如VisualStudio可以动态的得到CodeMap,同时能够集成github直接查看版本记录。还能直接看动态内存使用信息。 已经把自己想要把debugger与profiler放在一起的时候给做了。

并且在一些关键的点加上一些断点与trace就知道其所以然了。没有必要一行一行代码去看。 因为代码中大量的参数检查以及其他等等会影响你的逻辑。

debugger + profiler + CodeMap/Framework原理图,就够了。

当然你需要大量的知识的框架知识,或者可以快速获取相关知识。 #. 如何快速读懂代码找到框架这个很重要。 #. 一些问题,只看到代码一眼,就能明白知道答案 #. 看了代码,还不能快速明白的事情,那就要添加测试,通过猜想验证,设计测试用例。设计测试用例的过程,就像猜想+理解的过程。这个符合人的认知过程。直接在debugger中添加一些测试这样对原代码影响较小,那外的那就是利用宏+NVTX。以及专用的debugger,它可以支持特别数据的可视化。或者直接python 的画图与显示工具。

读代码的过程与代码的过程本质就是不断在设计结构与算法之间不断trade off的过程。而读代码是一个反向工程,那就要不断猜想验证。 就像反向工程一样,一样的思维不一样的实现而己。

  1. 看论文以及介绍文档,再加上自己的思考得到一个大致的流程
  2. 拿到code编译能跑, 看看它都用到什么库,从而也就知道需要哪些相关的知识
  3. tracing,通过timeline 就可以看到其pipline了。可以通过callstack,来选择一个函数当做NVTX不行,不需要事先加NVTX.
  4. profiling 以及加论文,来从重点入手。 充分利用profiling 工具来达到trace信息,并通过定制化,把一些关键点上的数据截流可视化。这样就可以大加快自己的速度 。充分利用下一层接口工具。
  5. debuging + 可视化分析,来突破重点难点。 例如,在复杂的数据结构。这样就需要一个很好的可视化工具来帮助理解。 一个好的流图,不同层级的示意图,源码都可以了跑起来,就一定能让源码告诉你一切。 如何代码返回公式
  6. 分析总结各个过程,来实现全流程的优化。并且出论文与专利与自身的效能的提升。

什么情况下算完成

如何快速的写出文档

  1. 禅道项目管理式具
  2. ` Doxygen + Graphviz + Htmlhelp, 成为文档好手。 <http://blog.csdn.net/cuijpus/archive/2008/05/22/2471014.aspx>`_
  3. KFI与Graphviz分析内核
  4. KFT(Kernal Function Trace)

如何使用Graphviz

要逐渐开始使用这个dot语言,这个语言本身就像基他标记语言一样简单。其基本组成就是由图,结点,边,组成。再加上嵌套的子图来完成.在这里几种不同布线方式。其实这就像VerilogHDL一样,这样可以产生布线。同时使自己想起了画电路板。那里边就有一个自动布线的功能。要充分利用这些功能。同时在也学到了一种linux的思想,WYTIWYG(what you think is what get). 这个不同于微软的思想,所见既所得。

  1. 如何布局与联线
  2. 这个语言本身很简单,就是图,结点,边组成。同时他们也实现了几种布线方式。在布线的时候,可以这样干,可以试试几种方式。然后再决定使用哪一种.在使用的时候可以在库中找一个类似的图,然后再看看怎么实现的。然后拿来自己用。就采用cas的那个gui的界面的开发与学习方法。这样就会越来越精通。
  3. group,id of graphviz how to use them?
  4. visualization of Information
  5. 用 Graphviz 可视化函数调用

HowToDebug

调试程序方法有很多,但最重要的是自己的思考。再好的工具的工具都不能取代你的思考。 调试的速度与精确度 思考 + 经验+实践+良好的系统知识。

gdb step 也是为了你的思考验证来准备的。通过log来分析,search才及动态的分析工具 都是为了你的思考。思考要用事实来验证,并且要基于事实,并且事实来突破自己的思维局限。 最难的那就是 mental model bugs. 在你认为最不可能出错的地方出错了。并且这种错误一般都是由引低级的错误引起来, 例如 语言中逻辑操作码的一些优先级引起的。 当遇到这种问题,要寻求外部的帮助。同时要意识可能是mental model 出错了。

思考什么呢,因果联系,这是最重要也是最基本的联系。 可以通过自己的常识不断细化这种因果联系。当然也可以即时从google上学习来的。 例如能够can’t break on sigfpe,就要能够快速学习相关知识来进一步的推理,能够判断出这个是硬件的问题。 https://devtalk.nvidia.com/default/topic/832136/nsight-tegra-visual-studio-edition/cannot-break-on-sigfpe/

原则 debugging is an art that we will practice regularly, The first thing to do is to think hard about the clues it presents. If there aren’t good clues, hard thinking is still the best step, to be followed by systematic attemps to narrow down the location of the problem.

– The Practice of Programming Page 145

  1. Examine the most recent change. 如果使用vs2015的话,内部集成这些git可以快速查看修改。
  2. Don’t make the same mistake twice. 修改的时候,最好用重构代替copy,并且进行搜索查看。反复的修改同一个问题,例如同一个问题提交很多版本会令人沮丧的。
  3. Debug it now, not later. 尽可能修复当前所有遇到crash以及己知问题。 因为这为后来提供更干净的环境。根据复利的计算,bug的修复越及时,成本越低。最起码不用 再手工清理恢复环境。
  4. Get a stack trace. 最重要的信息。
  5. read before typing. 多思考后动手。再没有思路的时候停下来,休息,然后再进行。
  6. Explain your code to someone else. 帮助自己理思路
  7. 发现别人的bug时,确定这个在最新version能够重现。 一般不会在一个老的版本中去修一个问题。
  8. 当给别人提bug时,站在owner的角度想想需要什么东东。

同时快速修复一个别人的bug时,也是根据这个原则来的。

  1. 看一个修改的时候历史。
  2. 查找一个变量的引用到的地方。
  3. 查看一下函数引用到地方。 就差不多,可以确定如何修改了。所以修一个bug,没有重构那么难。
  4. 看了解一下整体结构,看看要不要重构。 整个过程不要超过两个小时。

当遇到没有线索bugs时

  1. Make the bug reproducible. 然后统计分析规律(study the numerology of failures),然后narrow down问题。
  2. Divid and conquer, 采用二分法来narrow down.例如利用vim的undo功能,特别适合这个。 例如加入log, 例如执行到这里了。 并且记录这个过程。 当然git也支持2分法在版本之间查找。 即使是一时不出来原因,做好记录也可以为以后分析做积累。

当然这些原则再加上debugger会加速你的问题。

  1. 另外添加一些有用的self-checking code。也会加速你的问题解决。 并且能够把你的收集信息可视化,会大大加快你的速度。 这一点vs2015中可以在debug时生成codemap,并且随着step不断更新你的这个图。 把callstack不断可视化。 这个也可以用gdb + graphviz 来自己实现一个。
  2. 写好log,是你解决问题时信息的最大来源。当然对于大的工程没有现成的log可用时,可以debugger的trace功能以及profiling来产生规范的log,然后再加上自己的可视化分析。尤其是可视化可以大大地加快你的思索。

当代码在一个机器时正常,而在另一台机器不正常。 这个一般是由于环境引起的。 例如查看环境变量,以及依赖的库的版本。一些相关配置文件。 还有的那就是一些随机输入。 还有那就是共享变量,一些全局变量,无意中被不相关的东东给改掉了。 这是你采用链式逻辑推导不出来的。 这个时候就要用trace 变化来实现了。例如可以定时或者实时从 /proc/envinron 中获取这些信息。

  1. 要知道什么是对的,每一步中。 排除不确定因素。 特别是一些变量,指针没有初始化。使其处于未定状态。变量作用域的传递问题。 这些都是极其容易出问题的地方。
  2. 遇到sometimes问题,最好的办法,详细的log或者直接生成coredump,这样就能清晰的context了。 再加上合适的工具grep,diff,以及可视化工具等等。

调试程序很多方法,解决问题的最重要的方法,那就是不断narrow down,直到减少范围,直到找到root cause, 用log,debug能快速得到callstack等等线索。 因为模式设计就那几种,自己停下来想想,按照概率最大蒙也蒙的出来。 如果不能,选模块的分割,再了解流程再进一步narrow down. 就像修改那个 CMake 生成 Deploy 选项一样。 最终就只需要 else 语句就搞定了。

一个难点,那就是搭建调试环境,只要方便。最好方法那就是能在出错地方停下来(例如像pdb.set_trace()这样的功能最方便),即使不能可以打log.

调试器虽然可以用step by step,但是主要功能可不是在这里,最高效的功能在捕捉异常。所以重点是exeption,core处理,以及各种事件的支持,例如 .so load event.例如

  1. 自动停在main处
  2. 例如cuda 的自动停在kernel launch 处
  3. 自动停在出错那一行,尤其是cuda-memory等工具结合的时候。
set print thread-events.

如果能到源代码

  1. 添加编译选项使其具画出call_graphic. 或者直接使用 VS中智能分析出来的。
  2. 能否换成clang编译来优化一下代码。
  3. framework pipepline 查起。然后不断的narrow down.
  4. 通过读源码就得到答案,遇到问题,就要能够去想哪里出了问题。然后来猜想加验证。
strace and sreplay

应用程序分两大部分,一个是自身的计算,另一个是外面的调用,error信息肯定会体现在下层的调用上。 所以出错的时候,看一下一层的API log一般都会发现原因的。

stracesreplay 可以抓取系统调用并且能够回放。例子见[streplay]_

[sreplay]http://people.seas.harvard.edu/~apw/sreplay/
如何让自己的程序变的动态可调试
  1. 在自己的代码中全用 命令行参数处理 以及 logging等级处理 例如syslog,以及NLOG等的使用。
  2. C 语言中可以采用 assert() 函数来定制调试,并且这些是通过宏控制的。打印出错信息。然后限出。
  3. 每一个系统都会支持各种event,在处理前后都加上hook来capture. 同时也可以利用signal自己生成coredump.或者等待debugger连接上来,就像windows这样,只是一个hook而己。
  4. 另一种方式那就是把内存当成一个存储系统并在上面加载一个文件系统。这样就可以高效的存储了。充分利用各种cache. 例如debugfs,tempfs,/proc/ 都是直接存储做到内存上。可以非常方便查询各种信息。
  5. 充分利用配制信息,windows与linux是越来越像,都开始在home目录下写各种配制了。
  6. 对于windows还可以用debugView来查看这些调试log.
  7. 充分利用条件断点来添加log and trace.

process monitor可以实时显示文件系统,注册表和线程的活动。

如何调试并行调试

这个可以参考CUDA的并行调试。一个重要问题那就是对线程的控制,CUDA提供了基于lanes,warp,block,grid的,以及任意的frezen/thaw,以及支持与与或非的查询条件。可以方便过滤那些thread的查看。

调试都需要信息

debug Symbols 信息,有了符号表才能符号表地址对应起来,并且还源码对应起来了。对于GDB来说,那就需要设置 symbols directory, 另外那就是源码目录。还有那就是如何起动。 当调试环境与编译环境不一样.symbol path 是对应不起来的。 可以在 set substistute-path /afafa/fadfa /xxxxx 来解决这个问题。 for apk, they need androidManifest.xml to get the package name to start it.

signal

也就是kernel发现在东东,来通知应用程序来处理, 例如键盘有了输入,硬件中断在软件就叫signal. 也不是操作系统告诉你发生了什么事情,至于你怎么处理那是你的事情,除了一些标准的消息kernel会强制处理之外,例如kill -9 等等。 exception,就是kernel发现你做错了来通知你。你丫搞杂了。可以用kill -l 就可以看到所消息。 kernel与进程的通信,就是靠这些signal中,这些是模枋interrupt的。有些标准的signal,也有些预留的。例如进程什么停止,kernel都是利用这些signal来通知进程的。

条件断点使用

断点的本质,那就是各种event,例如库的加载与卸载。以及系统的其他event,callback. 道理早就懂,但是用的时候就想不起来,一个原则,那就是尽可能停下来地方尽可能接近出错的地方,包括时间与空间。再简单的场景: 你实现了一本功能,有很地方会用到它,突然其中一个调用crash了,或者出了问题。直接下断点,就会在没有crash的地方停下,停下来n多次。这个时间就需要加一些条件来帮助你停下来。

  1. 如果有明确的信息可以知道在什么条件下会出现,例如其caller,或者某个具体值,直接上条件断点。直接停到最佳地方,而不是手工去点next.
  2. 如果事先没有明确信息,可以先用trace的功能,打印出前后上下的context信息。 然后再根据这些信息设置条件断点。
  3. 充分利用数据断点,可以来快速调查被非法读写的内存块,数据断点,当内存被读写的时候,就会被停下来。

所以快速的解决应该最多三步就能搞定。

  1. 搭建环境,只需要重新编译一个代码加载symbols。
  2. 明确断点信息。 然后利用trace 的功能,来打印各种想要的信息,而不需要再改动代码。对于大的工程build是需要很长的赶时间的
  3. 停到最佳位置。直接用条件断点停到这个位置。是一部分到位。或者直接让gdb来hook signal或者exception是同样的道理。

#. 在第二步与三步之间采用二分法,无限细分下去,直到找到原因。 因为经常出错的事,我们分开验证A,B两部,都是正确的,但是合在一起就会出错。但是这两者已经离的很近了,并且或者从经验上认为是一致了。感觉已经没有办法了。实在是想不出来是哪里出错。 而实际上就是这个细微的差别出现的问题。 就像我自己项目中 从逻辑上,大的功能块上 二分,到代码行二分,再进一步到汇编指令二分。大部分时候,大家只能走到逻辑上二分,就以为到头了。

并条件断点处,打一些trace,再加上timestamp信息,格式再好一些,就可以直接profiling了. 例如在Visual 中,可以用$TICK 来打印出CPU的TICT, 对于gdb就更灵活了,各种shell命令可以用.同时python的集成. 还外也可以直接借用app本身的一些全局变量,Log模块也就可以.这样就不需要修改源码本身,就可以profiling了. 有了这个可以直接定位问题.如果能配合录屏软件时间坐标就更精确了.

例外对于一些profiling工具,如果能提供api 查找,并且显示其对应的timeline那就更方便了. 如果不行的话,又不想写代码,又想让app停在 某个位置,那个时候就要到debugger,pause的功能.如何这些功能整合在一起呢. 用expect +gdb + shell就可以搞定这一切了.

debugSymbols

机器在做什么,都是通过代码休现的,代码显示就是那些函数名了,通过debugsybols可以机制码与可读性代码连接起来了,方便人们理解 机器正在做什么,即使是release也是可以生成symbols的.

在大的功能快,module上二分这是逻辑问题,具体到源代码这一级,还是逻辑问题,到汇编这一级,那就是性能问题。从汇编到机器码,那就是ABI,机器构架之间的区别了。

system("fadfa")
exit(0)

实际代码中在system(“fadfa”)就已经crash了,但已经还是想当然以为exit(0)执行了。

如何在exception与handler里debug

特别是crash时,能够看到当前的callstack等等,并且来改变程序运行顺序,这个时候 就需要debugger,来捕捉exception and signal了。 一个最重要那就是拿到callstack,另外无非的情况那就是非法地址,首先是其owner是谁先打这个符号,例如oglContext这个值成为空词,自然对成员的访问会出错,所以这个值哪来的。我应该期望的值是多少。 根据地址段来分析可能出错。是数据本身出错,函数回指针出错。 然后根据地址来得符号表,这个地址是哪一段出现的。这个时候就需要debugger连接上去,然后hook这些exception然后就让他开始他。并且debugger attach上去之后,可以看到更多的信息。 http://www.read.seas.harvard.edu/~kohler/class/aosref/i386/s12_03.htm

SIGSEGV

出现段错误,指针不对, http://stackoverflow.com/questions/1564372/what-is-sigsegv-run-time-error-in-c 也就是adddress不对,读取不不该读取的地方。 https://en.wikipedia.org/wiki/Segmentation_fault

如何搭建环境

其实也就是现场截面的恢复。其实就是现场中断与恢复。以前也只是说一说,现在看来用到实际中了。

大的应用程序,那就是保存其环境变量以及输入与输出。 就可以直接切入环境,而不需要从头运行需要大量的时间。

对一个函数来说,也就是输入输出,以及相关的全局变量而己。而这些都是可以通过trace来得到。

还有那就是利用coredump与debug symbol来恢复现场。 例如gdb,先加载debug symbol,然后再打开coredump就可以了。 另外那就是让crash的程序自动生成dump文件,或者发生特定的事件的情况下生成dump文件,在windows就要用debug diagnostic tools了。对于linux 可以用gcore来生成,或者gdb里面也可以生成。 也可以用ulimit来指定。或者用signal SIGBRT,或者调用abort()函数就可以直接生成。 http://stackoverflow.com/questions/131439/how-can-a-c-program-produce-a-core-dump-of-itself-without-terminating/131539#131539 http://stackoverflow.com/questions/318647/what-is-a-good-way-to-dump-a-linux-core-file-from-inside-a-process http://www.codeproject.com/Articles/816527/Writing-Custom-Information-in-Linux-Core-dump-usin

同时glibc同时也开放一个backtrace的函数来得到callstack. http://skyscribe.github.io/blog/2012/11/27/linuxshang-ru-he-cong-c-plus-plus-cheng-xu-zhong-huo-qu-backtracexin-xi/

出了错了,另一个查找错误的方法,那就是读代码,如何读,通过版本的对比,同时根据依赖关系,得出一些改动的真实原因。 这时候就需要各种diff,快速编辑,以及快速navigate 同时能够做各种依赖关系的的工具。 脚本,vim,idff,vscode, git等等都是重要的工具。

NPE

NPE Null pointer exception.

Can’t Find resource

经常遇到这样的问题,例如undefined symbols,找不到的库。例如C#遇到找不到XX14.0.dll. 这样的原因有以下几种:

  1. 确实不存在对应的库
  2. 所依赖的库存在,只是依赖库的Error处理的不好,没有正确的显示
  3. 库确实存在,但是版本不对,有些依赖是版本要求的,所以搜索的条件也不一样。所以要仔细看它的搜索条件。
  4. 由于不同版本之间不兼容,例如函数名的改变,或者编译器不同而导致的格式改变。

解决办法

  1. 最简单的办法,在对应的目录里直接搜索,然后查看其版本信息等等。
  2. 用LDD 查看其依赖。 windows下用dumpbin 来查看。

#. 对于C#可以用FusionLogViewer来查看。 http://www.hanselman.com/blog/BackToBasicsUsingFusionLogViewerToDebugObscureLoaderErrors.aspx #. 最差写一个wraper来测试,直接debugger来查。

如果查看内存分配

如果精确查看进程的内存分配呢,在linux下有强大的 /proc 可以用,另一个方法,自己根据结构直接读内存。 从memoryWindow可以直接查看各个地址,并且还可以转换基本格式,像graphic debugger里那样显示texture 都是读取内存数据来得到的。同时还可以用来研究自动变量的分配。并且一些数据转换,例如整型,浮点型的转换,format 这些都是可以在memoryWindow直接做的。直接修改内存值。

进程数据存放无非两种,放在内存里,或者寄存器里。

内存泄漏可以inject内存管理函数,并且建立自己内存管理模型来进行监测,所谓的代码插装,在源码级别可以预处理的宏替换来实现,那就像MFC的那个消息结构一样。在宏替换原来内存管理函数后,同时利用 _FILE_, _LINE_,_FUNC_来获得动态分配函数所在的context信息。 利用exception + __FILE__,__LINE__,__FUNC__来得到callstack以及文件的对应关系。

strings的使用

在二制文件中查找error信息时会很有用。为什么呢,因为代码中一些字符串信息也都存储在binary中。只是编码不同的而己。

如何Goto

在大的工程里,因为一个小错误在重头来过,有点得浪费,怎么办呢,直接修改了,然后直接跳过去,这个是函数调用不能解决的。 只能goto才能解决的。 goto解决方法,当然用指令,另一种那就是直接修改PC寄存器值。 在Visual Studio中,那就是set Next Instruction的功能。 http://www.cprogramming.com/tutorial/visual_studio_tips.html

利用python plugin

以自定的命令,再加上各种command的hooks来实现 各种测试与与调试信息。 充分利用这些可以大大减少harness的准备的工作。

对于大的并行程序,有专门的profiling与debugging工具,例如 http://www.roguewave.com/products-services/totalview

如果调查crash

查看log时, 有很多error,一定要找到第一个error. 就是编程时,要从第一个error来解决开始。 在查看error时,最简单的办法,那就用时间戳来决定。

minidump

目标是为生成一个最小的包含问题的可执行程序,这样可以大大加快troubleshot步法,特别是对于程序,每一次repro都会浪费大量的时间。 如果生成这样一个程序切片,就可以大大加快troubleshot的效度。

SIGILL

一些warning也会产生一些运行时错误,不要轻易忽略编译中warning. 尤其是那些类型转换 https://peeterjoot.wordpress.com/2010/05/26/a-fun-and-curious-dig-gcc-generation-of-a-ud2a-instruction-sigill/

GDB 调试

Introduction

gdb 操作,相当于直接操作CPU与内存。 CPU的状态是可以通过寄存器的状态来进行控制的。例如set next step, 相当于修改 PC,IP 寄存器值。

debug都是基于debugsymbol中,这个symbol会存储debug与source code line num 的对应关系。 断点不能hit,也就是没有dbgsymbol没有,或者你的源码位置与dymbols中是不一致的。

如果首先看没有debugsymbol, sharelibs. 然后 info lines. 就知道了。 如果不匹配,会发生在build的机器与调试机器的环境的不同。 或者当时生成不是相对路径,或者现在相对路径不对。

可以修改相对路径,或者直接替换路径,例如下面

apt-get source linux-image-2.6.32-25-generic
apt-get install linux-image-2.6.32-25-generic-dbgsym
gdb /usr/lib/debug/root/vmlinux-2.6.32-25-generic
(gdb) list schedule
(gdb) set substitute-path /build/buildd/linux-2.6.32 /home/xxx/src/linux-26.3.32

同时可以用 objdump -Wl <lib> 来查看其path是否正确,特别//,\ 这些分界符还有的那就是相对路径。

debug 的难点

  1. 如何在别人不能断的地方进行断点设置。列如线程调试、远程调试,对正在运行的程序进行调试、gdb是可以的。
  2. 如何理解对象的逻辑结构,进行快速地诊断。
  3. 能够查看当前正在运行的调用stack,通过这些可以跟踪程序的内部运行机制。只要知道了原理,然后直接debug就可以得到更加真实可靠的知识,更快的方法是猜想+验证。 找几个具有特征点来看一下其调用关系。例如正常运行,以及特殊情况下,调用关系。同时在不同的时间调用关系,就可以得到系统运行的立体信息。
  4. 同时debug工具能够提供汇编代码与源码之间的动态关系,并且通过查看这些可以得到一些感性的知识,例如一条正常的语句的汇编语句呢。 要把语言常用结构都给过一下。
  5. 一般作为人机接口的协议栈都会提供两种接口,humnable接口,还有一种那就是MI接口(machine interface),例如telnet,tl1,机器接口不会回显。 perl 语言能远程调试吗。(什么?)
  6. 调试分为与硬件相关的与内核相关的,与硬件相关的,例如中断信号机制,对于内存地址的转变,以及系统资源状态的变化。例外那就是进程的状态,线程的状态,以及调用堆栈的变化,同时系统调用库的变化,以及编程语言的转化,例如脚本语言到高级语言,再从高级语言到汇编的转化过程,在调试的过程要学习编译原理。
  7. 变量声明的语句是没有,编译之后就没有了,所以不能在变量声明的地方设置断点。但是解释型好像就可以,例如在perl里是可以的,试一下java是否可以,以及其他语言。
  8. 多线程如何调试。
  9. gdb finish 执行完这个函数余下的部分,
  10. gdb until 执行到当前函数的某一位置。

vS 已经实现了更新debug方式,那就是在每一个断点处生成snapshot,这样就可以来全回退,这样就不需要每一次重新运行了。对于 gdb来说,我们可以完成每一次的手工的生成与加载切换不同coredump. 同时gdb 还自身也有bookmark的功能。

bookmark start/end
goto-bookmark
help bookmark 这个功能是在 gdb 5.6之后就有了。
#load core-file
gdb> core-file <coredump>
#gen
gdb>generate-core-file [file]
gdb>gcore [file]
gdb>set use-coredump-filter on/off
you can check /proc/pid/coredump_filter
批量的添加断点

在gdb 中直接用info source 或者info functions 就看到全部函数名,并且还可以用python来操作,就像vim中一样。要把gdb练成vim 一样熟悉。 这样就可以直接用trace命令来收集数据。 VS中对于immediate Windows是可以执行一些调试命令,并且提供运行时库的相互环境,就像一个脚本语言解释环境一样。 另一种方法,那就是利用event来收集数据。还有那就是系统的signal. https://xpapad.wordpress.com/2009/05/18/debugging-and-profiling-your-cc-programs-using-free-software/ 关键就是检查了symbol table了。

gdb,attach 意味着你进入这个进程的空间,可以方便它的一切。

调试器的用途

  1. 可以动态查看程序的各种信息,ABI,以及有哪些库的依赖。所以遇到查询各种信息的可以直接debugger联接上去。
  2. 动态修改代码,及执行其内部的函数。
  3. 动态获取系统的状态。
  4. 如何定时debug. 可以通过添加asm(bkpt) 来实现。

如何实现引导代码

profiling都是支持tree 的,调试也是一样的。自己可以设置一个引导程序然后来加载自己的应用程序。然后把调试移至后面。这里用到那就是 gdb set follow-fork-modedetach-on-fork 等。gdb process tree . 另外那就是 gdb wrapper see here .

  1. gdb wraper .

Debug 的实现机理

实现三部分,

用户的输入

user interface,除了CLI接口可用各种后端,例如ddd,以及VS的MIEngine. 可以利用 readline/history等库。

gdb 可以当做后台,也可以直接使用,同时也支持vim 类似的分屏功能,利用layout的函数就可以实现。 符号处理,symbol handling, 这里主要是由 BFD/opcodes来处理。

要解决的在什么加载符号表,以及如何手工加载,一般情况下,在文件被调用的同时加载符号表,如果没有加载,可以load,unload重新做。 add-symbol-file/from memory 同时在 命令行也是可以指的, –symbols, –write 可以应用程序写入信息。 目标系统控制层: 用ptrace类似的系统调用来实现。

断点原理

通过查找输入的断点和具体代码位置对应起来,并在该位置替换为一条断点指令,并且保存以前的指令,到目标程序运行到该断点处时,产生SIGTRAP信号,该信号被GDB捕获,GDB查找断点列表来确定是否命中断点。继续执行的时候则会把保存的指令重新放回并执行。n/s/ni/si/finish/uitil也会自动设置断点。 条件断点的实现,也就是对于SIGTRAP的event callback chain上的一个而己。

断点插入的时机, gdb 将断点实际插入目标程序的时机:当用户通过 break 命令设置一 个断点时,这个断点并不会立即生效,因为 gdb 此时只是在内部的断 点链表中为这个断点新创建了一个节点而已。 gdb 会在用户下次发出 继续目标程序运行的命令时,将所有断点插入目标程序,新设置的断 点到这个时候才会实际存在于目标程序中。与此相呼应,当目标程序 停止时, gdb 会将所有断点暂时从目标程序中清除。

http://www.kgdb.info/wp-content/uploads/2011/04/GdbPrincipleChinese.pdf

信号

内核传递给被调试进程所有的信号,都会先传递给GDB再由gdb采取定义的动作来和被调试进程之间进行相互协调操作。gdb暂停目标程序运行的方法是向其发送SIGSTOP信号,GDB对于随机信号(非GDB产生的)的处理包括,可以通过handle signals命令来预定义

对于信号的处理,gdb如何反应,另一个那就是要不要传给debugee本身。

GDB的实现 原理 以及如何手工操作 /proc*

目标的系统的控制

而在对子进程数据访问过程中,ptrace函数中对用户空间的访问通过辅助函数write_long()和read_long()函数完成的。访问进程空间的内存是通过调用Linux的分页管理机制完成的。从要访问进程的task结构中读出对进程内存的描述mm结构,并依次按页目录、中间页目录、页表的顺序查找到物理页,并进行读写操作。函数put_long()和get_long()完成的是对一个页内数据的读写操作。

除了修改数据,同时CPU的结构也是可以改的,各种寄存器值,以及堆栈的值,如何确定特定的位置呢。

一种是通过寄存器,因为各个寄存器在ABI上有对应的分配,例如一般R3放返回值,PC程序寄存器值,SP,BP,是段值。 直接用汇编就可以任意的指定。

虽然用汇编是灵活了,但是细节太多,太麻烦呢。如何在C语言达到汇编的效果呢。问题的关键是一些高级语言与低级语言没有直接mapping关系.其实也不是没有关系,而是以前不知道分配策略而己。一是可以用ASM接口而做,二是直接特殊变量的位置,来得到邻居的位置。 例如 不同类型的变量,static,global变量,还有函数中第一个变量,它的地址进行加加减减就可以得到其他变量的值,例如最后一个变量地址+1就是return或者call之前的 寄存器的值,这时候只需要用指针来修改一下内存就行了。同时也可以用函数指针,就可以得到代码段的数据本身。

研究编程这么久,从开始就把这一点给忽略了,从学习微机原理时候就知道CPU有单步执行的模式。其实也是通过中断的来实现的。在汇编语言中可以直接加入bkpt,或者trap 指令来实现。这也就是breakpoint与tracepoint的源头了。执行这个指令CPU就会停下,你可以查看CPU的各种信息。也就是所谓的调试。这个其实与python pdb.settrace()的功能是一样的(今天才知道它是如何实现的)。其实就是bkpt 的功能。如果自己在代码的任何地方停,就可以在里面直接加入asm(“bkpt;”)就会断下来,这个然后再进程发一个 SIGCON来走下去。现在知道如何利用汇编直接操作硬件了。这也就是今天看CUDE asmdebug的代码的成果吧。如果这一下能停,可以查看或者硬件各种寄存器了。就是现在linux也只是利用CPU的部分功能。例如linux只用CPU的执行等级中二级。如果充分利用硬件功能,那就要是汇编了。

同时硬件也提供硬件hardware,也有采用软中断的方式。 硬件本身可以提供一个断点表,同进也是软断点,实现。对于汇编来说直接就是bkpt这些指令了。对于高层代码是如何实现的,那就是debugInfo的表,这里有每一行有效代码对应的汇编地址。这里会提供每个函数的入口与出口地址,也就是LOW_PC 与HOW_PC,有了这个表,可以生成callgraphic, 一旦有这个表,就像往你的代码里注入任意的代码,所谓的那些profile参数就是这么看的,每一个函数执行的开始与结束都加进代码。或者直接全用tracepoint 来实现。 通过分析,每一个函数指令位置,然后查看中间的jump指令,就看ABI是如何规定函数调用。就可以画出这个图了。这样通过objdump 得到debug infotable,然后根据这个表生成call graph. 并且已经有这样工具利用h -finstrument-functions,在编译的时候加上这些选项。

如何在任意地方设置断点,如何找到函数的指始点,只要是可以执行文件,必然会有一个entry address,得到这个地址,看看其对应的代码的哪一个函数也就自然找到入口点了。

现在知道如何编译来进行分析source code了。

另外一点,那就是调试的那些信息都从哪里来的呢。

同时可以在 通过 info share 来查看指令段,就可以知道在哪里哪一个库crush,并且还可以知道在哪里设置断点。并且利用addr2line 就可以得到。

当然也可以直接在 gdb 中实现这些事情。例如 info address symbol等等。

info address symbol
info symbols addr
whatis expr
whatis

这些可以非常方便让我来查看 ELF的生成格式,这个要比 objdump要直接有效的多。

in linux, you can use signal and /proc and some CPU interrupt do debug, don’t need the GDB. for example on the production line. You can do like this. send Pause signal to the process and check the /proc directory to get the status of the process. Proc interrupts , /proc/interrupts 和 /proc/stat 查看中断的情况 那到底是用的硬中断来软中断来实现的呢。并且gdb 还支持对gdt,ldt,idt的查看DJGPP 。

info dos gdt/ldt/idt/pde/pte     ;info w32 info dll

几种方式是插入汇编asm(bkpt) 代码,或者采用指令替换的方式,例如在原理断点处插入跳转指令。把原来指令给换掉。 BP的插入也是代码注入的一种。 汇编的bkpt指令是一个字节,然后把第一个字节换掉,然后把原始指令保存下来。有两种做法,single-stepping inline,Single-step out of line.

gdb 主要是基于ptrace来实现,ptrace系统调用可以修改,进程的数据段与代码段的数据的,同时修改CPU的指令模模式。 进程是即有CPU的模型信息,又有代码与数据的信息。通用ptrace可以控制进程各种信息,例如加载什么包,调用过什么函数都可以用这个来进行控制调用。 http://www.spongeliu.com/240.html 可以参考这本书GDB Pocket Reference。 http://www.cnblogs.com/catch/p/3476280.html, 使用ptrace可以实现进程各种定制操作。 http://www.linuxjournal.com/article/6100?page=0,1

ptrace是通过发送SIGSTOP让进程挂起的,ptrace也是通过系统信号来与进程交互的。ptrace是通过修改断点处的指令为trap指令来实现的。ptrace采用SIGCHLD的编程模型,即目标进程发送SIGCHLD,调试器调用waitpid来等进程。

但是ptrace也有很多的缺点,例如一个进程只能被ptrace attach一次。将来会用utrace来取代。 http://www.ibm.com/developerworks/cn/linux/l-cn-utrace/index.html

当然主要是也sigtrap信号的实现,不管CPU的硬件实现,还是软件实现。原理都是一样的,因为在于速度:http://stackoverflow.com/questions/3475262/what-causes-a-sigtrap-in-a-debug-session

并且虚拟机也是通过ptrace,来实现的,现在可以用utrace来实现。

变量的值

我们在调试器里看到的变量的值,都是从哪里来的呢。是在内存里,还是在寄存里。对于CPU这种时分复用的机器,变量基本上就都存在内存里,而寄存上只是短暂的时间片的瞬间, 所以说这些值是内存的哪一段放着,并且它的邻居是谁呢,这样同样会大大影响存取的性能的。如何得到这个变量的赋值表呢,就是简单的bss段以及.data段吗。

一种是全局变量,文件静态变量,函数的静态变量如何查看,通过

file::variable
function::variable

同时 gdb创建了 variable object 这个是为 frontend与gdbserver之间同步信息使用。哪些内容我关系,我发一个variable object过去。有更新变化就得通知我的方式。 http://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_231.html https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Variable-Objects.html

经常看到些结构是欠套的,所以经常看到 -var-create next的东东。

为了减少数据的传输,也做了各种优化。例如 trust-readonly-sections. 只读数据不就需要从传输了,从本地取就可以了。

这是同步一种方式,相当于双方建立同样的符号表状态表,server那些有变化就通知client,没有的话就只用保持同步的heartbeat了。 当然client自身还是可以做别的事情。 那就是通过event来同步,可以是同步,也可是异步的。例如step in/out/over应该是同步。 其他的就可以是异步的。

进程表与线程表

这个又是读的信息呢,正常怀况是进程是读的全局的GOT,是直接读的还是通过API呢。

而线程表则是每一个进程内部的TLS吧表吧。

module列表

elf结构的哪一些块放着呢。 module 加载的顺序采用深度优先的模式,并且得不断改写进程中GOT表,来进行重定位那些lib。这些module都是按照顺序加载的。逐section加载的。然后需要不断的调整各个.got表,以及.got.plt。 各个module就是通过自己.got 与.got.plt形成一个module链。 这个列表是可以用:command:info file 来看到的。 对于动态的链接库来说,第一个加载就应该是 /system/bin/app_linker. 在哪里寻找这些库,可以用set-solibsearchpath 来设置,原理path的一样的,不支持递归。 或者直接用 sysroot来进行统一的设置。 同时加载 moudle还可以定制化,

set stop-on-solib-events 0/1
show stop-on-solib-events
auto-solib-load

来设置加载lib是否加载, lib. 当然也可以用sharelibrary来强制加载一个或者全部的symbol单独来做。

http://visualgdb.com/gdbreference/commands/set_auto-solib-add http://visualgdb.com/gdbreference/commands/set_stop-on-solib-events

GDB 会在solibpaths 中按照顺序查找匹配的lib. 匹配的标准:

  1. 名字相同
  2. 有DWARF section
  3. Arch match
  4. build-id RSA 签名验证一致

同一个库,加载找到第一个,如果每一个失败,停止继续寻找这个库。

content/LLD.png

VS 给的link 顺序为 A,D,C,B;而gcc 需要顺序为 A,B,C,D.

代码块

既然代码可是每一个代码一个section,那在内存里呢,这个表又是如何组织的呢。在内存里是把所有代码放在一起呢,还是每一份独立放置的。这些都是可以通过调试器可以得到的。

写两个函数直接放在一起,然后最后两个内存地址相距多远。

callstack是如何查询的

这个当然是通过进程的栈来查看的,如果在不出栈的情况下就知道下函数调用在哪里,是如知道一个函数占用了多少呢。

disassmbly window

这个window是把代码段给解析了出来。

Auto local Watch

三者分别在哪里,

  1. auto 应该是当前指令正在执行的变量,应该这个时候就都已经在寄存里的。
  2. local 变量应该是函数内部变量,就是当前栈里所能看到变量。前auto一样是动态的。
  3. watch 而是 .bss 以及 .data对应的内存段。

通过这些地址就可以知道,进程大概的内存分布状况了,并且只要找到起始值,就知道其范围了。

而那些debug info 这些默认起动不加载呢,还是根据文件本身,有了就加载,没有就不加。

而这些是通过 GDB variable object 来实现的。

符号表以及其加载机制

debug_info 表对于调试起着至关重要的意义,它是源码与二制码之间的桥梁,只有debug_info 表认出来了,才能知道走到了源码的哪一行了,没有符号表那只能调试汇编了。另外没有符号表,BP就认不出来,因为你的断点是加在源码上。所以不能hit断点,两个东西要去查,符号表是否加载了,一个是相关库是否加载,另外库是带有符号表,还是被stripped, 库加载了,但是符号表没有加载。如何判断呢,在加载之前设置断点,然后一步步来,看看能不加载。例如module列表,是不是加载。另外还要看符号表有没有。

一般情况下debug_info表生成是绝对路径,当然也可以设置生成相对路径。当采用remote debug时,采用相对路径就会相对方便一些。

debug_info表与 符号表是不同的两表,符号是要程序动态加载的用的。具体见符号表。

对于gdb中要设置的一个是 solib-search-path. 另一个就是源码目录,directory

同时当大的obj文件中,加载symbol本身也会很慢,gdb 支持 生成index来加速这个过程,同时一些编译也支持生成。 另一方面在remotedebug时,把远程的sysroot给提前cache到本地就也可以加块速度。 例如 android的备份的是

同时也可以 set sysroot    target://

system/bin
system/lib
system/vendor/lib
gdb -batch -nx -ex "save gdb-index C:\\directory_path" "C:\path_to_UE4_project_output_directory\libUE4.so"
自动加载原理

符号表放在obj文件中一个独立的section.符号的加载随着.so的加载而加载。所以.so加载顺序就决定了符号表的加载顺序。而 .so 的加载顺序是按照链接的顺序,并根据依赖树,采取深度优先的机制来加载的。 并且如果前面已经加载了,后面就不会再加载了。 而module 列表会显示加载顺序。这个顺序与 solib-search-path 一般情况是不一样的。 这是由于加载是根据依赖树深度优先来的。

手动加载symbol
  1. info symbol

  2. 查看加载加了.so

    info share

  3. 构造路径

    set sysroot

  4. 加载symbol

    symbol-file filename

一旦符号表加载了可以查看符号表的内容

symbol command
Name Content
info line 查看符号与源码行的对应关系
info source/sources 查看源代码的信息
info symbols 查看符号表
info function 查看加所有函数

Note

这些都通过查看online help来得到更多的信息

例如遇到了中途遇到crash,但是此时没有debug 信息怎么办,这里可以要求重新加载一下 lib,重新进行一次解析就可以。 这时候就需要用到

symbol-reloading symbol-reload 当然自动加载的时候,也要注意库的名字,名字不一样的时候,也是找不到的。 这样时候ln 就可以来帮忙了。当然也可以直接改名换路径。当然如加载的lib不对时,会报linkzip error.

GNU GDB

debuger 是一个大工程,不仅检测CPU的状态,还要提供一个运行时环境,就像tclsh一样,可以实时运行情境。

digraph gdb {
    rankdir=LR;
    gdb -> {BP; CPU;Program;OS;target;server;Interface;ownSettings;stack;SourceCodeView;DataView};

   // break point
    BP -> {breakpoints;watchpoints;catchpoints;tracepoints};
     breakpoints [shape=record, label = "break | break function | break +/- offset | break linenum | break filename:linenum | break filename:function | break \*address |break if | tbreak|hbreak |thbreak | rbreak regex "];
     watchpoints [shape =record, label ="watch | watch expr | rwatch expr | awatch expr | info watchpoints "];
     tracepoints [shape=record, label = "{trace|tfind,tstart,tstop,tstatus,tdump,save-tracepoints|passcount | actions |collect data | while-stepping }"]
    Interface-> {HI;MI};
    //
    Program -> {Inputs;Outputs;Execution};
   Inputs [shape=record,label ="<f0> Inputs |<f1> args |<f2> corefile| <f3> attach "];
   Outputs [shape=record, label ="<f0>Outputs |<f1>  STDOUT |<f2> STDERR" ];
   Execution -> {Step,Continue;Next;Until;Jump;Thread};
   Thread [shape=record; label = "thread |   thread threadno | info threads | thread apply "];

   //stack
    stack->stackOps;
    stackOps [shape=record, label = "frame args |select-frame"];
   //SourceCodeView
   SourceCodeView -> viewOpts;
   viewOpts [shape=record,
             label="{list|set listsize |linenumer |function |*address} | \
                 {search regexp | forward-search|reverse-search} | \
                 {dir |directory show directories }| \
                 {file | symbol file | core-file, exec-file |add-symbol-file |add-shared-symbol-file | section } | \
                 {mapping linetoaddress |info line *address|disassemble  range | set disassemble-flavor }"
          ];
   //DataView;
   DataView  [shape=record,
             label= "{DataView  || \
                         p/xuf \*array@len  \l \
                         x (type) \*array@len \l}"
     ]

}
breakpoint

,不仅能够disable/enable以及one stop,还能设置回调函数,不仅可以使用gdb脚本还可以被调试对象函数,以及第三张通过环境变量shell=指定的脚本。是支持python的。

watchpoint
用完就会背删除,并且不能直接加断点,必须每一次用完之后要,要重新设置,pentak是否会保存,并且如果是软件实现的话,速度会非常的慢,并且在多线程里,如果是软件实现只对当前的线程有效。
catchpoint

gdb 提供对load,try,catch,throw等等支持,另一个更加直接方式那就是对用__raise_exception.加一个断点,类似于perl中把把DIE包装一下。

对于程序的执行控制,利用exception, singal 等等控制。

例如对不起trhow, catch,exec fork,load等等控制,都可以直接用catch 命令设置,而对于程序自身那就是raise() 来发启signal,可以用raise(),signal()结合起来实现一个状态机。http://www.csl.mtu.edu/cs4411.ck/www/NOTES/signal/raise.html

tracepoint

this is just a pm point of SDH. you monitor the system state at the tracepoint, you can collect the data. so you that %RED%how to use tracepoint to make write down execution log just bash set +x%ENDCOLOR% the core-file is implemented use this.I guess so. there are three target for GDB: process, corefile,and executable file. what is more, GDB could offer some simulator for most of the GDB.

next,step,until,contil,return,jump,fg,ignore

这些命令都有两种xxxi这种,是针对机器指令,也就是汇编指定的,另一种是针对源码的。并且后面都可以跟一个数值来实现循环。 进入了gdb后,你完全可以重起组织代码执行顺序,甚至把应用当做一个库,利用gdb脚本重新实现一遍应用程序,例如直接把attach上当前的进程,然后,加载自己的东西,因为gdb是支持写回功能的。这样就可以强hacking 的目的。

display automation display the info display /i $pc —print and x you can also control the scope and format of data. by <verbatim>set print XXX //static-memebers ,vtbl </verbatim> and meanwhile you can retrive the history value of the variable. by *.$. $ is special symbol. $$n refers to the nth value from the end.

In GDB there is convenience variable(prefix with $ $AAA,$BB) you use it during the whole GDB life. register you can also get the register value from =info registers= or = print/x $<registername>=

the strongest point is that GDB could manipulate the memory directly. <verbatim>mem address1 address2 attributes …</verbatim> there is also a cache for data.

BP set

when I can I set the BP. 在今天的测试中,断点能设置在哪,并且是否被击中,并且什么被解析了。例如在空白处是不能设的,编译形与解释型debug有区别吗,

working language and native language.

you do extension for gdb as native lanuage or working language. you control these by show/set language. info extensions. different language supported different type and range check.

GDB extension

gdb 支持自身命令的扩展,一种是通过<verbatim>define commandname</verbatim>. 另一种通过命令hook来实现。另外现在gdb 都支持 python来进行扩展 。并且gdb也是可以`http://docs.python.org/devguide/gdb.html <直接调试python>`_ .

..cas-table:

meta element , define commandname , define a new function ,
         ^ ,  if,while document,echo,printf,output ,
         ^ , help user-defined,show user ,
hook , hookpost-XXX , after ,
  ^  , hook-XXX ,  before ,
 command file   ,  source, .gdbinit <verbatim>gdb <cmds >log 2>&1</verbatim> ,查一下pentak这个是在什么时候调用的 ,

now, there is good example for define command, ndk/common/gdb/common.setup for art on.

pretty printer

GDB 是支持python,并且可以通过python来实现大量的定制化,例如正好的显示,当然也可以利用python 起动一个socket 然后当做一个server,来远程操作一些东东。当然今天先看python 对于显示的优化。 c-gdb-python-pretty-printing-tutorial gdb 如何直接执行python

python
import sys
print afa
end

通过学习 ndk 中ndk-gdb-python 来作为参考。 gdb 扩展可以参考`Extending GDB using Python <https://sourceware.org/gdb/onlinedocs/gdb/Python.html#Python>`_ visual-studio-debugger-related-attributes-cheat-sheet 这里讲了一些 debug的设置。

gdb 中使用 python 类似于 vim 中使用 python 一样的。

对于一些负责的数据内存数据结构,完成可且numpy + Image 等等方式来进行可视化。这个是最简单有效方式,加载一个解释器,能够访问进程的内存空间 然后对其可以做任何操作。

也可以利用gdb + python来做各种单元测试。

对于PentaK 与VSAuto 都会 visualize功能。基本用法那就是根据结构体类型如何显示其内容,例如只显示头,以及如何以树形展开,因为对于基本的基本的数据结构的组合。 浅谈autoexp.dat文件的配置 以及我们http://devtools.nvidia.com/fogbugz/default.asp?30959

VS2013 Visualizers

How to write Visualizer 分两部分 debugger,与debugee两部分。然后根据模板来显示。

VS 自身的模板在 C:Program Files (x86)Microsoft Visual Studio <version>Common7PackagesDebuggerautoexp.dat 里。

基本类型,整型,长整型,十六进制,以及浮点树,以及字符串。 这里分preview and stringView,children, 基本的数据结构有#array,#list,#tree, # 本身,以及特殊的自由变量。

$e,$c 是自由变量,m_pszData等等结构体自身变量。

这个类似于python中pytable的功能,可以直接table值。

type=[text]<member[,format]>....

http://www.xuebuyuan.com/1300115.html 这是一个不错的教程 http://blogs.msdn.com/b/joshpoley/archive/2008/01/24/custom-debugger-auto-expansion-tips.aspx http://www.manicai.net/comp/debugging/visualizer/

GUI

gdb 两种方式支持GUI就像VS那样,一种是自带的TUI接口,另一种那就是利用Emacs做为界面。

while 循环的汇编实现

汇编的时候是直接跳到第一内部第一行执行的。dissembly window 提供行号,源代码等等东西,可以很方便的找出其翻译的对应关系。 调试信息表都有哪些信息,为什么没有源码,调试就跟不进去,能否调试Java虚拟机的原语操作呢。

反编译

反向工程向来是个大课题,把C语言翻译成汇编,并反过来,就一定成立,因为语言之间不是一一切对应的关系。所以可读性会非常差。但是也是可以参考的。` 反汇编 <http://baike.baidu.com/view/637356.htm>`_ IDA pro 5.2 反汇编代码转C语言插件

core dump 调试

  1. 开启core 文件的生成 ulimit -c unlimited
  2. gdb 分析core文件 gdb debugme core.xyz
  3. 动态生成core, gcore pid.
  4. 动态生成strace strace -p pid .

#. 调试正在运行的程序 gdb debuggee pid. http://linux.maruhn.com/sec/glibc-debug.html

利用信用号来进行调试

http://www.ibm.com/developerworks/cn/linux/l-sigdebug.html. 在代码里自己给发一个停下来的信号就行了,然后gdb在attach 上来就行了。

See also

  • jdb IBM web %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  • VS 调试技巧 VS 的immediately Window 就像tcl那个调试器的功能,也就是给你一个运行时环境,就像脚本语言的解释器一样。可以直接调用你的所有函数。MSDN 参考命令
  • 符号表 %IF{” ‘二进制可执行文件结构’ = ‘’ ” then=”” else=”- “}%二进制可执行文件结构
  • MSdebug %IF{” ‘NV debug wiki’ = ‘’ ” then=”” else=”- “}%NV debug wiki
  • core file for debug %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  • sparc-stub.c %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  • Extending gdb %IF{” ‘you can use python ,gdb cmd, alias to shell programming.’ = ‘’ ” then=”” else=”- “}%you can use python ,gdb cmd, alias to shell programming.
  • Visualgdb %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
  • GDB学习总结–实现原理 , Linux信号列表 gdb 是利用SIGTRAP信号来实现的。至于SIGTRAP是用硬件还是软件这个要看内核了。
  • gdb server manual %IF{” ‘gdb server 也是可以直接加载应用程序,而不是只能attach,只是pentaK 对于APK采用这种方式’ = ‘’ ” then=”” else=”- “}%gdb server 也是可以直接加载应用程序,而不是只能attach,只是pentaK 对于APK采用这种方式
  • gdb 如何调试多进程 %IF{” ‘一个方法,gdb wrapper. 一旦设置的断点,就会引用SIGTRAP信号。’ = ‘’ ” then=”” else=”- “}%一个方法,gdb wrapper. 一旦设置的断点,就会引用SIGTRAP信号。
  • Miscellaneous GDB/MI Commands %IF{” ‘’ = ‘’ ” then=”” else=”- “}%
Thinking

远程调试 远端与近端要配套才行,有两种情况,一种是远端可以执行文件本身含有调试信息的,第二种那就是远端没有调试信息,而是需要本地提供的,加载各种调试信息以及原码,只是依赖远端的进程与本地拥有相同地址,通过地址对应来实现调试。当然你可以自己实现一个gdbserver,并且gdb已经预留了接口与模板,remote.c 并且在attach的过程,gdbserver 会先向进程发一个暂停信号,然后连接上去。这些是根据进程与内核的之间的调度来实现的。A minimal GDB stub for embedded remote debugging. ,`GDBstub的剖析与改进 <http://www.mcu123.com/news/Article/ARMsource/ARM/200705/4297.html>`_ ,并且gdb源码为库中还提供了大量的模板与例子。对于常见一些CPU架构的支持。 例如android 的调试 use Project Symbol 参数一样。你要选择:

"/system/bin/app_process", "/system/lib/", "/system/bin/linker            C:\Users\vili\AppData\Local\Temp\Android  并且按照设备号来存放的。
为什么要linker   这个linker是做什么用,如果不需要本地的话,就只需要app_process与linker.

gdbserver + unix_debug_socket --attach 123

Debugging an already-running process –attach function need system support. there is an process concept. how about the bare board target.

其实也很简单, –tty是可以直接指tty的。 – Main.GangweiLi - 05 Feb 2013

数据一致性 特别是在troubleshot的时候,尤其要注意这个问题,例如你改的文件,没有保存,保存了没有重新编译,编译了没有重新deploy,以及远程调试两边的版本不一致。都会感觉到莫名其妙。怎么看都对,就是结果不对。

– Main.GangweiLi - 05 Feb 2013

多线程调试 step by step时,能不能跨线程或者手工进行线程切换 是根据CPU的架构以及 scheduler-locking 来决定的,在gdb中是可以设置的,set scheduler-locking mode。线程内部的调用关系,都要很方便的显示出来。多进程调试有同样的问题。可以查看每一个线程的状态,并且可以进入每一个进程。 All-Stop-Mode

– Main.GangweiLi - 07 Feb 2013

quickly debug call stack and filter BP. One more is diff with the baseline. the first get workable path, and then look at the difference between each other.

– Main.GangweiLi - 08 Mar 2013

execution control you execute an command just like tclsh. should be able to jump at the source code for example skip some step. The arguments to your program can be specified by the arguments of the run command, They are passed to a shell, which expands wildcard characters and performed redirection of I/O, and then to your program, Your shell environment variable specifies what shell GDB uses.

the environment of software : working directory. lib search path, stdio.

– Main.GangweiLi - 14 Mar 2013

  1. automation gdb sessions
#!/bin/bash
echo "run -c test.conf" > test.gdb
echo "bt" >> test.gdb
echo "bt full" >> test.gdb
echo "info thread" >> test.gdb
echo "thread apply all backtrace full" >> test.gdb
until gdb ./core -x test.gdb --batch >test.log 2>test.err
do date && echo "test server died with exit code $?. Restarting..."
grep -B 10 -A 1800 "SIGSEGV" "test.log" > "testtrace.log"
cat "testtrace.log" | ./paster | grep "http" >> "test.link"
cat "test.err" > "testerror.log"
sleep 31;
done;

shell interpretor You can regard the gdb as shell interpretor, the software you prime command you can use you shell language. the gdb shell include two: target language that you the language you debug. the scripts language, gdb support by it self. you can use both. Once the program you load, you can use all of this function. and you source the other scripts. GDB-Python-API , Extending-GDB there is .gdbinit file. and during the execution, you can source the scripts file. all the gdb cmd you can use it. and the input and output is every regular, you use the annotationlevel and machine Interface to do the automation.

– Main.GangweiLi - 24 Mar 2013

you can just load the nostripped binary code. it just load it, not run it. and -g also include sourcecode in the binary execution file? when debugging, do we need the sourcecode, normally, we didn’t need the sourcecode. and meanwhile, it means that -g binary and .so lib has the sourcecode information. how can we get the sourcecode from debug version binary.

– Main.GangweiLi - 02 Apr 2013

How to hit boot code normally, it execute quickly pass the stage. how to make this, one way is that you add a dead loop for exmaple int i=1;while(i). so when you hit it. and then change it i=0, and continue the execution. for the debugger, you can change the value at local window. 自己包引导程序等待一个信号来起动eglretrace,这样就可以给我足够的时候来–attach上去,当然引导程序,如果通用shell来直接来做就会更加方便。perl应该就可以,但是android只有简单的sh,如果可以这样最好,还有一个办法,直接–attach到程序的加载器上,然后可以控制后面的加载函数。

– Main.GangweiLi - 10 Apr 2013

info locals window how to implement it. is it using this command?

– Main.GangweiLi - 15 Apr 2013

如何例出所有函数 如何查询代码,所有函数名呢。不只是当前的文件。these operation is regard about symbol table. you set -n read symbol all at the inital. then you can do query the symbol(function name, varible name, CPU struction, address, any label). by these command | info address symbol | Describle where the data for symbol is stored | | info symbol add | print the name of a symbol which is stored at the addresss addr| | whatis expr | print the data type of expression expr | | whatis | | ptype typename | print a description od data type typename | | ptype expr | | ptype | | info types regexp | | info scope addr | | info source | Show the name of current source file | | info functions [regexp ] | print the names and data types of all functions | | info variable [regexp ] | print the names and data types of all vrables |

the other hand, GDB offer another way to manipulate the symbol file just like (operation on section). you load it into gdb and query and modify it and save it.

– Main.GangweiLi - 16 Apr 2013

GDB的命令行编辑习惯 你可以用VI-style, emacs-style, csh-like. it use readline lib to implement it. and readline lib support vi-style and emacs-style 以及history 功能。并且这个history 支持正则查找替换。 <verbatim> set editing on/off show editing set history filename/size/save set debug arch/event/expression/overload/remote/target/varojb/screen/versbose/complaints/confirm

</verbatim>

– Main.GangweiLi - 16 Apr 2013

GDB machineInterface this one is just like tl1. there is two mode. human readable/raw. and the telnet has two mode too. at the early age, gdb annotation to change this mode and emacs use it.

– Main.GangweiLi - 17 Apr 2013

JUST IN TIME DEBUGGER http://msdn.microsoft.com/en-us/library/5hs4b7a6.aspx 如何使用,并且今天看了,VS调试壳,是否可以利用vim或者emacas也来招调试器。

– Main.GangweiLi - 06 Jun 2013

gdb就可以实现debug,看见汇编之间的关系吗?

– Main.GegeZhang - 25 Jun 2013

什么是声明变量

– Main.GegeZhang - 25 Jun 2013

*arm exidx unwinding *

– Main.GangweiLi - 22 Jul 2013

– Main.GangweiLi - 30 Jul 2013

对于指针内容的显示 在我们使用指针时,常用的变量的类型就没有办法显示其内容了,使用指针,你可以任意组装任意的东西。但是如何查看了,就时候用到了,gdb 查看内存的方式了,p/xuf 等等。例如在native_globe里,生成那些顶点数据时都是使用的指针。如何查看这些值呢。使用immediateWindows现在是支持不了,直接连到GDB上发送一些命令。

– Main.GangweiLi - 29 Aug 2013

Debugging Infomation In Seperate Files

https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

可以通过同名文件 xxx.debug或者build-id 进行同步,如果使用前者还会有一个 CRC的校验和。

同样可以用

objcopy --only-keep-debug foo foo.debug; strip -g foo 就可以得到 debug info table file.

Ptrace

gdb 主要原理就是动态修改的进程的所有状态与内容,还有寄存器的能力。例如修改返回寄存器的值,就可以改其反回值了。

#include <sys/ptrace.h>
Long ptrace(enum_ptrace_request request,pid_t pid, void *addr, void *data)
/**/

request 是具体的操作。

整个过程就是追踪者先通过PTRACE_ATTACH与被追踪进程建立关系,或者说attach到被追踪进程。 然后,就可以通过各种PEEK和POKE操作来读/写进程的代码段,数据段,或各寄存器,每一次4个字节 通过data 域传递,由addr 指明地址,或可全用PTRACE_SINGLESTEP,PTRACE_KILL,PTRACE_SYSCALL各 PTRACE_CONT等操作来控制被追踪进程的运行,最后通过 PTRACE_DETACH与被追踪进程脱离关系。

但是当多线程的时候,有可能PTRACE_CONT有可能会失败。http://stackoverflow.com/questions/16360366/ptraceptrace-cont-cannot-resume-just-attached-processes

脚本扩展

简单可以用gdb的本的shell来做,while,for,if也都是支持的。复杂的可以用python来做。就像vim一样。 https://sourceware.org/gdb/onlinedocs/gdb/Python-Commands.html#Python-Commands

如何用gdb来收集数据

tracepoint一个一个加太麻烦,有什么更快的一点方法,那就用gdb来做,最灵活。 具体某几个点可以用直接用tracepoint来做。 大面积可以用event,以及signal来做。 http://stackoverflow.com/questions/2281739/automatically-adding-enter-exit-function-logs-to-a-project

对于gdb.event可以python来做https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html gdb.events.inferior_call_pre/post 事件。

对于SIGNAL直接用`handle SIGUSER` 来实现https://sourceware.org/gdb/onlinedocs/gdb/Events-In-Python.html

一些其他的事件,http://visualgdb.com/gdbreference/commands/set_stop-on-solib-events http://stackoverflow.com/questions/7481091/in-gdb-how-do-i-execute-a-command-automatically-when-program-stops-like-displ

https://sourceware.org/gdb/current/onlinedocs/gdb/Hooks.html#Hooks

最终看代码实现 https://sourceware.org/gdb/current/onlinedocs/gdb/Hooks.html#Hooks

define hook-stop

如果只是看stack,有这样的工具https://github.com/yoshinorim/quickstack http://poormansprofiler.org/ http://readwrite.com/2010/11/01/using-gdb-as-a-poor-mans-profi/ https://github.com/Muon/gdbprof

用gdb来进行测试

起真实的进程是最好的环境。如果能起app然后在这个context里,利用gdb来直接执行。 http://stackoverflow.com/questions/16734783/in-gdb-i-can-call-some-class-functions-but-others-cannot-be-resolved-why

但是一些编译复杂的结构,gdb是没有办法直接编译的,这个时候就需要JIT来帮忙了。

另外把测试的函数单独放在一个dll中,然后 dlopen来加载。 http://stackoverflow.com/questions/2604715/add-functions-in-gdb-at-runtime

即使没有,也可以临时写一个,只要编译的时候加上一个 -fPIC 就可以了。

先生成一个coredump,然后再coredump中来进行各种各样的测试。

在crash直接调用gdb. http://stackoverflow.com/questions/22509088/is-it-possible-to-attach-gdb-to-a-crashed-process-a-k-a-just-in-time-debuggin

尽可能不要在头文件中下断点,这样可能造成n多断点,在n多地方。 是由于断点寻找机制造成。

shared object 在加载之前,是加不上断点的。 这也就是为什么我们hack lib-loaded event的原因。

ProfilingAndAnalysis

实现原理

要么是静态的分析各种资源的分析,或者硬件本身支持。 软件库的实现APIC的功能,主要是inject来实现的。

当硬件资源有限的情况下,采用multi-passes 来实现,多跑几遍,每一次只测量一段,尽量减少对被测对象的干扰与破坏。 同时inject可以LD_PRELOAD 这种一次ALL_IN的模式,也可以自定义实现dll_export 功能来定制某一些API。 最有可能破坏,malloc函数。 操作的对象就是ABI的 ELF的符号解析与跳转这一块, 经常会出现的问题,随意injection会破坏内存对齐从而导致程序crash.

Optimization 的过程,本身就可以是寻径的过程,最终变成汇编语言之后,每条汇编指定有一个定cost,把哪有cost的分类到一个group,然后每一个group生成group 的cost函数,这样就可得到每下代码块的cost了,同时也就生成一个cost函数,这样基于callgraph图来生成路径。

  • 传统的 tree只能看到单一调用关系,例如多个函数调用同一个基层的函数是看不出来的。 利用callgraph,就可以清楚看到哪一个节点,最调用最多,并且是从哪些路径来的。
  • 并且现在DL的代码生成也都是直接计算图,中得出来的。完全是可以计算图。

profiling的本质找到所有stakeholder,并且找到这个系统的最大平衡点。 所谓profiling,也就是各个资源的利用率。 pipeline以及算法本身都是约束条件。那些PMUcounter能统计那就是离开始点的,绝对时间例如ns值,另一种那就是CPU cyclyes的数。 例如然后那就是自身的counter,指令数,以及读写的数等等,并且他们的状态以及各种统计值,total,max,min,avg,std. 当硬件PMU不够的时候,就采用多次pass的方式来分次分段采样。

每一层,只要不是只有一个个体,存在完备的个体,做同样的事情应该不会只有一条,但是哪一条效率最高呢,只要是一个集体,就会排兵部署的要求。

每个体的各种属性,最终也会变成在上一个维度时,的一个属性以及cost, 在计算各种path cost,就是这样来的。

优化最后结果,那就是功耗比。 具体的上层算法本身的流程要求,还有框架本身的pipline,以及硬件各种资源的occupancy.等等。

工作的流程

digraph G {
   rankdir=LR;

   "Moduling"->"Generate_Code"->"Trace/Profiling"->"Test/Verify";
}
  1. Moduling, 建模,根据具体问题建模,形成算法,并且相应算法复杂度分析,也就是所谓的常,对,线,平,立,指。形成算法多项式。

  2. 生成代码,现有各种代码生成工具,把各种计算模型直接生成代码。

  3. 优化的级别

    • 直接调用更好,优化的库,工作量最小就是换一个API。

    • 利用 openacc 来标记,让编译来优化。

    • 代码级优化 例如LLVM,

    • 硬件指令级优化

      因为现在硬件结构都是流水线的,pipline,而分支就是pipline的杀手,同时如何使硬件的资源的load balance,这是编译器可以提高与优化的地方。 branchless program

      _images/LLVM-Passes-only.png _images/architecture.png
为什么优化

程序慢 ,首先要看系统的资源使用的如何,如果系统还是比较空,那就要改程序来充分利用系统资源,系统资源已经很紧张,那就优化程序本身

程序占的资源太多 ,首先要保证速度的情况下,优化程序内部结构。

但是如何量化这些标准呢,而些标准是来源于哪呢,是源于应用本身的要求。对于游戏来说,其中一条那就是framerate,最低要达到24frame来达到动画效果,不过一般情况下都要求做到60fps, 对于3D的显示可能要求更高的fps,例如通过切换左右眼的影像来得到3D效果,这个就需要120fps。

其实这些就是所谓的用户体验的一部分吧。最终反应终端用户面前的,一个是流程本身的合理性,另一部分则操作的流畅性。而所有的这些都是技术指标的。例如视频本身fps.不同的应用会有不同的需求。对于破解会对破解速度有要求。对于仿真对于仿真的精度有要求。对于计算机的不可靠性很大一部分就是指的其精度的问题。这个对于大型科学计算尤为重要。

  1. Reduce IT spend, find and eliminate waste,find areas to tune, and do more with less.
  2. Build scalable architectures - understand system limits and develop around them
  3. Solve issues -locate bottlenecks and latency outliers
对于产品的需求一般是两部分:
  1. 功能性需求 来解决特定的问题,对于我们来说,大部分时间是在解决这种问题。特别是自己平时的写的大部分脚本。对于产品的开发就要做到人无我有,就是指功能。人有我精指的是性能。
  2. 性能要求 对于种技术指标的要求。

同时大家所谓的80/20原则。也就是20%时间就解决了80%功能。80%的时间用在解决那20%的性能。

如果对于性能要求不高的,你的solution的选择就会很多,会有各种各样的库可以供你用。但是考虑到性能化那就未必了。那就是为什么相同的功能为什么会有这么多库。并且基本上大的项目,很多东东都是自己实现的,而非用一些语言本身的实现或者库的实现。最明显的例子,就是那些队列,以及reference count之类的东东都基本上都上是自己实现的。根据性能要求,来做不同编译,例如满足精度的情况下,尽可能用硬件浮点计算。或者换用不同库会有质的变化。

优化的前提 是保证正确性,在编译器的一些激进的优化,可能会出错,同时采用近似的计算。

优化的目标

  1. 算法本身的优化,减少计算量
  2. 指令优化,减少指令条数
  3. 删除掉不必要的负载,看看是不是加载不了不必要的库,以及是不是有更优化的库可以用。

硬件的优化

尽可能大的利用硬件资源。 改变读取pattern提高cache的命中率。 可以把整数部分换成浮点数, 提高系统的利用率

终级优化人工优化汇编指令

这个是最难的,对于每一个具体问题,都会有一个很多很好的solution.但是所有问题放在一块,就不见得有了。所以要在保证效率的情况下来提高效用性.

因为汇编指令是一个完备集,所以对于指令的统计状态,就是当前状态反映,例如每一类指令执行数量与频率就体现相应的资源利用率。现在终于明白了 CUDA Analaysis 对于每一类的指令分析的用途了

如何使用timeline

要使用 timeline 首先要能读懂timeline. timeline是一个立体的图形,x轴代表时间线。而y轴代表并行的资源。它反映的在某一时刻,各种并行资源都在做什么。例如这个时间CPU在做什么,GPU在做什么。并在每一条横轴都有会数据,显示各个资源自己的参数。实际采集实际最少三维的数据。时间轴,并行轴,每一个并行轴的资源的每一个参数。 使用timeline可以时间轴来看某个时间点,系统都发生了什么事情。当然这个只是从时间关系上。当然还有一些依赖关系是不能直接时间分析来得到,但是可以从那里得到一些线索。

从timeline中能读出什么呢:

  1. 系统资源的调度效率,速度很慢,并且系统各种资源的使用率也不高。这个说明资源调度效率不同。

    • CPU Core Utilization Avg(black) & Max(gray)
    • Thread 状态
      • 使用率
      • CPU 的core 占有率
      • Thread State (running,blocked) 通过这些看到这个线程在做什么,为什么会blocked.
      • Event Trace (various APIs cuda/opengl/nvtx)
  2. 可以看到并行与串行的真实分配情况。并且计算 Amdahl’s law 优化最终效果,是取于不能优化的部分所占的比重,求极值,可以知道极限在哪里。

    • 那些时间片中大量的空间,就是要需要优化的地方
    • 能时通过下方的函数统计,就知道,这个时间点,哪些函数在执行并且在做什么。
    • 并且在这个时间点,哪些函数是blocking issue
  3. timeline的时间轴就是一段段的时间片,其最小单位也就是一个pixel代表多少时间片的问题。在timeline上会标出这个时间片里某种精源利用率。

    quadD gravy->maxium heavy->middle vaule  
    NSight    
    android systrace    
  4. 从timeline上看trace.

    • 直接看代码太多的细节,不利于快速掌握整个的workflow, 如果添加了nvtx之类的标记的话,可以直接用。 如果没有可以直接根据timeline上的时间片来观察,在sample freq足够高的情况下,如果timeline上没有空隙,那就说明现在CPU正在集中一件事,一个大的函数。 没有发生的大的线程切换。
    • 从线程数中,可以看是否启用了多线程,如果用了,一般都会是生产消费者模型或者fork-join. 得到大体流程。
    • 先找到主线程,然后根据其timeline上的大的时间片,来查看其相对应的callstack,来得到其执行的trace. 而不要再debug来看其执行了。
    • 再每一个线程中,看其执行的函数在timeline上有重复,就说明这里是一个循环。
    • 如果工程太大,或者复杂,可以选择添加在代码中添加nvtx来实现,进一步合理颗粒度的注释。
    • 调用关系可以从下callstack来搜索。某一个函数的调用。
如何优化

各种书上都讲了各种方法与规则但是如何动手呢,NSight Analysis就提供这样一套分析工具。并且是从上到下从粗到细的,并且从定性到定量的。

例如GPU的thread如何分配呢,主要靠分析occupancy.

follow the CUDA_Best_Practice.pdf and CUDA_Profiling_Guide.pdf这两个就够了。

看了那这么多,至少从前下一层来看。所谓的优化,也是资源的使用率是不是符合预期。 而基本现在系统都是分层模块的设计。 由于计算机的透明性,每一层都是系统资源的一种抽象,要想知道当前使用的是否合理,至少下一层去看了。 这也就是为各级probe了。 微内核模式的,每一级都可以是指令集,而非微内核的模式。一般是函数为边界。 想到函数更小一级,那就是利用nvtx之类东东,或者到指令集的,直接用模拟器或者PMU硬件来得到。 这也是各种profiler工具存在的原因。

当然为了减少overhead,人们是想进各种办法,使用独立的硬件。 独立的线程。 减少context切换,例如内核态与用户态的切换。 当然使用JIT动态插入断点的办法来提高灵活性。

要想底层的支持

  1. kernel本身支持, 查看 /proc/config.gz 查看其编译选项是否打开,如果没有打开,是不是可以通过补丁来解决,或换一个更新的kernel.
  2. 对应的debug info是否有,其原理也是添加断点hook来实现的。
  3. module的build 环境要有, probe的实现原理,也是当写一个.ko 插入内核,只不过内核补丁自己提供一些函数,例如systemtap,你的probe可以调用这些内部函数。 例如打印pid,tid,callstack等等。例如systemtap会提供一个DSL,然后JIT编译直接使用。断点的插入在register module中实现,所以当你insmod时,就插入了。
  4. 对应kernel的工具的perf 工具,例如linux-tools-$(uname -r) . 没有的话,就得自己下载 kernel编译的环境,工具本身的source code来进行编译了。
  5. DUT本身的source code, 方便hack使用。
  6. 对应平台的 debugger 方便出现问题的时候,快速troubleshot.
  7. API层的probe,就是通过inject实现,实际上在动态链接的inject lib api来实现。利用LD_PRELOAD来实现。

如何手工uprobe, 能有现成的binary,版本都能对应上直接用,没有的话,就要从源码来了,先看内核 +硬件支持不。然后再按照流程来进行就可以了。可以独立编译或者在 源码树中进行编译。

如果各个性定制

如果Nsight Analysis提供的那些方法还不行,还有办法,那就是定制化。如GPU有profilingAPI的,例如最简单的`CuProfilingStart(),CuProfilingStop()`控制。当然你还可以取得另一些数据控制。让应用程序自身实现终身的优化。

collection of Data
  1. function entry/exit
  2. process and thread create/destroy/stateChange
  3. Context Switches
  4. DLL load and unloaded
How to sampling

this system should support approach to make Snapshot quickly, may this is supported by dedicated hardware. for example, snapshot all the registe and state to somewhare, how to use the info is descided by user how to analysiz these. the sampling frequency is resticted by two things:

  1. offline analyise , it is simple, only the speed of snapshot and store the info.
  2. RT analysis, in addition to the the above restrict, the analysis speed is also a restrict.

直接在CUDA中直接加入 PTX代码 这样可以解决编译转化效率的问题,这个结论从何而来,可以利用CUDA的analysis的 kernel/source 来这样,可以看到每条指令使用的次数,与每行原码执行时间。如何使用PTX 可以见帮助文档,Inline_PTX_Assembly.pdf与ptx_isa_4.0.pdf

– Main.GangweiLi - 18 May 2014

存取加速 缓存,寄存器,显存,内存,外部存储设置等。为了达到更块的速度,从两方面入手:
1. 用更块的硬件,例如 GPUDirect 直接与第三方设备读取DMA方式,而不需要CPU与经过内统的内存。另外那就是NVlink来加速CPU的通信的方式。主要来解决机器内部传输带宽的问题,原来的PCIE总线速度太低。当然也要driver库都有对应的支持才行。 1. 通过算法,并行提高register与__share__显存的利用率,以及常量的cache的利用率。

但是如何来衡量这些呢,那就是Nsight的analysis.

一个做的流程那就是APOD,道理也很简单,

Stage Tool Target
Asset Timeline;gprof wark with Amdahl’s law
P OPENACC;OPENMP;Thrust; Parallel.  
O traceing;profiling;  
D JIT make use of new hardware.

现在对于整个流程有所了解了,知道各个技术都在往里用,是要自己新开发应用,还是加速的应用程序。一些简单的做法,对于常用数学库直接换用GPU的函数库,对于一些数学计算也直接算用GPU的函数库,对于loop,sort,scan,reduce等可以通过Thrusts模板来实现。

当然你要改动就会有风险,那么所以要采用敏捷的方式来加入测试来验证这些。

latency VS Occupancy

这两个是矛盾,latency越小越好,那当在占的资源多,执行时间短,Occupancy那就是尽可能并行,系统的总的资源是有限,并行度越大,每一个可以分到资源也就越小,那么执行间可能就会越长。在CUDA里资源,那就是寄存器,share memory. Occupancy研究的是使用率,相当于CPU的的使用率,如何CPU使用率100%的问题。这个是在解决GPU资源大于所需的要求,如何原来结果更快。Occupancy高,意味着更多的线程在干活,首先要解决理论occupnacy,然后是实际的值,极限值,GPU的最大值。只有有足够多并行,才能隐藏latency的问题。一个线程不执行完,就不会被释放,并且最小调度单元是warp,也就是只有一个thread在占着,那么整warp就不能被再调度了。

解决方法有三个:

  1. execution configuration.
  2. launch bounds 用来帮助NVCC来分配寄存器。
  3. Pipe Utilization 解决那种长尾问题。就像火车站买票一样,半个小时内票,再好看单独一队,而是在长对后面等。
如何充分利用缓存

如何缓存也就是要提高hit率,首先要理解缓存工作原理:缓存采取就近原理。离自己最近也是可能用到。这样的话,就只有一个原则那就是尽可能的减少跳转。如何能做到这一点呢。尽可能有序这个不管是对 data还是instruction都是有效的。 [optimizing-for-the-instruction-cache] 例如 A->A->A->A->A->B->B->B->C->C->C-> 执行起来会比 A->B->C->A->C->B->A->C 更有效。因为前者大大提高了cache hit的效率。

[optimizing-for-the-instruction-cache]http://www.altdev.co/2011/08/22/optimizing-for-the-instruction-cache/
编译本身优化

链接不同的库,使用不同编译选项都会改变程序的性能。特别是浮点数等等,应用性强的功能。都会特殊的优化。每一个平台都会自己特定的优势,你是否利用了其独特的优势。首先要是知道这个平台的特性有哪些。然后去查看利用了没有。有的时候,什么都不需要做,就只需要改变一下编译选项就解决问题了。

一般source code 都会有各种宏,来控制代码的生成,例如opencl,还是cuda,以及是什么GPU,都是可以配置生成对应的代码来得到优化。

对于一些策略的优化

对于大的工程来说,每一次编译都需要挺长的时间,并且并不是很一个工程可以定制化做的很好。这个时候怎么办呢。那就是gdb中的或者ptrace来做性能测试。同时来修改程序的各个参数来生成对应的report.再加上gdb加上python的扩展,就可以相当shell可以重复使用。

The command is “readelf -A libapp.so”. With hardware fp, you will be able to see ” Tag_ABI_VFP_args” section.

Usign armeabi-v7a, you will by default get those compiler settings ” -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -mthumb”, if I am not wrong. Both “softfp” and “hard” (for mfloat-abi) are using hardware floating, here is a link for your reference: https://wiki.debian.org/ArmHardFloatPort/VfpComparison#FPU_selection

Please request them to change APP_ABI to armeabi-v7a.

t430:~/work/jiuyin$ readelf -A libapp.so Attribute Section: aeabi File Attributes
   Tag_CPU_name: "5TE"
   Tag_CPU_arch: v5TE
   Tag_ARM_ISA_use: Yes
   Tag_THUMB_ISA_use: Thumb-1
   Tag_FP_arch: VFPv2
   Tag_ABI_PCS_wchar_t: 4
   Tag_ABI_FP_denormal: Needed
   Tag_ABI_FP_exceptions: Needed
   Tag_ABI_FP_number_model: IEEE 754
   Tag_ABI_align_needed: 8-byte
   Tag_ABI_enum_size: int
   Tag_ABI_optimization_goals: Aggressive Speed xuan@xuan-t430:~/work/jiuyin$ readelf -A lib
libapp.so     libfmodex.so
t430:~/work/jiuyin$ readelf -A libfmodex.so Attribute Section: aeabi File Attributes
   Tag_CPU_name: "5TE"
   Tag_CPU_arch: v5TE
   Tag_ARM_ISA_use: Yes
   Tag_THUMB_ISA_use: Thumb-1
   Tag_FP_arch: VFPv1
   Tag_ABI_PCS_wchar_t: 4
   Tag_ABI_FP_denormal: Needed
   Tag_ABI_FP_exceptions: Needed
   Tag_ABI_FP_number_model: IEEE 754
   Tag_ABI_align_needed: 8-byte
   Tag_ABI_align_preserved: 8-byte, except leaf SP
   Tag_ABI_enum_size: int
   Tag_ABI_optimization_goals: Aggressive Speed xuan@xuan-t430:~/work/jiuyin$

动态的得到callstack

sudo gdb -ex "set pagination 0" -ex "thread apply all bt" --batch --pid `pidof python`

https://github.com/springmeyer/profiling-guide

各种profiling的核心在于数据格式交换,后期可以采用数据可视化的工具来做各种显示。 perf data format

运行框架

instruction, counter, trace. 不同级别的profiling方法,在性能与灵活性是不一样的。把这些数据收集上来了,后面就分析了。 counter可是硬件,也可以是软件的。

分层优化

每一层的优化

  1. Number of memory allocation
  2. Number of system calls
  3. Concurrency model

optimization should focus on the critical path. optimize where it makes difference. Optimizing pieces of code that are not on the critical path is wasted effort.

不同的操作对于scale关系是不一样的。并不是一个简单线性关系。 所以在优化的时候,不要假定只有一个最佳方案。 是要根据约束来进行解决方程的。 所以profiling一定弄清楚配置情况来进行。 例如copy 文件的大小,对性能影响也是不一样的。 copy方便,还是传指针方便,最终体现是指令数,例如小于2个字节。传个指针也得四个四字节吧。

同步的方法

信号,或者atomic CPU operations.

profiling也是分层模块化

http://www.brendangregg.com/linuxperf.html 看其图。 硬件层,一般都会对应PMUdriver与之对应。 例如CPU cycles, instructions retired, memory stall cycles, level2 cache missing. #. 找到bottleneck可以考虑是替换算法,以及数据结构 #. 减少overhead. 例如函数调用的。 #. 从callstack看到函数后,要从框架上看,从哪里动手最合理。 而不是简单只要找到最大的函数直接优化本身。有可能这个函数使用最大是由于上一层算法的调用问题。 #. 找到真正的原因。从flat模式可以看哪一个函数用的最多。 TOP-Bottom,有利于分解,bottom-top快速看到最大值,并且都是调用的。 #. 找到最大值,一般有两种方法: 换一个更好的算法与数据结构,或者重写surrouding program 把这个函数给扔掉。 具取于为什么它这么大。

生成callgraph
perf record -g ./cmatrix
perf report --stdio

OSkernel层

tracepint
  1. system calls,TCP events, file system I/O, disk I/O,
  2. Dynamic Tracing kprobes and uprobes.
  3. Timed Profiling. with CPU usage.
  4. process create/statechange/terminal.
  5. thread create/statechange/terminal
  6. IO event
  7. resource allocation

可以使用systemtrap生成kprobe hooks,只是在需要检查的指令的第一个字节中插入一个断点指令,当调用该指令时,将执行对探针的特定处理函数,执行完成之后接着执行 原始的指令(从断点开始)。就是一个中断的过程。 http://blog.csdn.net/wudongxu/article/details/6345481 先用脚本生成C语言,然后再编译插入 ko. https://www.ibm.com/developerworks/cn/linux/l-systemtap/

https://wiki.ubuntu.com/Kernel/Systemtap

software

  1. event message

可视化工具

CPU flame graph,http://www.brendangregg.com/flamegraphs.html heat map, timeline.

从哪里寻找工具

  1. Who is causing the load? PID,UID,IP addr,…
  2. Why is the load called? code path
  3. What is the load? IOPS,tput,direction,type
  4. How is the load changing over time?
  5. Best performance wins are from eliminating unnecessary work

tuning

通过/sys/kernel/…来调整配置。

优化的过程就是资源重新分配的过程。也就是对数据结构重构的过程。 对于大数据结构的实现,都是基于array,list,hash,tree. 以及对应的操作。修改代码也改这些并与之相应的操作,来应对算法输入的scale要求。profiling的理论基础是计算复杂度理论。

  1. Collect common subexpressions. 尽可能让计算只做一次,但到底是用时间换空间,还是空间换时间。

    sqrt(dx*dx + dy*dy) +((sqrt(dx*dx + dy*dy)>0)....)
    

    像这种采用一次的计算,还是多次,取决于指令的速度。 变量赋值意味着大量的move操作,一个是move本身的速度,还有move的位置,不同存储bandwith也是不一样的。

  2. Replace expensive operations by cheap ones. 这个计算的机指令周期了。 不同硬件对不指令周期也都是不同。 例如精度要求不高,可以用单精度指令来计算。 除法,尽可能用移位来计算来进行近似计算。

  3. Unroll or elminate loops, 这样可以大大减少overhead. 但是这样加大代码的长度。或者降低loop的次数与层数。

  4. 提高cache 的命中率,通过局部化算法来提高。

  5. 根据操作overhead,要考虑是批处理或者二分级处理。 例如内存分配,一次分配个大大。自己来做二次分配。因为默认内存管理方式可能对你应用程序来说可能并不高效。

  6. 批处理的作法,那是用buffer input and output.所以在printf的时候,在不需要 n的时候,没有必要习惯性的加。

  7. Handle special sperately. 没必要完全大一统。 特殊的地方,特殊处理。 就像内存分配方法,可以同时支持几种不同分配方法。例如动态array的增长方法,没有都采用一个方法。 用参数来指定不同的算法。

  8. Precompute result. 算算数据依赖需不需要动态,不需要的话,完成可以提前计算,然后查表。但是特别容易算的,也就没有必要存了,因为读取也是要时间的。

  9. Rewrite in a lower language. 在分层实现的时候可以用。机器生成代码的效率与人优化的代码指令精减度是不一样的。

  10. 对于空间效率来说,尽量采用少的数据类型,这也是为什么arm中他们经常使用thunder指令集的一个原因。空间效率也是有代价的,例如要在内存解压。也是要时间的。

  11. 提前评估各个单元时间效率 这个表在Pactice of Programming Page 193.

    1. 指令本身

      不同类型指令,同样是+,-等等不同data type也是不一样的。

    2. 存取速度

      array的一维,二维,三维 hash,以及数据类型的影响。

    3. 各层API本身

    当然可以读各家的数据手册,得到这些数据。 各个硬件厂商都会提供这些数据的。

对于操作系统的优化

  1. 2-20% wins, I/O or buffer size tuning, NUMA config,etc
  2. 2-200x wins: bugs, disable features, perturbations causing latency outliers.
  3. Kernels change, new devices are added, workloads scale, and new perf issue are encountered
  4. Analyze application perf from kernel/system context 2-2000x wins, identifing and eliminating unnecessary work.

对优化的策略

  1. 要方便可重复,保存相关元数据,例如版本,配置文件等等。并且要保持往前走,而不是回退。 保存数据,并且利用可视化工具来推动往前走。

iostat,ionice

Utrace,systemtap,Dtrace

http://landley.net/kdocs/ols/2007/ols2007v1-pages-215-224.pdf 为了提高profiling本身性能以及灵活性,人们已经不断探索之后。

Dtrace 采用的是 expect的 expect/action模式,并且采用D语言来实现脚本。 http://www.ibm.com/developerworks/cn/linux/l-cn-systemtap2/

可视化

最好的可视化,就像示波器一样,有一个系统的原理框图,并且各个模块的数据演示在上面,例如热图的变化等等。 系统图就像http://www.brendangregg.com/usemethod.html 方法里提到的一样。USE是一种比较可行的方法。

profiling experiments

cmatrix

https://github.com/abishekvashok/cmatrix

总体用一个二维的数组保存状态,然后循环更新一行,有新的一行,就随机生成。关键是加入一些垂直的空格,这样看起来就像向下滚动。并加入一颜色。 应用vim 的键盘操作也能实现。

/* Matrix typedef */
typedef struct cmatrix {
    int val;
    int bold;
} cmatrix;

主要是利用了库 libncurses 来实现。

主要使用到的函数.

move(i,j)
attron(COLOR_PAIR(COLOR_WHITE))
addch(matrix[i][j].val)
attroff(COLOR_PAIR(COLOR_WHITE))
namps(ms)  //sleep for ms millseconds

总共用了近3个小时,code lines 总共也才725 行。正常用应该用半个小时就应该够了。

git clone
./configure --enable-debug
vim Makefile remove -O2
perf record -g ./cmatrix
perf record  //flat call time usage
perf report --stdio  //callgraph

#. profiling with SP
LD_PRELOAD="/opt/nvidia/system_profiler/libPerfInjection64.so" ./cmatrix

#. vscode debug the code
分析
  1. 动态二维数据分配是有问题,是正好,cmatrix 大小正好等于了 指针的长度。

    cmatrix **matrix = (cmatrix **) NULL;
    ....
    
    matrix = nmalloc(sizeof(cmatrix **) * (LINES + 1));
    for (i = 0; i <= LINES; i++) {
        matrix[i] = nmalloc(sizeof(cmatrix) * COLS);
    }
    

专题研究

  1. C 语言的实现原理
  2. C++的内存对象
  3. python 源码解读
  4. 语言的设计

stage of GCC

对于程序的各个过程现在才有更深的认识,以前停留在浅层的认识。尤其是在IDE上更是不知道怎么回事。真实的过程。就拿C语言来说。 首先要区分的那就是编译与链接。编译的时候,是以文件为单件的,相互独立的。最终链接成一个应用程序。但是这个应用程序也非得完全意义的应用程序。根据不同的操作系统,会有不同要求。例如每一个库会公用的,哪些需要独立提供的。例如操作系统是如何操作一个应用程序的。

预编译

宏替换是全局的,所以要想保证唯一就要加各种各样的前缀,也可以gcc来查看各个宏的定义。

如何解决编译的问题

找不到头文件 ,是因为 -isysroot ,或者 -I 没有设置需要的路径。到底设置了哪些路径,可以通过gcc -v 来得到来查看其真实的设置路径。当然还会一些其他的调试手段,这个与gdb 的过程是一样,那如何对于gcc扩展,也就是LLVM如何来操作呢。具体的可以查看 例如

详情见 gcc manual 3.9 Options for Debugging Your Program or GCC ,基本上编译所有过程都是可以debug的。所以以后遇到问题,要能够用以前学过的那些编译理论来进行推理,并且通过这些命令来进行验证。VS的好处就是可以只编译一个文件,与配置每一个文件的编译属性,那个与makefile是一样的,make采用每个编译的都要指定编译参数,并且通过变量来进行复用。例如右键来设置命令行参数或者属性来进行调试。例如 -E -v 就可以通过 -o file 来可以查到预处理的结果,预处理结果会有注释,#include 是从哪个文件进来的。并且可以调试那些预处理命令。 今天问题的关键是没有思路,不知道遇到这个情况去利用已经有的知识去解决,单个文件的处理与大规模的处理之间的关系。 并且编译参数都是可以由环境变量来指定的,例如bash中可以用环境变量PERL来指定系统所使用的perl. 关于gcc的更多问题,还可以查看200711-GCC-Internals-1-condensed.pdf,以及manual.

找到的文件不是你想要的

这个就是今天link.h 的内容不对,原来apex取了ndk中的link.h了,如何解决这个问题,

  1. 快速搜索一下有多少个link.h,在linux 下使用find,grep,sort,diff等,而在windows下可以使用powershell,gci+select-object+out-file 等等。
  2. 利用gcc 的 -E -v来查看一下,它 include进来的结果到底对不对。gcc -E -v -o file 就可以查看到file中的预处理内容,预处理注释信息是用# 来说明,在第几行加载的。
  3. 可以根据规则,#include <>,”“,以及使用这些参数来控制优先级 Options for Directory Search 优先级,-I 要高于-isysroot, “” 会基于源文件的当前路径,而不会去找父路径。当前,-I,-isysroot.

-I -iquote -b, 查找exe 文件 -isysroot

编辑器找到文件,与gcc找到文件可能是不一样的。但通常情况是一样的。 也可以通过 #error 等指令来进行判断。 同时预处理文件格式说明参考 Preprocessor-Output.html 同时利用在语言本身中也是可以用#pragma warning/error等来进行编译的控制。 http://www.cnblogs.com/xiaoyixy/archive/2006/04/12/372770.html

例如你在期望的位置放 #error message 如果是你期望的路径就会报错。

同时这些头文件的先后顺序也会影响的编译的结果。 例如Nsight Tegra 的头文件搜索顺序

Stage_3/Compiler/include_path_order.png
#ifdefine HFAFAF_FE
#error “something'  __FILE__ __FART_NAME__
#message
#warning

#pragma

如果是链接找到不库函数,则是库的版本不兼容,如果出现mingle name不对,则是编译器的版本不一致造成的。换成统一的toolchain重新编译就可以了。

选项分类

-m Machine Dependent Options -mfpu  
-M 根据include以及宏定义自动产生 makefile的依赖规则 在make 中可以$CC –M 来使用  

Code Overlays

If your program is too large to fit completely in your target system’s memory. we could use =overlays= to work around this problem.

  1. LD 讲解
  2. gnu-linker manual
  3. UNIX 目标文件初探
  4. ld命令初识
  5. ldconfig 用来管理与更新动态连接库的,更新/etc/ld.so.cache 例如 -p 就会打印系统所用到动态链接库。
  6. ld.bfd vs ld.gold it seems ld.gold can’t compiling the kernel.
  7. ld参数 now 主要解决符号解析,与segment的创建。

float 点数

这个是每家处理器一个竞争的功能,每家的功能也不一样。

  1. 对于浮点数,硬件支持,还是软实现,它的ABI也是不一样的。
  2. sec-armfloat

See also

#. abi application binnary interface, the object file structure and naming rule #. #. #. mouseOS 技术小站 关于汇编与机器码一个非常好的站 #. Including Frameworks #. PolyhedralInterface #. gcc 源码分析

thinking

profling when you want profiling with Gprof,gcov (gnu coverage of code), you need compiler with -pg, or use the ld . normally there are three version: #. release strip the debug symbol #. debug add the debug symbol #. profiling add the tracing function for gather the information

ld  -o myprog /lib/gcrt0.o myprog.o  utils.o -lc_p

the real system is that ctr0.o

objcopy you use it do format transform directly on .o and o.bin file. http://hi.baidu.com/weiweisuo1986/item/b8a142b8e3e46cec4fc7fd05 http://book.51cto.com/art/200806/78862.htm.

为什么避免干扰,一般把生成的/lib, /obj /build目录都分开,那么些在make or ant 是如何设定的。

代码的生成方式 --enable-static-link, --disable-shared -static 对于是生成exe,或者.so 只是编译的参数与链接的库不一样,完全可以同一套代码,生成多种格式。

debug information

-gtab  produces debug info in a format that is superior to formats such as COFF.
-gdwarf-2 is also effective form for debug info.

如何查看当前编译的各种配置 gcc会有一个配置文件,spec 文件。 同时也提供了各种参数供你来查询,例如-dumpXXX,-printXXXX等。同时也-spec 来指定配置文件。 具体的语法是3.1.5.并且gcc 只是一个前端,他在后端去调用各种宏替换,以及编译器,连接器等。所有的参数都是分发都是根据配置文件来定的。如果这样的话,是不是可以利用gcc的壳来实现一些自己的东西。gcc 的强大在于,支持重多的参数多,把各个后台的参数都集中起来。 并且这个配置文件也是支持脚本的。看来脚本在计算机大老里是一个很容易的事情。自己是不是去读一下 reference1 , Howto SpecsFile 配置toolchains的过程其实就是很大一部分工作就是这个specfile的修改过程。 自己做导出4.7.2与4.7 spec 可以通过diff,同时学习下这些语法。 并且对于这种脚本语法进行一下总结。类似于gawk,他们表一般都一些全局的特珠变量,以及正则表达式的替换规则,以及巴斯特范式。 – Main.GangweiLi - 25 Apr 2013

如何解决循环依赖

Circular Dependency 可以动态替换的方式。产生了鸡与蛋的问题。对于gcc 可以使用–start-group –end-group / -( -) 这样来保证的循环。一般情况下。LD会自动判断依赖的。 gcc 库顺序问题解决方法 lib.a 静态库,*lib.o*动态库。

-W 来控制所有的告警,gcc把后端的所有输出都集中这里,这个是如何做到,并且保持这种灵活性。

– Main.GangweiLi - 25 Apr 2013

gcc 对于管道的支持

巧用:

echo -e '#define cat(c,d) c##.d \n #define mb(a,b) a##@b \n mb(cat(xiyou,wangcong),cat(gmail,com))'  | gcc -E -xc - 2>/dev/null |tail -n 1

from http://wangcong.org/

– Main.GangweiLi - 25 Apr 2013

FP寄存器及frame pointer介绍 函数调用的栈的标志位,这个这个寄存器来快速得到当前那个这个函数栈长度。如果没有,就只能根据指令来了。对于backtrace时就会很麻烦。一般情况下没有了FP,很多系统不支持backtrace.为了简单。 Register Usage 这么多年的困惑终于明白了,一直想知道C语言如何来直接操作寄存器的。原来在编译的时候,可以根据ABI接口来定义寄存器的分配规则。来动态分配。为了能够尽可能接近人直接编写汇编的效率,人们对于编译原理进行深入的感觉 ,并且研究各种算法来帮助我们实现。目前最新的LLVM采用SSA的方法大大简化了跟踪方法。只要分析抽象分析归纳终究是能够找到好的方法的。正因为有编程原理,我们才可以利用向自然语言的描述与机器打交道。只要找到一种简单有效的map规律就可以简化我们操作。

Nsight Tegra has three configuration

debug   -g  -O0    -fno-omit-frame-pointer
profile   -g   -03    -fno-omit-frame-pointer
release        -03  -fomit-frame-pointer

– Main.GangweiLi - 08 May 2013

如何在代码中控制优化的行为 gcc 6.30 Delcaring Attributes of Fuctions, 定义了对函数的各种属性,以及变量也有各种属性,例如volatile, register等。都是为了控制编译与优化的。告诉你这一段代码有什么特性。还让编译器来做一些特定的事情。就个与今天所听到openACC。通过指令来标记代码,来让编译器来优化与改变。例如多核,情况下来保护现有代码。例如可能把所有代码都重新再用cuda写一遍吧。例如这里有各种`实验 <http://www.cnblogs.com/respawn/archive/2012/07/09/2582078.html>`_ ,同时也想起当然那个bell lib的那个有趣破解故事。__declspec C99标准里只有extern, static等几个关键字。

– Main.GangweiLi - 09 May 2013

对于预编译 如何预防重复的加载呢,以及循环加载呢。采用宏定义,不能完全避免。因为你也不知道你的include的文件里已经include了。#if ndefine pragma once 当然另外一种预编译那就是提前编译好现成,可以只提供一个空文件名来骗过编译,只在链接的时候直接读库就行了。

编译与连接问题 include路径不是嵌套原因,原因在搜索机制,它是简单通过再组装来判断文件是否存在进行搜索的。所在编译的时候,要么指直接用绝对路径来指,要么就是先指路径名,然后再指文件名,这样让编译器的搜索机制来处理,当然这会有冲突,这个与搜索顺序有关。找不到的原因,经常的原因是路径有空格之类的问题,不管IDE 工具的什么样的继承,或者additonXXX之类,不过是都是编译的-I XXXX 中一员而己,无非是编译的顺序不同而己。在IDE中出现这个问题,很大部分原因会是编译器并没有把选项传递给编译器。 现在突然明白了所谓的IDE工具都是如何工作的了。并且有IDE工具在收集错话的过程会把详细的信息给丢了。只有最后的yes or no的信息,如何才能收集到更加信息呢。那就是直接在命令执行这个编译命令。并且还可以打开编译器的log信息。来进一步定位。

另外一方便也可能是toolchain本身的兼容性,特别是ld.更是如此, 以及如忽略那些undefined symbols.等等问题。

– Main.GangweiLi - 02 Jul 2013

如何在代码中加汇编 一个方式那就是直接ASM(),具体的语法可以看Inline CTX in CUDA.pdf 相当于一个函数调用,参数传递函数参数的传递,但是代码是直接copy到输出的。 其实原理也很简单,就是m4中的替换原则,这个就是那些直接copy输出到就行了。其实M4是原始的编程语言,可以直接实现各种转换,而scheme需要少量的delimiter同样实现这些。所谓的那些lambda理论都是可以用m4 来实现。不过现在都简化成列表了。其实更加像现在sphinx一样,加入少量的原语标记,就可以实现实时再编程。把CDF直接做出来,就像我可以简单在一个文本简单的处理一下,可以变成python的collection,dictionary或者复杂结构了,解决xml更加简单的做法,那就是直接替换成python的数据结构,直接实现嵌套进去就解决了。 例如xml->.py -> import it. this is perfect. no need other lib to do this.哈哈看来可以把文本处理再提高一个水平。后面直接scheme或者haskell来实现与解决这些。看来需要时间把rackit抓紧时时间学一下,然后研究一下王垠的那些理论了。同时也慢慢对LLVM会有更深的认识了。

GCC

编译过程,主要分为两大块,把C语言翻译成ASM代码,然后ASM编译成机器码。

GCC优化集中C到—>ASM这一段。

同时c++也是这一层。 而LLVM把每一层通用化,就可以层层优化。GCC在语言之前用M4 对c代码,进行一次优化定制。解决写重复代码的机制。

所以这个翻译的过程,基本都是这样的过程,这一本身的优化,以及往下一层的翻译功能。 同时还要有一个linker的机制,如何一个个的小文件单元合成一个大的小文件单元。

https://en.wikibooks.org/wiki/Category:GNU_C_Compiler_Internals

预定的宏用可以用调试,或者生成版本号。 __DATE__,__TIME__,__FILE__,__LINE__

一般情况下,C/C++编译器会内置几个宏,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。

ANSI C标准中有几个标准预定义宏(也是常用的): __LINE__:在源代码中插入当前源代码行号; __FILE__:在源文件中插入当前源文件名; __DATE__:在源文件中插入当前的编译日期 __TIME__:在源文件中插入当前编译时间; __STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1; __cplusplus:当编写C++程序时该标识符被定义。·

__cplusplus:当编写C++程序时该标识符被定义。·

目标
  1. 预编译与宏处理的应用
digraph GCC {
      subgraph   cluster_flow {
            label = "flow";
            rank= same;
            preprocessing -> compilation -> assembly -> linking;
      };

    subgraph cluster_software {
              rank=same;
              label = software ;
               sourceCode;
               compilerFile [label = ".S"];
               OBJFile [ label = ".o"];
               exeFile;
           }
        preprocessing ->sourceCode  [label = "gcc -E"];
        compilation -> compilerFile [ label = "gcc -S"];
        assembly   ->  OBJFile [ label = "gcc -c"];
        linking -> exeFile ;
}

优化实例

在编译Gameworks 中Bloom时生成x86结构的代码的时候就会出现 :error:`size of array "NvCompileTimeAsert_Dummy" is negative`, 这个由于内存对齐问题产生。 不同的硬件对于对齐有不同的要求。gcc 提供了灵活控制,对于一个小结构。#pragma pack(n)来进行控制。但是对齐之后的数据的地址也是有些变化。

如果对于整体的要求,而可以 -malign-XX 来控制。但是会引起ABI的不兼合问题。see

On x86-64, ‘-malign-double’ is enabled by default. Warning: if you use the ‘-malign-double’ switch, structures containing the above types are aligned differently than the published application binary interface specifications for the 386 and are not binary compatible with structures in code compiled without that switch.

—Page 231 of gcc.pdf

  1. 算法本身对数据结构有对齐的要求,那么直接 -malign-XXX来进行整体控制。

no-strict-aliasing

http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule

也就是允许,同一块内存,可以用不结构体去读它,因点类似于 union的概念。而在实际的操作过程,就直接是指针操作了。 只要自己知道 其内部的layout pack了。

直接发利用instruments代码中添加trace

直接利用 -finstrument-fuctions-finstrument-functions-exclude-file-list=file,file,.. 来实现

你要提供这样两个函数

void _cyg_profile_func_enter(void * this_fn, void *call_site);
void _cyg_profile_func_exit(void *this_fn,void *call_site);

同时可以参考 例子 https://github.com/gwli/code-samples/tree/master/posts/nvtx

或者参考另一个例子 https://mcuoneclipse.com/2015/04/04/poor-mans-trace-free-of-charge-function-entryexit-trace-with-gnu-tools/ 代码见 https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_FuncTrace

并在代码中添加 getpid gettid 就可以生成timeline了。

fwrapv

这指有符号运算用补码计算的方式。

https://gcc.gnu.org/onlinedocs/gcc-4.3.4/gcc/Code-Gen-Options.html

#include

中的文件路径名,怎么写是根据你的include path来的,与环境path 的方式是一样,直接发路径拼接起来去找的。 就可以了。有的时候 egl.h 找不到, 但是EGLegl.h 就可以找到。区别就在于 include path不一样。

-funsigned-char

不同的系统中 char的定义是不一样的。分为signed 或者unsigned.

-inline

在一些版本上格式的有一些要求,不然会报错。http://10.19.226.116:8800/trac/ticket/6132#no6

.o 与.so 的区别

本质的区别,.so .a 都是.o ar包,区别在于地址形式的不同。libtool工具同时解决库依赖的问题。 用libtool和生成库会自动管理依赖。 并且不同平台的库的搜索方式是细微的不同的。

http://www.eetop.cn/blog/html/40/202640-8862.html

asan-stack

address sanity analysis. 地址分析。

ipa

程序块间的依赖分析。

pta

指针分析

branch-likely

可以根据优先级概率来生成代码。可以参考 https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html

什么是ABI

ABI 的发展史也是程序发展史,从最初的打点卡,一步步地解决执行的效率的问题,另一个那就是复用的问题,还有编程复杂度的问题。

符号修饰标准,变量内存布局,函数调用方式等这些跟可执行代码二进制兼容性样相关的内容称为 Application Binary Interface.

ABI 发展一直就是复用性演变的结果。 只要实现一次动态,就要加一个表来实现转接跳转。

从代码要复用,就要地址无关,那就要从定向,数据也是一样的,也要实现平台无关也要重定向。所以也就有了各种各样的动态表。

一般流程, CPU取指令,如果发现取令地址为征,就要触发中断来找查了。这样的效率是要比静态的要低。

代码重定向从简单分段,到后面真正的动态。是要两张表的.

ABI的发展,也就是ELF的发展,也就是计算机发展史,同时也程序的发展史。从一开始的卡片机,那时候的单进程。并且每一次都只能从头开始。在到后来,可以多条进程串行,而不用重新来过,或者调整地址。而实现重定向功能。而实现分段功能。同时由于程序局部分性,程序的执行也不需要所有代码都在内存里,只要其前后再里边就行了。这样也就产生了页式内存了。每一次只用加载一页就行了。但是每一次加载一页就地址就又得重新调整。这也产生虚拟地址。这样每一个程序都独立占有4G的内存空间。这样也就彻底解决了多进程的问题,以及内存不够的问题。 这个过程也就是内存管理的过程。

每一个进程的地址空间是这样的分配的,内核空间占用了高位的2G左右地址,而实际上在内存的低位,这样方便的计算,最大地址-虚拟地址就是真实的物理地址。而从内核高位的向下生长的的栈了,而堆在是下面向上生长的,最低位的一些地址,两者往中间挤,程序的内存放在中间,而Data分区放在低位,而代码段data之后。

加载的时候,属性相同的段也是可以合并的,内存的分配了,是按照页分配的,如果把属性的段合并在一起就可以减少了页的碎片的浪费。并且每一个段根据属性,只读或只写,或者可读与可写。有了这样的分类,就可以对其安全保护,对于只读的代码是不有写的。这正是elf中段属性可读与可写的规定,并且代码出现段错误,也是读写不该读写的段,同时想进行线程注入,首先得那这些标志位给改掉。

而这个完了之后,我回来头看程序本身,内存本身很宝贵,要就尽可能节省内存了。就要看看程序能不能节首的一些空间。程序也最多的静态链接,变成动态链接,以及运行时加载。

静态链接是方便,但是重复太多,浪费的太多的内存,就想一些公用的库来共享使用,这样就想到动态的链接,也就是态链接器的用法,那就是加载时链接,-Share 就是要加载 进行连接。一个公用函数在每一个程序的使用的位置也不一样的。这样需要保证要有重定位的机制,来适应的不同的地址。这样就可以让这些动态库在不同的地址空间有不同地址。这个就使用 地址独立了。这也就是 -fPIC 的用途。它是如何实现的法,也没有什么好的办法,通过添加一级来进行查找,能做那就是查表了,也就是 .got 的用途。

.got 用于数据变量的重定向,而.plt用于函数调用的,好像也不完全是。 就是通过这两个表来实现的调用依赖,并且在链接的时候也是在不断更新这两个表。 https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html 这里采用两级表,首先,GOT里记录一个起点,例如libc.so 的起点在哪里, .plt记载了当前这个函数或者变量在偏移量,libc.so的偏移量为20地方。 所以在plt表时经看到 最后相当于拿到起点值,再加上移值量,基本上三条指令。 第一条从r12得到全局表,第二条得到起点值,第三条得到需要的值。正好pc值,这也就跳转对应的代码了。而下面这表就是当前.o文件plt. 根据这个表找到libc.so 中 raise:的位置。

pthread_create@plt:
0x40693644  add          r12, pc, #0, 12
0x40693648  add          r12, r12, #401408   ; 0x62000
0x4069364C  ldr          pc, [r12, #892]!    ; 0x37c
pthread_gettid_np@plt:
0x40693650  add          r12, pc, #0, 12
0x40693654  add          r12, r12, #401408   ; 0x62000
0x40693658  ldr          pc, [r12, #884]!    ; 0x374
pthread_kill@plt:
0x4069365C  add          r12, pc, #0, 12
0x40693660  add          r12, r12, #401408   ; 0x62000
0x40693664  ldr          pc, [r12, #876]!    ; 0x36c
pthread_setname_np@plt:
0x40693668  add          r12, pc, #0, 12
0x4069366C  add          r12, r12, #401408   ; 0x62000
0x40693670  ldr          pc, [r12, #868]!    ; 0x364
__timer_delete@plt:
0x40693674  add          r12, pc, #0, 12
0x40693678  add          r12, r12, #401408   ; 0x62000
0x4069367C  ldr          pc, [r12, #860]!    ; 0x35c
__timer_gettime@plt:

.got 主要用于模块间的符号调用,表格中放的是数据全局符号的地址,该表项是在动态库被加载后由动态加载器进行初始化,动态库内所有对数据全局符号的访问都到该表中取出相应的地址。即可做到与具体地址了,而该作为动态库的一部分,访问起来与访问模块内的数据是一样的。 所以每一个模块对外部的需求都放在got中,这样就可以实现了自包含了,你给我一个正确的地址就行了。 代码的链接过程,包括代码本身的代码的共享与数据的共享机制,并且还有还要冲突的问题。

延迟加载的实现,就用了GOT.PLT,这样只需要在第一次使用外部函数的进行解析,而需要事先解析全部的函数地址。

.rel.dyn 主要是为了全局变量,.rel.plt 主要是为函数的跳转。 http://stackoverflow.com/questions/11676472/what-is-the-difference-between-got-and-got-plt-section LD_BIND_NOW 环境变量可以控制, 在应用程序加载的时候就解析,而非等到使用时再解析。 http://refspecs.linuxfoundation.org/ELF/zSeries/lzsabi0_zSeries/x2251.html

fun@plt:
jmp * (fun@got.plt)
push index
jump _init

这样不是第一次调用,则在GOT.PLT表中存好了函数地址,就直接执行了,没有没有把 index 放在栈中,然后调用 _init来填充表项。

每一个module的 数据段都是独立的,都是通过GOT表来进行依赖的查询的。

为什么要有链接方式呢,就是为方便复用,解决地址冲突的问题的。在写代码的时候,命名冲突,这包括函数名以及变亮名,这也就有了命名空间以及demange的做法,所以在函数连接失败某些东东的时候,一个原因生成的符号不匹配。 利用 external C 来实现的。 也就是为解决符号匹配一致的问题。另外在链接还需要地址修订的问题。可以静态的时候做,也可以用动态加载的时候去做。动态的时候时候做 ELF 就会各种各样的 .rel.XXX 这样段,来指定某一段应该如何重定向。

现在对于整个编译是有了更深的认识,在预编译阶段,include 的各种源码都加载进来,所谓的那些头文件,只是为fake卡的,也就是为即使你不用编译原码本身,就可以我们的代码认为我们已经有这个函数可以用。而真正的函数地址在哪里在编译的时候并不知道在哪里。而是等到链接时候,才能知道真正的地址,所在链接之前,还是必有符号表的,这样才保证找到真正的地址。

在编译的时候,每个源文件都要所有的符号都存在,即使那是一个假符号,而在符号表里,符号会被标出来哪些是本地已经有的,哪些是需要去外面找的。在链接的时候是会把这些符号进行合并,并且也还会解决符号的冲突问题。

之所以能够重定向,为什么知道不同的文件里大家调用是同一段代码呢,那就是通过符号表。大家引用了相同的符号表就是说明调用了相同函数。如果出现同名就可能出错了,这个与你链接的时库查询顺序是相关的。

这个库的顺序就像PATH是一样的,是由 /etc/ld.conf 来指定的, 并且操作顺序一般好像是

  1. /usr/lib
  2. /lib 这个下面一般都 sbin的一些库。
  3. /usr/local/lib

为了减少空间,同时也为提高解决问题方便,可以符号文件也可以单纯放的,linux是放在 /usr/lib/debug下面的,并且是根据文件名或者build-id来进行识别的。

如果想要到这个过程进行控制的话有几个地方是可以控制的

  1. 全局性的控制, /etc/ld.conf
  2. 临时性的控制利用环境变量, - LD_LIBPATH_PATH - LD_PRELOAD - LD_DEBUG
  3. 程序链接时参数 -L -l
  4. 代码级的控制,那就是label了,etext,edata,eend等等。记录了其特殊的位置。

在不需要对符号级的指令调整,就可以把symbols给strip掉了,这个一般在编译时就可以做了。 在加载的时候,都不会有函数级的指令调整,一般都是module级的调整。

Symbols Table Format

https://sourceware.org/gdb/onlinedocs/stabs/Symbol-Table-Format.html

格式,以及 利用info symbols 来查看一下。

struct internal_nlist {
    unsigned long n_strx;         /* index into string table of name */
    unsigned char n_type;         /* type of symbol * /
    unsigned char n_other;        /* misc info (usually empty) * /
    unsigned short n_desc;        /* description field */
    bfd_vma n_value;              /* value of symbol * /
 };

然后再看看其是如何存储的。

对于profiling的采用也很简单,只要记录当时的指令的地址,然后根据地址来计算出 在所个文件里,哪一个函数里。这样callstack就出来了。

其实所有的二制结构,要么采用表机制,要么采用TLV机制,指针采用就是TLV机制,所谓的灵活, 那就是几级表的问题,目前复杂的ABI结构,以及操作系统memory结构都是这样的。只用table或者TLV或者两者都有,并且不只一级。

每一行source code 至少对应一条指令,source line/asm code 比值是多少。其实一个逻辑块越大越容易优化。 其实就像函数式编程。

在汇编程序层来说,都是机器的执行是没有区别的。但是在操作系统层面就一样的。就会有各种各样的调用约定,如果程序执行输入 与输出顺序,那就是参数传递机制。所以参数长度的检查很重要,过长就会靠成stackoverflow的问题。

BFDAandABI

这里就 ELF 格式为例, 来进行来研究。

例如pentak就是利用ELF头来判断binary 的架构的,一个简单做法那就是。

internal ElfHeader GetElfHeader(string packageName, int pid)
     {
         string header = SubmitShellRunAsCommand(TimeoutMs, packageName, "dd bs={0} count=1 if=/proc/{1}/exe 2>/dev/null", ElfHeader.Size, pid);
         Contract.Assert(header.Length == ElfHeader.Size);
         return new ElfHeader(Encoding.ASCII.GetBytes(header));
     }

为什么变量的长短的以及函数名的长短的问题

这个的长短会影响不大呢,原来ELF 所有字符串会都会放在 .string table里,所有用到自符串的地方都会从这里去头,所以函数名与变量名的长度只是影响了 .string table的大小而己。 而在需要这些名字的地方是 .string table 的索引而己。

PE PE structure study  
ELF    

ABI 指的就是`ELF,COFF,和PE COFF <http://www.cnblogs.com/yizhu2000/archive/2009/03/24/1420953.html>`_ 这些东东,可执行文件的格式。不同的操作系统是不一样的。思考一个问题,同一个CPU对应的汇编指令是一样的,并且结构也都是一样的,但是为什么ABI为什么会不一样的。原因不同的ABI是内存管理分配的方式是不一样的。并且代码组织方式也都是不一样的。 例如`C++ABI <http://mentorembedded.github.io/cxx-abi/abi.html>`_ 这里描述了各种虚表的实现方式。

一个可执行文件对于外部库是不知道的,只是生成一个占位符,然后由加载器在加载的时候,去查找其位置,并把其替换成对应的地址。

对于面向对象的编程,函数表是在运行时,还是只存在于编译阶段,应该是都有吧,要不然,RTTI如何来做的呢。

什么东东需要知道ABI,OS kernel, linker,dynamic linker, 以及GDB需要知道这些。当然正常情况下都是可以自动识别的 另外就是处理器自身的编码格式,例如ARM采用的固定长度的编码。可以采用哈夫曼编码。所以ABI应该包含两部分,一个汇编指令集本身,另外一种它本身的结构了。汇编就是是汉字一样,要组成一文章还要一些文法结构。例如诗体,散文等。 #. `对于GDB你也可以改它的 http://sourceware.org/gdb/onlinedocs/gdb/ABI.html>`_ . #. ABI Policy and Guidelines #. API 与 ABI 一个通俗点的解释。并且可以检测这种变化的。 #. 向其它应用程序地址空间注入代码 #. PE格式文件的代码注入 #. 代码注入技术 #. ptrace应用之三代码注入 也可以利用

set write on ;show write
注意的是动态库libdynlib.so在编译时指定了-fPIC选项,用来生成地址无关的程序。
也可以利用ld脚本来进行代码注入。利用gcc进行注入的方法,也当然bell lib 所采用一种方式。
*COFF file structure*

when you add -g to gcc, when compile will add .loc .Ldebug_info: in assembly code and assembly will instore these in the symbol table fnd String Table and LineNumber Table of objfile. without -g, these information will be striped, so will can’t reverse back which line to line.

Object file is almost same with .exe file. the most different is that the address and entry points.

Options for Code Generation Conventions

Most of the options are prefix with -f. for different requirement, there is need different code(this code means final code,not the immediate code). for example the share lib need position-independent code.

elf,pe these are ABI, each one has its own structure, it specify the how the program is load into the memory, and this memory allocation for the process, where put the data,where put the code. where put on the resource. each section has its own function. when and how to use it and triger these code has specification. the how is virus generate and not to infect the exe file. all is base on ABI,

Virus the probelm for virus is how to triger execute malicious code. you utilize init stage or change standard lib call, this is good method, you can wrap the standard share lib call, interrupt the call link, for example, you change printf call, you change intercept printf, after execute you code and then return nomal printf. so you need study standard libc. how many call. how the share lib call. one of method change linker and loader of the system. the other method you can exception handle to trigger your code. dwarf is this way, this paper is also put on kuaipan/debug, there is the katana you can use it to do hotfix for binary code. for example currently running process. %RED%use this to implement Dynamic linker of exe%ENDCOLOR%

Libunwind this use ABI layout to discuss manipulate the stack of programming. there is a project libunwind , and Pentak begin add this. if So, it support SetJump directly. how to control CPU flow, one is use assemble. the other is that you just add function to the target program. As long as, the input and output is legal.

LD

程序的链接和装入及Linux下动态链接的实现 编译的时候,只处理本地符号,本地找不到就会标识成未定义的,然后由linker去查找修改。如果linker也找不到,就会报错了。所以出错,首先要看你调用是本地的还是。。 你可以用gcc -c 只编译成obj文件。可以使用objdump查看obj文件。例如 -dx还可以看到反汇编。 你可以通过find + objdump 来进行查找各种符号与汇编的信息。虽然不要求读懂每一行,但要知道常用调用,函数的开头与结尾要能够看出来。 linker is loader’s brother, and reversely. One of problem is how to redirect the address of your program. and GDB support this feature for debugging.

要想实现指令级的复用,那就得好好研究一下loader了。

normally the lib linker order is not specially, but sometimes you need a specific order. but the linker loaded it by the order you specify it. 当然如果出现你已经加载了某一个库,但还是报找不到链接或者未定义,这个时候应该就是链接顺序的问题了。 [[http://www.cppblog.com/findingworld/archive/2008/11/09/66408.html][gcc 库顺序问题解决方法]]。 并且可以用strace来跟踪你的应用程序调用哪些API。可以轻松知道应用起动的过程都做什么。

如果修改系统库的一些函数,这个时候,不需要加载系统库,不然会冲突,这个时候,你可以用 -nostdlib 或者-nodefaultlib等来做。libgcc就是其中之一。但是大部分程序都会需要它,-llibgcc. 当然如果想hook一个API时,在linux 下很简单那直接写一个自己.so 然后再加上一个LD_PRELOAD,这样应用程序在调用应API时,就会先在`LD_PRELOAD库去找]]。 而在windows 下会有一个 [[http://easyhook.codeplex.com/][easyhook <http://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/>`_ 与MS 的detour 来实现。

应用程序在加先从应用程序的地址来判断这个地址在哪一个库里,然后再查表找到相对应的库的符号表去查询。但是如何编译ABI不一样,例如C直接调用C++函数是不行,你还是发现找不到函数定义的,原因在于C++的函数在mangle方式与C的是不一样的,并且符号表结构也可能是不一样的。这样当然也就找不到了。

在解决链接问题的时候,要注意两点,对于编译问题,VS支持从当前编译路径去查找,所以在找不到定义的时候,自己或以来用这个方法来解决,如果却实没有,那就是漏了一些源码目标或者头文件。用-I 来添加。 对于链接问题,一个是用-L 来添加搜索目录,例外要用-l 来指定库名。 而-I(include)加载头文件,-isystem加载系统头文件。 并且通过预编译指令来控制编译。例如各种宏定义。

-Wl,–as-need 这样就可以避免链接不必要的库,另外ldd -u 可以查看到哪些库链接了,但是根本用不着。 * -Wl* 可以直接把参数传给linker, -Wl,-z,no execstack 现在终于明白C语言指针可做硬件灵活性在哪里,C把格式变成编格式就是最好LLVM了,并且C语言中指针,将来就是真实内存地址。当你想crack一些系统或者硬件行为的时候,利用C语言可以达到汇编直接操作,例如函数指针,例如符号表的得到,原来系统函数的地址,然后把地址改在自己的函数,并且函数的声明要原来一样,保证调用不会出错,然后自己处理,再调用系统函数,这也是各种wrapper的写法。在perl里,只就直接使用$e这些中断函数处理通过hook__DIE__这个函数调来实现的,在语言可以trap自己的函数来对segmentfault以及abort,exit等等进行hook处理。或者直接启动调器来工作。现在明白syscall有漏洞的用法了,因为syscall是不受权限限制,可以通过内核启动自己程序。这样解决权限的问题。

这就是如何用语言得到汇编的控制水平,因为在汇编可以任意改变PC值来改变执行的流。明白了汇编到了高级语言失去了什么。失去了对硬件直接控制,同时提高通用性。例如汇编直接硬件机器的指令,以及直接操作硬件的各种信息。而高级语言则失去这种控制,但来的通用性。但在有些时候,还想直接控制如何处理呢,可以通过在C语言中直接使用汇编来处理。另一个办法那就是找到精确的对应,例如如何直接控制PC值呢。当然在嵌入式编程中C语言是可以控制寄存器的。

现在终于明白了连接的意义从前到后。

如果想在带码中控制将来代码分配与装载的位置,可以用一些特殊的label,这些label是会被 linker认识的,并且在编译的时候是会保留的。

extern etext,edata,end 这三个是程序segments.并且可以通用 man end 来查看。

float

至于是用softfloat,还是hardfloat,这个取决于你的系统是不是有float指令运算集,如果有就直接用hardware来就会非常的高效,如果没有 只能用software来行转,同时为通用,那是不是可以在加载连接的时候去动态的调整呢。也就是所谓的JIT编译的一部分,其实更像了NVCC那样 PTX到SASS这样的效率就会更高。会根据真实的环境进行再一次编译来提高效率。也就是在汇编级的化简了。

程序需要链接根本原因是用于带码的复用。 链接分时静态连接,动态连接。 另外还有代码链接方式与数据连接方式。

LD_PRELOAD 预先加载一些库,这样可以方便把一个help库加载到要调试的进程空间,大大加快的调试的进程。这个特别是大的库的开发的情况下会用到,apk会在某个库里会失败,但是这个库却没有相关工具去查看。这个时候利用LD_PRELOAD把其引进来,或者利用python 通过ctype把库给引进来。

http://blog.csdn.net/haoel/article/details/1602108

ABI 是什么

也就是如何生汇编的, 例如函数调用参数如何传递,以及寄存器的分配原则是什么。决定了如何生成由中间语言来生成汇编代码。

例如ARM 的寄存器规则。http://lli_njupt.0fees.net/ar01s05.html , R11 是栈指针,R11为SP。

一个简单的赋值是两条ASM 例如

int i = 1;
mov r0 #1
str r0 [r11,#-8]

函数内部实现变量,就是栈上加减的。

int add(int a,int b) {
   return a + b;
}

int i =0;
i = add(0,1);

mov r0 #0
mov r1 #1
bl 0x<addDress>

##add asm
push {r11} // save framepointer
add sp, sp ,#0  //save current framepointer
sub sp,sp #12, //apply memory for parameter
str r0, [r11,#-8]
str r1, [r11,#-12] //pass the para to stack
ldr r2 [r11,#-8]
ldr r3 [r11,#-12]
add r3,r2,43
mov r0,r3    // r0 as return
sub sp, r11,#0 // recover stack
pop {r11}   //recover last framepoint
bx lr   //go to call point  lr is saved by pc+1 of caller.

函数调用约定,以及寄存器分配策略。这个是ABI要解决有事情。

所以做优化时候要看ABI,而不是瞎想。

例如http://www.x86-64.org/documentation_folder/abi-0.99.pdf 主要内容 #. Machine Interface #. Function Call Sequence #. Operating System Interface #. Process Initialization #. Coding Examples #. Object Files

  • ELF Header
  • Sections
  • Symbol Table
  • Relocation
  1. Program Loading and Dynamic Linking
  2. Libraries.
    • C lib
    • Unwind lib
  3. Execution Environment
  4. Conventions.

所以变量声明也是对分配寄存器有影响。

为什么可以omit-frame-pointer

http://m.blog.csdn.net/article/details?id=49154509,因为只要参数固定,栈的大小是固定的,在编译的时候可以直接计算出 栈的大小的,直接加减就可以搞定 你看到

subq $8 $rsp
addq $8 $rsp

就是直接计算好了,而不需要在额外 pushl,popl指令了,毕竟差异还是很大的。

参数传递

在寄存器少时,是通过内存的入栈来进行参数传递,而当寄存器数量多时候,直接使用寄存器来进行参数传递,一般来说x86-64是6通用寄存器。 当多于6个时,还是用入栈的传递。 所以用函数参数尽好不要超过通用寄存器的数量。

具体还得查看硬件的手册,使其达到满速运行。 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488d/BIICDBDF.html

ABI 四方面内容
  1. Low Level System Info AMD_64 地址指针虽然是64位,但实现上只有48位,并有三个逻辑段,text,data,stack. 内存 Page对齐为4KB-64KB之间。
  2. OBJfile
  3. 程序动态加载
  4. lib
VMA的分配
  1. The system reservera a configuration dependent amount of verual space.
  2. Small code model. 方便程序跳转,因为跳转就要为PC赋值,采用立即数当然是最快的。 但是立即数的大小有限的。只能从0-2^31-2^24-1
  3. Kernel code model. 2^64-2^31到2^64-2^24.
  4. Medium code model. data section is split two path: .ldata,.lrodata,.lbss.
  5. Large code model.

#. Small/medium/Large (PIC). ABI 也要定义DWARF (Debug with Arbitrary Format).中寄存器的对应关系。· PIC code,必然用到GOT表。

llvm-tblgen

让编译器读懂硬件框架, 只有这样才能编译生成针对这个模型最优化代码。当然你可以添加尽可能多的策略。 所有东东都建立其逻辑device,并加载真实的数据,然后用软件仿真分析优化,这样就可以把硬件本身做的简单,而不需要复杂控制逻辑。 这个是目标代码那一块专门一个表来实现。 而在llvm 中tblgen就用来干这个用的。 http://llvm.org/docs/TableGen/LangIntro.html http://llvm.org/docs/TableGen/ http://llvm.org/docs/TableGen/LangRef.html http://llvm.org/docs/TableGen/BackEnds.html

TableGen文件td

由record组成,definiation and class. class是如何继承的。 并且使用let可以赋值。有点像racket.

同时文件类型也支持 include的操作。

当然也支持模板操作。同时支持C++注释语言。

tblgen ARM.td -gen-registery-enums -o ARMGenRegisterNames.inc
tblgen ARM.td -gen-registery-desc -o ARMGenRegisterInfo.inc
tblgen ARM.td -gen-registery-desc-header -o ARMGenRegisterNames.h.inc

register allocation

而汇编语言本身就是CPU的一种抽象。对硬件抽象主要是两大块,对于寄存器的描述。 而寄存器主要继承MRegisterInfo类,同时实现 XXXX.RegisterInfo.td, XXXXRegisterInfo.h XXXRegisterInfo.cpp

并且在XXXXRegisterInfo.td 中描述出目标处理器每个寄存品质属性,以及寄存器之间的别名关系以及程序 运行时的寄存器分本方案。 主要由个类严实: Register,RegisterGroup,RegisterClass,DwarfRegNum.

然后挨个实现各个虚函数就行了。

instruction selection

指令的实现也是样同,主要是TargetInstrInfo.类。 XXXXInstrInfo.td, XXXXInstrInfo.h, XXXXInstrInfo.cpp,

td 中需要描述目标处理器的指令集,指令功能,指令的寻址方式。 指令操作数,指令编码,指令汇编代码的输出格式。 以及指令与LLVM 虚拟指令的匹配关系。

记录定义ops,用于标识指令的操作列表。 Predicate类用来 描述指令进行匹配选择时 的所需的特定条件。 FuncUnit类用于建立目标处理器的功能单元。 InstrStage类用于描述指令执行中某一阶段。

同时出现不匹配时,还要处理 lowering的转换。

帧栈布局描述

主要是继承不愉快实现TargetFrameInfo类实现。

class TargetFrameInfo {
    StackDirection StackDir;
    unsigned StackAlignment;
    int LocalAreaOffset;
}

函数参数个数与返回值个数是受寄存器个数的限制。不够的情况下就得 用栈来做了。

栈是内存中用于局部变量存储以及子进程调用缺少足够参数寄器时传递额外参数 的连续内存区域。 APCS(ARM Process Call Standard) 规定ARM为满递减堆栈。 使用SP做为栈指针,并规定栈的对齐大小为8个字节,也就 SP mode 8 ==0

函数调用约定, #. 当前的函数FP的指向地址 #. 返回链接地址的值 #. 返回的SP、FP值 。 。。。。 相关参数寄存器值。

常的函数内外跳转指令两种,一种是bl,一种是move. 直接move pc XXXX 可以全地址空间跳转。 而bl 只前后32M地址:

bl next
.....
next
.....
move pc lr  从子程序返回。

上层语言中函数,在汇编这一层就只有label,也就是代码块的起始地址。 而这些开始地址怎么来,是由汇编给你算出来的。

最容易调用的,那就是相对位置固定的,利用偏移量来搞定,当然由于直接发计算出来一般是在本module内部。 一个文件本身很少超过32M指令的。所以直接发BL 这种来跳转。

还有一些操作系统预留的函数地址,可以直接move pc 来得到。

其余的函数都是查表的,有一个符号表,那这个符号表,来知道各个function label的值。 就是那个 /poc/kallsys 那个mapping 表。现在明白为什么符号表作用的原因,函数地址查找,就靠它了。 对于函数级别之上地址查询都是符号表,而函数内部的跳转,基本都是采用相对跳转。这样才不会出错。 节省符号表。 理论符号表都是一直存在的。函数内部label是编译器自己定义的。 http://blog.chinaunix.net/uid-16459552-id-3364761.html, 各种函数在汇编层都是代码段标号,标号就是地址。硬件中断的地址是固定的。

因为ARM中 call出栈与入栈的顺序是固定的,其实就可以根据调用约定,来修改ret,sp这些等等。

f(ant a) {
 void * ap = &a;
  * (ap-4) = xxxx;
}

就改掉了你想要的值.

编译设置

在源码树中build脚本中添加编译的选项。

中间代码的转换描述

主要是操作数合法化,指令匹配选择等等。并不是指令都是一一对应的。不匹配时

  1. 目标处理器支持最小类型比LLVM的类型要大,此时将该LLVM类型的数据提升为目标处理器类型的数据 以进行下一步工作。
  2. 目标处理器支持最大类型比LLVM类型要小,把LLVM类型的数据折成数个目标处理可以支持 的类型数据以进行一下步。

这些合法化主要就是通过TargetLowering来实现的。

汇编输出描述: 主要是AsmPrinter类,与AsmWriter 类。

以及JIT的支持也都在这里。

  1. scheduling
  2. code layout optimization
  3. assembly emission
全局描述符

xxxx.td ,XXXXTargetMachine.h,XXXXTargetMachine.cpp 每一个目标系统都有 xx.td 文件,生成也就是解约束方程的过程。

如果经常做profiling,就像经常看这些评测数据,查当前最新的硬件水平。

http://openbenchmarking.org/

函数的优化

使用函数可以提高复用性,减少代码的编写,但是它增加了oerhead,再在性能要求歌严格的条件, 去掉要保证,编写代码的复用性,又要去除代码调用的overhead.

其中一个办法那就是inline. 但是inline的函数只对自己编写的代码有效。对于库代码没有作用。

一个办法,使用靓态编译,但是只把需要代码拉回来,并且没有解决合并的问题。 并且完全静态编译的话,空间浪费又比较大。一个折中方案,那就是静动混合编译。 只对需要的函数静态编译。 即使是这样,还是一些额外的工作,例如把目标函数单独一个module, 虽然对这个module进行静态编译。

  1. 有源码情况下

    gcc foo.c -Wl,-Bstatic -lbar -lbaz -lqux -Wl,-Bdynamic -lcorge -o foo.exe
    http://stackoverflow.com/questions/2954387/can-i-mix-static-and-shared-object-libraries-when-linking
    
  2. 没有源码情况下,可以直接发动态链接转化静态链接 http://stackoverflow.com/questions/271089/how-to-statically-link-an-existing-linux-executable 这个也是一些虚拟函数docker的实现原理之一。例如virtual box,chroot.

当然在编译的时候每一个函数编译成一个section,然后直接把那些需要直接抽出来。这也是静态链接的原理。

但还不是那么灵活,我想在任意的边界下实现最优化。 在什么样的范围内寻找最优解。 例如. all { a(); b(); c(); } 我们在什么样水平上优化,是在all中,把a,b,c放在一起来优化。 还是在a,b,c这级来进行优化。 并且保证其对等。 函数大小基本约束了编译器的优化边界。多大的函数最合适呢,这个根据最体问题来的。主要是根据profiling的结果来的,例如一个函数cost很高,但是其内部调用很多的小函数,但是每一个小函数cost也都不是很高。 这就说明这个函数内部的overhead太高。 要把这些overhead给去掉,就在在这个大函数为边界来优化

可以代码转化到LLVM中,然后再其中添加inline然后再优化。

函数优化基础是CFG的建立,CFG的建立,就是如何产生基本块。利用拓扑学的domintor树理论,生成基本块。 在CFG中,如果A是B的dominator,则从程序入口执行到B的任意路径一定经过A。 在指令这一层,在函数这一层。

每一个块的中指令都具有相同执行次数。

而CFG的化简,在控制流图化简 在复杂度相同的情况下,CFG的规模影响算法的效果。如果一个CFG仅通过如下变换能化简为一个节点,则它是可化简的: 如果节点n有唯一的前驱,那么将其和其前驱合并为一个节点 如果节点存在到自身的边,那么将该边删除 构造SSA SSA可以由CFG构造。

而CFG是控制依赖分析的基础。 https://www.zhihu.com/question/23269416/answer/43461637

DFA

Data flow analysis,变量的生命周期,指的就是在多少指令空间存活,有了CFG之后,就可以以基本块为单位来测量生命长度。 然后把CFG用符号化表示,然后在符号化之上再往前走一步,就实现了机器人理解代码的水平。 而见的DFA的分析有Constant Propagation,Range propagation, Reaching Definition,这个就是大家喜闻乐见的跳转定义处。的实现方式。 https://www.zhihu.com/question/23269416/answer/43461637

KLEE

测试就是用一种逻辑来测试另一种逻辑。其本质那就是从两条路得到的同样的结果才能保证的结果的正确性。

符号计算可以用到测试之中,可以用形式逻辑来理解代码的逻辑,利用代码块的拓扑结构 来理解代码,来自动的生成测试代码。

symbols execution,另一方面也就是形式计算。

而KLEE就是这种逻辑,利用符号执行来建立自己逻辑模型,然后再利用数学结构来进行验证。

静态分析 < 符号执行 < 动态执行。

为代码本身建立逻辑模型,最简单的那就是模式匹配,这是大部分的静态扫描所采用模式。

动态执行,纯手工测试,覆盖率太低。虽然也有自动化,但也只是基于手工测式的,生产率不高。

想要快速的发现程序的bug,就得能够自动理解程序逻辑本身,然后建立数学模型来进行推理验证。

基本框架

C code ->LLVM bitcode -> klee ->stp clauses -> klee ->output. KLEE 由于使用STP对于符点数是不支持的。

KLEE 本质就是利用符号计算,来实现路径的搜索。每遇到一个分支就fork出一个状态。 这样每一个分支就独立计算下去,也就是每一个state都保存下去,放在队列里,等待轮询。而EXE采用的方式是利用fork进程来执行,每遇到分支就fork出新的进程来执行。 直接到状态结构或者timeout。

你需要指出的是迭代timeout时间,

符号包括,变量,常量,以及LLVM内部的结点。

而KLEE 采用是STP 模型。

  1. State scheduling, 采用的随机路径选择。 或者基于覆盖度的优化搜索。

而外部环境建模也基本上采用inject方式对API进行HOOK来实现。也对部分API进行穿透。 同时HOOK API来人为创造例如硬盘full的事件。

KLEE only checks for low-level errors and violations of user-level assets.

路径搜索就会遇到 path explosion 问题,就会有各种搜索算法。

code-coverage的计算

在编译的时候就要生成 -fprofile-arcs -ftest-coverage 在编译选项中。 而产生路径分析的原理,基本块的定义,如果一段程序的第一条语句被执行过一次,这段程序中每一个都要执行一次,称为基本块,一个BB中所有语句的执行次数一定是相同的。 跳转ARC就是从一个基本快跳转到另一个基本块。

在LLVM中有一个sancov的工具来计算,函数级别的,指令级别的,基本块级别的。 clang.llvm.org/docs/SanitizerCoverage.html

约束方程的解,把各种溢出的判断变为一个约束方程组的判别,然后就可以利用各种约束 solver来求解。这样就可以实时计算与符号执行同步进行,一步步进行溢出检测,相当于在instrument 这一级别的实时检测: https://www.google.com/patents/CN103399780A?cl=zh

这里关键是有一个完备的中间指令集。而LLVM以及微软的IVL等等。

状态的优化
  1. expression rewriting
  2. Constraint Set Simplification

#. Implied Value Concretization #.

也可以版本控制的log来指出最容易出错地方。例如文件被改动的次数。 http://google-engtools.blogspot.com/2011/12/bug-prediction-at-google.html https://github.com/igrigorik/bugspots

一个在线试验 http://klee.doc.ic.ac.uk/#

EXE: Automatically Generating Inputs of Death

http://web.stanford.edu/~engler/exe-ccs-06.pdf

exe 是KLEE的前身,其主要做对STP的优化。 KLEE 的核心是符号计算,来实现等介计算。变成一个约束问题。

关于符号计算分析 都在往前发展,微软在这方面做的比较靠前。 https://www.microsoft.com/en-us/research/people/leonardo/?from=http%3A%2F%2Fresearch.microsoft.com%2Fen-us%2Fum%2Fpeople%2Fleonardo%2Ffmcad06.pdf

并且在编译soup时,会自动把klee给编译好,而不需要自己再去找依赖。

Build for Android
  1. make standlone toolchain

    NDK_ROOT/build/tools/make-standalone-toolchain.sh \
    --platform=android-21 \
    --toolchain=x86-4.9 \
    --install-dir=$HOME/Toolchains/x86-21
    
    NDK_ROOT/build/tools/make-standalone-toolchain.sh \
    --platform=android-21 \
    --toolchain=x86_64-4.9 \
    --install-dir=$HOME/Toolchains/x86_64-21
    
    NDK_ROOT/build/tools/make-standalone-toolchain.sh \
    --platform=android-21 \
    --toolchain=aarch64-linux-android-4.9 \
    --install-dir=$HOME/Toolchains/aarch64-21
    
    NDK_ROOT/build/tools/make-standalone-toolchain.sh \
    --platform=android-21 \
    --toolchain=arm-linux-androideabi-4.9 \
    --install-dir=$HOME/Toolchains/arm-21
    

LLVM IR 的核心是SSA-based(Static Single Assignment) representation. 为了实现这一目的,采用加下标的树做法,第一次用 var,以后就 XX.1 XX.2来标识。需要合并的的时候,phi,或者select来进行。 当然都是简单变量能够实现这些,而对于复杂结构体,可好像也是无能为力。 Commdat 主要于指示如何链接,例如ELF,COFF文件的哪些段,如何寻找那些在本module中没有定义的symbol.

另一个好像那就是IR自身信息的完备集,这一点不是很一个编程语言能做到的很好的。

通过 llvm.o 文件结构

  1. type定义
  2. 外部链接 comdat的定义
  3. 常量的定义
  4. 函数定义
  5. attributes
  6. meta 定义
LLVM 代码单位
  1. 指令集
  2. 函数集
  3. BB 块集

最后,BB块,放在ELF的某一个section,然后,就看symbols只是只要本section找,还是整个ELF空间找,并且查找到策略。 都是comdats 来指定的。

并且 comdats 的指令在直接分析opt的时候,是报错,找不到定义的。 优化时候,也是以基本单位来进行的。 采用是match/replace的方式,相当于原址替换。

所以优化是可以叠加的,但顺序不同,可能效果会不同。 这里解决前向的声明。 用于欺骗compiler.来满足定义在前的基本要求。

Well-Formedness

指的就是满足SSA,就是Well-formedness.

内存操作

Alloca 分配栈空间, malloc用于分配堆空间。 load,store read,write fence. cast,getelementptr 用于取结构体的子元素。 http://www.lai18.com/content/7919787.html

另一个是对原子操作的支持 cmpxchg,atomicrwmv,

把一些

dbg

需要产生dbg的信息的地方,都直接调用 llvm.dbg.declare来实现了。具体信息都放在 !dbg data中。

typecast

bitcast

phi值的应用

主要是用来解决类如

<result> = phi <ty> [<val0,<label0],….

a = 1;
if (v <10)
    a = 2;
b =a;
b = PHI(a1,a2);

使其满足SSA的约束。 有点类似于 switch case的功能。

select

有点array中,下标操作,例如一个下标数组,只把取下标为true时的数据。 select <10 x i1> [0,1,0,0,…] i32 1,i32 9,i3…. 这个就是选i32 9了。

位操作

对于小于一个字节的单位表示, i1 表示一位,也就是bool类型。 经常在br 看到它,它用来表示类型。

ICMP
  1. eq equal
  2. ne not equal
  3. ugt unsigned greater than
  4. uge
  5. ult
  6. ule
  7. sgt
  8. sge
  9. slt
  10. sle
attribute

娈量,以及函数都可以有属性,并且这些属性还可以分组。

meta

也有大量的定义,例如DIFile来代表文件。并且支持BasicType,也支持SubrontineType,以及DerivedType.为了节省空间,支持相互引用。

blockaddress

这个就有点GOT的意思了,在哪一个module的中哪一函数。用于形成GOT,PLT的表的内容。

函数调用

正常的函数调用,就call,异常处理就用到invoke与unwind了。 invoke指令指定在栈展的过程必须要执行的代码。 unwind指令用于抛出异常代码并执行栈的展开的操作。 http://www.lai18.com/content/7919787.html

目标
  1. 建立起自己工具套件。
  2. 写出自己的逻辑

非常好用的sandbox, https://wandbox.org 可以试验各种编译器。

先进编译技术研究与发展

程序分析技术–发现程序并行性 芯片上的指令级并行和多线程并行编译技术 共享存储多处理上,发掘循环级和任务集并行的编译技术 分布式存储并行机上,解决代码和数据的分割 通讯优化和代码生成问题 在复杂的多级存储体系结构中,为是得到高性能,局部优化和预取技术必须与并行化技术结合。

LLVM的流程

IR这是LLVM核心, LLVM为了保证与现有编译系统,可以采用前端工具例如C/C++/python变成IR. 然后经过llvm自己的优化。 然后还可以自己的bc 字节码,然后用bc生成汇编,然后再gcc来生成binary. 但是现在已经有CLang来解决GCC的toolchain了。 IR能够实现自包含,从指令,到函数,再到代码块。并且把整编译chain 模块化,抽象化。 并且变成类库,方便每个人可以 根据自身的需求进行定制。

opt 就是一个optimizer manager, 你可以指定哪些pass要用,并且什么时候用。同时可以做全时优化。所谓的优化就是把根据 资源的特征来进行正确的分配。但是并不是所有的资源信息都是在编译时就能知道的。有些信息是链接时才能获取的。 有些信息只有在安装时才能获取的。有些信息是根据只有在运行时,才能获取的。

LLVM怎么做到这一点呢,LLVM把自己.o格式代码写在ELF中,当程序运行时,退到LLVM代码自动进行编译优化。同时可以也可以能够把 需要用到优化器抽取到应用程序中,让应用程序自身能够不断的优化。

LLVM 能够实现自动profiling优化,进一个目标那就是代码的自动演化。自我演化的关键是我自完备。 http://www.aosabook.org/en/llvm.html 代码生成的模块做的还不是很好,还在发展。

  1. Frontend 语法检查
  2. Optimizer
  3. Backend - instruction selection, - register allocation - instruction scheduling. tbl的用途,就是用来生成目标代码的。
digraph llvm {
 nodesep=0.8;
 node [fontname="bitStream Vera Sans",fontsize=8,shape="record"]
 edge [fontsize=8,arrowhead="empty"]
 rankdir=LR;
 XXX_Language-> LLVM_IR->TargetLanguange;
 LLVM [
     label="{LLVM PASS  | \
             ModulePass | \
                     CallGraphSCCPass | \
                     FunctionPass | \
                     LoopPass | \
                     RegionPass | \
                     BasicBlockPass}"
 ]
}

现在对于编译的应用才有了更深的认识,主要过程输入语言变成,变成中间语言,然后进行各种优化,然后再生成目标语言。例如为自己FPGA片子生成一个可执行代码了。利用LLVM/gcc就可以直接做到。 在生成汇编指令的时候,这里有一个中间RTL(register transfer Language),通过这个可以快速为一个片子生成不同目标程序。 gcc for arm,x86等等主要就是通过改写RTL来实现。 至于Register 如何分配,可以通过解整数方程来获得。以及求解来多项式来进行化简。以及生成一个语法树来进行化简。

编译的过程也是也是资源收集与分配的过程。高级语言主要用来收集计算需求,而低层语言以及硬件能力则体现的是硬件能力。而如何两者达到最大的match,则是中间翻译与优化目标。而这个功能可以由 LLVM来提供。 而传统编译器只是起到了翻译的过程。没并且起到一个资源分配的过程。而这个过程是要靠profiling来解决的。但是这些是可以利用contract编程以及测试来得到每一个函数使用的资源数的。 例如NEON的使用,如果完成采用手工写代码的速度太慢。 而是应该像CLOOG这样,输入计算模型与计算量生成执行代码。 例如protobuf一样的工具,来来大大加速你的编程过程。

通过数据依赖的分析,以及NEON本身的计算能力,实现一个NEON的代码生成模型。

编译指令的参数修改也是资源分配的修改。

ANTR+LLVM 更好的实现,利用ANTR实现前端实现到LLVM IR然后用LLVM往后编译。 #. 编译点滴 LLVM 的关注 #. llvm 编译技术最新发展方向 #. SSA Static_single_assignment_form ,在`这里 <http://www.lingcc.com/2011/08/13/11685/#more-11685>`_ 讲解,主要是对于变量的赋值只有一个次。这个类似于GTL中做法对于文件只写一次,然后就是读的工作量。对于以后每一次读进行版本控制。对于分支定义新的变量。这样变量的值改变就有了版本控制的作用。这样就可以精确的进行数据流分析。以及各种依赖分析。并且很容易跟踪版本的改变。这样就大大简化了后面的优化算法。因为所有的东西都具有了GUID,也就是全局唯一性。很方便跟踪定位。 #. llvm大事记 通过这篇文章看到,目前llvm性能还是不很好,但是架构清晰明了。前途很好。同时也可以看看编译技术的最前沿的论文。 #. LLVM 与ANTLR 一般编译器分两部分,前端与后端,前端进行进行语法分析,建立符号表与中间代码。后端来根据这些信息进行优化,例如画出流程调用图。在写解释新的语言时一个偷懒的做法,前端把处理成已知语言的中间代码,然后利用现有的编译器进行后端的处理。例如cfront是把c++变成c的中间代码。然后利用c的后端来进行操作的。

由于我们可以将字节码附在可执行文件中,所以也就保留了高层次的信息以便后面阶段的再优化。

如何使用 clang

使用环境变量,CC,CXX,CFLAG,CXXFLAG。 https://stackoverflow.com/questions/7031126/switching-between-gcc-and-clang-llvm-using-cmake

See also
Thinking

用图论来化简 利用图论来分析数据结构以及流程path都可以用图来化简,主要也就是最短路径,避免循环等等方法。图论是化简最有效的方法。

– Main.GangweiLi - 19 Jun 2014

LLVM 优势就在于保留了所有信息,指令像底层的汇编,但保存了上层类层信息,类层信息,采用了C的语法结构。没有面对象对象结构,这也是为什么C语言的转换效率最高的原因,可以用C本身就具有这样的性质,把语句规范一下,就可像C汇编了。然后把变量看成寄存器,就是汇编了,就像所谓的Low Level virtual Set. 而原来的汇编指令是没有类型信息的,例如逻辑与算术运算,数据的传递指令。而没有存储空间的分配指令。而LLVM提供这个malloc,alloca的功能,把堆与栈的指令。然后根据指令使用情况对各种存储空间使用情况,来分配硬件资源。当所有有指令都变成算数运算与逻辑运算,再加上跳转指令,就相当于最基本的代数运算,相当于先化简,然后再求值。 化简就是编译,而求值就是运行。不化简直接求值,那就是代数中不化简直接代数求值,是傻X的作法。如何化简呢。 现在可以方法都已经用了2002-12-LattnerMSThesis-book_fin.pdf已经介绍了。先是局部化简然后再整体化简。局部化简的过程就是每一个文件的编译,并化简,然后link的时候再进一步化简,这时候整体化简,并且在运行时,还可以实时动态化简。可以这些PM数据保存下来,做进一步优化的输入数据。因为运行时的情况是千变万化的。

让进程像一样具有一定自主性,而优化算法可以是共享。每一个应用程序规定一下自己特征,PM过滤器采集哪些系统信息与自身信息,优化算法过滤器,本程序本身采用会哪用哪些优化算法。所以当进程闲的时候把开始自己做优化。其实就有点像GC的功能。 因为LLVM代码自身会存一会的,并且LLVM的代码会三种形式,文本形式,二进制形式,以及内存形式。三者是对应。而不像一般的汇编三者独立的。LLVM的指令集是可以在LLVM虚拟机跑的。并且自动保存了大量debug信息,方便调试。

– Main.GangweiLi - 20 Jun 2014

只要自己的语言到LLVM就可以在任意的机器像本地一样的速度去跑了。

– Main.GangweiLi - 20 Jun 2014

LLVM是一个闭包空间 可以不断的化简优化。opt-3.0 来指定各种化简。U:/project/LLVM/paper/02-Compiler-LLVM.pdf 非常简明的教程,只要把opt 变成opt-3.0就一切OK了。

– Main.GangweiLi - 20 Jun 2014

寄存器的分配 对于非常短的代码,完全可以在寄存器中操作,而非是一个标准流程,只要是函数,只要声明变量,就在内存中申请一块空间,然后在ldr进来,然后计算,然后存回去,浪费不少指令。小函数的局部变量完全没有必要申请内存空间。直接在寄存器上操作就行了。

优化方向

优化原则会限制代码规则的。出现异常的时候,一般都是代码使用规则是随意的与优化规则冲突了。gcc-strict-aliasing

用gcc来进行测试

完全用手工的方式去测试是低效的。但是测试与开发分开的话,确实只能这样的,但是让开发自己做呢,就可以大大的利用编译器与debug来进行测试。并且来提高效率。

例如用https://xpapad.wordpress.com/2009/05/18/debugging-and-profiling-your-cc-programs-using-free-software/ -Wall,来进行所有warning进检查。 -O2 进行没有初始化变量以及数组越界的检查。

-Wshadow 来检查重名的函数的应用范围。 -pg 会生成一个 gmon.out 可以让gprof来分析的。

寄存器的分配方法

其实就是一个解整数方程组的过程,以及多面体的问题,可以从http://cloog.org/ 来看到。从扫描多面体生成能达到每个顶点代码。自动编写loop. 但是解决一维线性方程组的整数解。

自动添加代码

-finstruction-function with __cyg_profile_func, 同时注意 添加 __attribute__((no_instrument_function)).

https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#Instrumentation-Options https://mcuoneclipse.com/2015/04/04/poor-mans-trace-free-of-charge-function-entryexit-trace-with-gnu-tools/ https://mcuoneclipse.com/2015/04/04/poor-mans-trace-free-of-charge-function-entryexit-trace-with-gnu-tools/ 这个功能在clang中同样支持http://wengsht.github.io/2014/03/16/Function+Tracer+Using+clang+++–+application+and+principle.html

对Clang中还可以这样

-ftrap-function=[name] http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation http://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization

debugging Options
JIT

每一种JIT都会对应一种计算对象模型,如果你的计算模型与之相差很远,自然优化的效果也不会好。

GCC很难当做lib来复用。

当然可以直接使用gcc 的python扩展来进行测试。 直接写测试用例,来进行测试。 当然这个也需要一些线程注入的技巧

也可以用LLVM来直接发改写代码,例如生成函数用LLVMAPI, 主要就是生成一个module然后连接一些block. 并且用API生成语句。 http://releases.llvm.org/2.6/docs/tutorial/JITTutorial2.html

优化的过程
  1. Look for a pattern to be transformed.
  2. Verify that the transformation is safe/correct for the matched instance.
  3. Do the transformation, updating the code.
clang

支持gcc 的流程, -E,-c 等等。 同时还有 -emit-ast,-emit-llvm

clang 同gcc 一样,是一个前端,同时自己实现了一个AST把C代码生成 LLVM IR。然后再IR上进行各种优化 然后再用ABI生成对应用平台binary.或者汇编代码,然后再成binary.

同时可以可以通过命令行参数 -fxxsanitize-xx=xxxx,xxxx来控制优化。并且还有blacklist的机制。

如何做优化

  1. 通过gcc一样的参数控制

  2. 直接生中间过程,然后管道传输了给opt了。 lvm-as < /dev/null | opt -O3 -disable-output -debug-pass=Arguments http://stackoverflow.com/questions/15548023/clang-optimization-levels

    http://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization

JIT

想在自己的应用程序中使用JIT也可以直接使用了LLVM来实现。 https://pauladamsmith.com/blog/2015/01/how-to-get-started-with-llvm-c-api.html

主要过程就是创建一个Module,然后添加变量函数。再创建编译环境。 Module->Function->Block->Instruction. 当然通过API是可以看到IR的所有信息的。

当然自己在实现代码的时候,可以写一个AST来生成IR,也可以直接生成IR来做算法分析。

例如python来说,从4.0之后,llvm有自己python api wraper. 或者使用llvmlite,llvmpy,但是版本依赖很严重,要严格版本对应。 http://llvmlite.pydata.org/en/latest/install/index.html https://llvmlite.readthedocs.io/en/latest/

自己手工实现pass

http://llvm.org/docs/WritingAnLLVMPass.html#multithreaded-llvm 具体每个数据结构,就可以看例子。 https://www.cl.cam.ac.uk/teaching/1314/L25/4LLVMIRandTransformPipeline.pdf 主要是结承各个类,然后实现相应的虚函数。

IR结构

http://llvm.org/docs/LangRef.html#introduction 语言设计本身要具有完备性,它会结合高级语言,汇编语言以及ABI,ELF标准来定义。

把汇编label提升到函数。

  1. comdat 其实就是直接操作ELF,来分配 data-section.

特别之处,那就IR还有各种attribute,parameter本身有,函数也有。 另外还有metadata,可以用来存储额外的东东。 这样方便进行一步优化。

变量

分为全局变量与局部变量,还有临时变量,并且采用SSA的分析变量的用途。对于全局变量用comdat方式操作ELF的data-section进行。 也就是申请资源。 而于寄存器,分配还要化简

函数

prefix data, 是不是可当于 function static 变量 另外那就是数据对齐填充。 prologueData,用enabling function hot-pathing and instrumentation. 这个正是自己想要功能。

PersonalityFunction,用于exception handle.

  1. Attribute Groups, 可以后attribute合并分组,当然是一个module范围内。

Function Attributes, 主要是 #. noinline, alwaysinline, optize,cold,”patchable-function”,readonly

Funclet Operand Bundles,相当于闭包运算了。

Data Layout, 来规定不同平台的数据定义, 相当于C语言的种 typedef short int SUINT Target Triple,描述主机信息 Pointer Aliasing Rules,指针的用法 Memory Model for Concurrent Operations

Use-list Order directives 相关指令的关系。有点NEON的味道。

如何计算两个函数的相似度,利用IR来生成符号,充分利用符号替换来解决变量名的区别。 从函数入口直接把所有变量替换成中间变量。这样只剩下形式与指令顺序的问题。 http://llvm.org/docs/MergeFunctions.html 这样找到相同函数,就像可以替换。

利用相同的思路把找到最长常匹配块,split一个大的函数成多个小的函数。然后再编译的时候再用inline,这样即解决了模块化,又解决了效率的问题。

Type System

IR 是类型安全的语言。 指针还是*表示, Vector <4 x i32> Vector of 4 32-bit integer values.

Array Type: 类似C语言的数组,支持embeded 结构。 Structure Type: C的结构体 Opaque Structure, 相当于 C nontion of a foward declared structure. 相当于符号推导中符号。

Constants, Complex Constants

Global Variable and Function Address.

Undef values, Poison Values, 相当于

Addresses of Basic BLocks, 相当于GOT,PLT的功能。

指针是什么,就是申请资源时的,资源的url. 用到指针,就要资源的分配。

还有一些特征编译单元指令 DICompileUNit/DIFile/DISubgrance/DIEnumerator/DILocalVariable/DILocation./DIExpression. #. DIExpression nodes 来表示 DWARF expression sequences. 基本上LLVM采用图论的方式来进行优化。这些都相当于是一个node.

invoke

相当于goto 对于exception处理以及状态机来使用。

各种指令 <result> = shl <ty> <op1> <op2>

LLVM 这个原语树与Theano 的图的方式应该差不多。

Super Optimizer

让每个应用程序自主的优化,现在已经有人开始实现,现在叫Supper Optimizer.

让进程像一样具有一定自主性,而优化算法可以是共享。每一个应用程序规定一下自己特征,PM过滤器采集哪些系统信息与自身信息,优化算法过滤器,本程序本身采用会哪用哪些优化算法。所以当进程闲的时候把开始自己做优化。其实就有点像GC的功能。 因为LLVM IR 可以存有大量的MetaData 来做这些事情。

llc

可以用于生成目标机器码,同时还能生成反向的cpp 代码。 http://richardustc.github.io/2013-07-07-2013-07-07-llc-cpp-backend.html llc -march=cpp test.o / llc -march=cpp test.s 相当于反向工程了。

lli

虚拟机,直接运行llvm bytecode

Transform

这些pass为什么,可能由于代码的不规范,所以需要正则化。 更加便于分析。同时做一些初级的分析。化简也是变型一种。 本质就是一种是analyze另一种那就是transform.

LLVM 当前的问题
  1. wide abstraction gap between source and LLVM IR
  2. IR isn’t suitable for source-level analysis
  3. CFG lacks fidelity
  4. CFG is off the hot path
  5. Duplicated effort in CFG and IR lowering

并且SWIFT在LLVM实现一个SIL,同时加强了IR这些功能。

当然LLVM也有自己的限制,首先语言相关的优化只能在编译前端实现,也就是生成LLVM code之前。LLVM不能直接表示语言相关的类型和特性,例如C++的类或者继承体系是用结构体模拟出来的,虚表是通过一个大的全局列表模拟的。另外需要复杂运行时系统的语言,例如Java,是否能够从LLVM中获益还是一个问题。在这篇文章中,Lattner提到,他们正在研究将Java或者CLI构建在LLVM上的可行性。 新想法的诞生从来都不是一夜之间出现的,一定是掌握了足够多的知识,在不同问题的比较和知识碰撞中获得灵感,然后像一个襁褓中的婴儿一样缓步前进的。当然现在LLVM还存在很多问题,特别是跟应用很多年的工业级的编译器在某些方面还有差距,但是差距正在逐步缩小,附一篇Open64开发人员对LLVM的看法《Open64业内外人士对LLVM和Open64的观点》。

SSA的基础

各种各样的编译层出不穷,例如QBE号10%代码达到LLVM70%的功能,主要是基于SSA来做的, #. SSA 形式的构造本就是复写传播(copy propagation). #. SCCP (sparse condition constant propagation), SSA 形式上最经典的数据流分析与优化分析 之一。

面向局部性和并行优化的循环分块技术

局部性,意味着可以利用cache,如何自控制局部分块,并且充分利用多级cache来提高效率呢。 不只是简单的减少if else以及switch代码的问题。 调用本身也有很大的overhead,所以大对于大的循环来说,循环展开来减少overhead来提得高效率。

scan-build

http://clang-analyzer.llvm.org/scan-build.html 静态分析工具,直接分析代码。

直接在编译命令之前加上scan-build 通过改变一些环境变量与编译的参数来实现相关的检查。

有了符号计算之后,就可以变换模式的匹配了。

寄存器的分配

https://www.zhihu.com/question/29355187 这里有全面的总结了。 #. Expression tree #. Local (basic block) #. Loop #. Global(routine) #. Interprocedural 也就是资源与需求的搓合机制,有点类似于股票交易是一样的。主要是变量的生命周期的计算以及使用频度的计算。

变量的生命周期而是根据指令长度来算,现在常规的算法,直接从前往向后编号。这样是不对的。而是用二叉树,多维空间的表示。 指令周期模型,用拓扑结构更有效,而不是简单线性模型。

资源是有限的,寄存器放不下的变量,就像放进内存里了。 现在常用模型有线性模型,以及图着色模型,以及矩阵填充模型。主要是循环区域寄存器分配更重要。要寄存器分配队列与spill队列。 一种基于分区域优先级的寄存器分配算法.pdf

生命密度: 生命域的溢出权值 为生命域内变量定义和使用的数量除以生命域的长度。可以反遇溢出整个生命域的代价。 分区域方法是构造一个矩阵,对于小的循环,就意味着分块。

但是各种算法最后都变成一个计算问题。而计算本身还有一个P与NP的问题。

当然可以在代码中直接指定寄存器分配 ,例如在C语言中是有register这样的变量类型的。 同时在嵌入式开发中,也经常是变量与寄存器可以直接mapping的。

并且http://compilers.cs.ucla.edu/fernando/projects/puzzles/ 模型用来解决寄存器分配问题。

线性扫描主要是变量生存周期问题。 并且指令添slot模型来改进线性扫描。 http://llvm.org/ProjectsWithLLVM/2004-Fall-CS426-LS.pdf

寄存器分配的难点在于变量的生命周期,以及分时复用的问题。 http://blog.csdn.net/wuhui_gdnt/article/details/51800101

代码生成

https://github.com/wuye9036/ChsLLVMDocs/blob/master/CodeGen.md,是代生生成框架简述。 函数生成过程,先生成中间,然后再生成首尾的连接工作,就像IP包的构造一样。

内存管理

各种内存对齐是为利用cache,高效,但是为默认的struct没有办法重排呢,主要是其解读方式决定。如果像protobuf就可以这样干。

利用拓扑分析来判别离散与连续的数据结构及操作。 也就是lists,trees, heaps,graphs,hash tables,等等的可视化来进行优化。 以及对这些结构存取进行profiling就可以得到很好内存管理模型。这样就可以编译的时候就进行优化。 例如结构体重排,Automatic Pool Allocation. 而这个的分析就是要对cast, getmemeryptr,以及alloc,free等使用pattern的分析得到的。主要是对指针的分析使用。 显示内存分配和统一内存模型。LLVM提供特定类型的内存分配,可以使用malloc指令在堆上分配一个或多个同一类型的内存对象,free指令用来释放malloc分配的内存(和C语言中的内存分配类似)。另外提供了alloca指令用于在栈上分配内存对象(通常指局部变量,只是显示表示而已),用alloca来表示局部变量在栈帧上的分配,当然通过alloca分配的变量在函数结尾会自动释放的。

其实这样做是有好处,统一内存模型,所有能够取地址的对象(也就是左值)都必须显示分配。这就解释了为什么局部变量也要使用alloca来显示分配。没有隐式地手段来获取内存地址,这就简化了关于内存的分析。 用拓扑结构来分析具有天然的结构,例如点就是节点,线就是link,拓扑结构就代表了存储结构。 LLVM也可以将局部结构体对象或者列表映射到寄存器上,用于构造LLVM IR所要求的SSA形式。这一块我感觉应该是比较难的一块,编译器对structure或者说是memory layout的优化都是很难的一块

-targetdata,globalsmodref,Exhaustive-Alias-Analysis-Precission-Evaluator, memory-dependency analysis.

Structure peeling,structure splitting and field recorder. struct-array copy/inlining instance interleaving Array remapping https://gcc.gnu.org/wiki/cauldron2015?action=AttachFile&do=view&target=Olga%20Golovanevsky_%20Memory%20Layout%20Optimizations%20of%20Structures%20and%20Objects.pdf

LLVM的好处有自己独立的类型系统,通过对cast,getElementPtr就可以来分析内存的结构与利用率了。LLVM 包含基本的数据类型(void,bool.signed/unsigned,doboule,floating,int),并且长度从8bit到64bit,同时还有四种复杂的类型,pointer,array,structures,functions. 而c++的继承可以用结构体的嵌套来实现。

结构体重排

padding,alignment,point compression. 可以节省空间,提高cache的利用率。一个大的结构体按照使用频率,切成小结构体,并且放在cache里。

https://docs.google.com/viewer?url=http://users.ece.cmu.edu/~schen1/cs745/paper_pres.ppt

change data structure. structure splitting field reordering

而这些都是可以拓扑学来分析。 符号化之后,就像成点,关系就是线,面就是集合与组。 点线面关系也就构成拓扑学。

另外也可以用元编程来实现结构体的重排,我们在写struct的时候,并没有在意其内存结构了,只是注意其逻辑结构了。 只要在增加一层,再增加一层重排,这样就可以保证正确的结构了,而非一个随意的结构。相当于我们只是描述数据结构的需求。 而销定实现。

利用图论方法来对链表进行分析,然后用内存池的方式来进行优化,一般对于链表结构都是手工方式进行内存池的方式优化。 根据指针的类型与数据结构本身的关系来建立拓扑图。http://research.microsoft.com/en-us/um/people/trishulc/msp2002/mcd_msp02/adve_newpaper.pdf 并且根据每一段数据使用频率以及cache的cost建立相应的内存池以及结构体的重排。

基于当前的水平,DSA与automatic Pooling是技术发展的方向。http://llvm.org/pubs/2005-05-04-LattnerPHDThesis.pdf 函数摘要信息 procedure summary ==============================

好的摘要信息,可以直接使用摘要进行过程间分析,相当于增量编译了。

指令的优化

例如用一个复杂指令来代替长序列的指令。 窥孔 就是相邻两条开始,不断的加大窗口,但是首先还先找到原来指令集的等价关系。

如果硬件支持指令集并行,例如程序与数据空间是分开就可以充分软件流水线来实现ILP(instruction level Paralleism).

指令的分配与寄存器的分配是交叉的。

采用人工智能的方式来进行编译。

统计编译,首先要知道哪些指令要多少次,然后根据指令时间长短不一样,来解决采用哪一种方式,并且像GPU这样的对于寄存器的读写是有要求的,写完之后24周期之后才能读,这样的话,就要改变指令了。例如在这读写之间可以执行其他的命令。 所以在实现一个函数或者说明的时候,要指令说明这个函数的使用模式,例如函数是多次使用,还是一次使用,多次使用,其实也算是算法的性能维度分析。这个就是保证优化的模型。 基于theano的生成方法来来试验一下。 当对于同一个算法有多种实现的时候,就可以这么干了。 例如排序,对于你的调用就是排序,至是如何排交给算法工程师去解决了。然后直接优化在代码里。 其实就像cuda里的kernel,launch一样。函数里直接加上性能参数。 直接拿python来的sort来做一个实验。 或者theano来做。 这个与现在OPENACC有什么本质区别呢,是不是OPENACC之类正在做的事情。

Kepler 简化指令信赖关系就是靠编译器来做的,只要你事先要告诉编译器指令信赖关系,就可以用分析优化,例如读写指令是有latency的,例如 c=a+b;e=d+e; h=i+j;与c=a+b;h=i+j;e=d+e;效率是一样吗,一个关键因素那就是寄存器的latency了。有如果比较大,后者效率会比较高,利用了latency,但是可读性差。

其中一个重要问题,那就是如何隐藏latency的问题。

strict alias-rule

http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule, 就是不请允许用两种不同类型指针指到同一块地址上。这样会引起分析失效。

CFG优化

用图论. 如果A只有一个子,那就应该合并。从而减少调用overhead也就是直接inline.

加上context,这样就有利于编译的优化,如何定义context,以及自动识别这些呢。 是不是可以神经网络呢,提高 pattern的识别率呢,神经网络拓扑结构与CFG对比使用。 生志CFG并且合并且 dominator,同时根据回路算法复杂度分析。

同时根据CFG生成一个最小的程序切片来复现问题,其实就像KLEE中根据依赖生成路径。 根据关注点的不同,实现一个最小的可执行代码切片。一般在控制流图上根据数据依赖及控制依赖关系,采用不动点迭代求解

化简CFG就是要删除那些无效的符号,CFG有两种形式CNF,GNF,关键是产生式的形式的区别。 http://grid.cs.gsu.edu/~cscskp/Automata/cfl/node5.html 删除 Unit 产生式,以及空产生式。 http://www.tutorialspoint.com/automata_theory/cfg_simplification.htm

Clang 对于OMP 的支持还不是很好,https://stackoverflow.com/questions/33400462/omp-h-file-not-found-when-compiling-using-clang

How to Trace
  1. 生成callgraph,

#. 在各个边界点进行check,在不同的level api检查,并且在函数级别的,我们可以随时用各种 probe来实现,trace,而对于原码级,小于函数级别,就要用NVTX,或者log的功能实现。 其实把trace放在log中实现也是一种方式。 当然在函数级别的或者llvm IR级别,也是通过hook来实现一些东东的。

什么优算是最好的

在于信息的缺失与不确定性。 信息的缺失: 进行向下翻译以及transform的时候,会存在信息的流失,有的时候这个流失是我们需要的,有的却不是我们所期望的。 如果没有信息的缺失,有固定的方法来进行优化,那么让代码来做,比用手工做会更有效率。 同时在进行各种变形编译的时候,也产生信息的流失。哪些信息是下一层优化所需要的,哪些是多余的, 优化的要根据上下文来调整优化的顺序来保证 必要信息没有流失。 例如在汇编这一层,是没有内存分配这一书的,汇编指令是只有load,store读写操作,至于内存如何分配是由操作系统来决定的。

不确性: 有些信息本身不确定的,例如机器的配置信息,是在你写代码的时候,是未知的。为了保证正确性,我们采用的囚徒困境,采用了最基保守 的方法,自然就不可能最大化利用机器的资源。

所谓优化: 就是资源的优先级分配,就像下棋,什么时候,分给谁,多少的什么样的资源。 一方面弄明白算法的资源需求,另一个机器的资源配置。 然后实现二者的最佳匹配。

找到需求与限制的平衡点。 问题的编译技术是基于固定模式来的,也就是说按照语义编译的,还是字面的编译的。 #. 到底是资源不够,还是因为没有调度好。 #. 慢,是因为指令太多,还是因为因为在被block,这个就要API真实的执行时间,以及物理硬件的线速了之间的差异在哪里。 所以标准那就是要有每一个APICALL的执行时间上,最终都体现在时间上。那么标准时间如何而来。或者从大到小排序,然后直接以最小为基准。

在之前我们很能知道其极限在哪里。现在很容易了。

硬件极限是可以通过手册来得到。 代码的本身的极限我们可以用LLVM来的优化来达到。然后再生成目标的代码时候来实现二者平衡。

如果分析计算量的大小,就得建立数学模型,不然没有办法量化。原理的推导直接符号。但是计算量的分析来是数学公式来量化的。 另一种方法直接查看这种评测数据,得到业界的最新水平,一般纯代码的优化能够提高20%到10倍左右的提高。 超过了10倍的提高外在表现中就会有显著的变化。

优化的阶段

编译时优化,链接时优化,装载时优化,运行时优化,以及闲时优化

从算法最基本的入手

变量的分配就是意味内存的分配,如何使用cache与register. 一个最经典用法那就是SSA分析。

control flow analysis,data flow analysis,partial evaluation,static single assignment,global value numbering,liveness analysis. victorlization. 而这些的设计都要体现在LLVM 的IR中。

代码的实现就是一种资源的分配以及排兵部阵下棋一样,如何使用CPU等最基本的加减乘除等实现复杂的运算。加减乘除+与或非,以及基本指令。一套指令集就是一个完备集。

Control flow

CFG 的优化,主要是基于图论与拓扑,找到环路与边界。来进一步优化。 如何找到重复等价块,来实现函数,同时对于只有调用一次的函数。以及多次的代码是不是要把变成inline, 减少overhead. 以及由人写一个初始代码,然后算法进化最优代码结构,例如到底有多少需要重构的,模块化的。 然后再动优化,或者代码中加入一些宏指令,就像OPENACC那样来控制编译。

控制流图,每BB块,对应的原码块,可以看到依赖关系。并且有前向与后向的关系。 http://www.valleytalk.org/wp-content/uploads/2011/10/%E6%8E%A7%E5%88%B6%E6%B5%81%E5%88%86%E6%9E%90.pdf

循环

现在已经有一种数学模型,那就是多面体。可以采用数学的方式来进行优化。现有库有gcc用CLOOG。而LLVM,polly.来实现。 Polly可以用于各个阶段。 http://polly.llvm.org/docs/Architecture.html 在前端的话,优化后更接近原始代码,更容易理解,在后端可以充分利用其他优化的pass,但是没有可读性就比较差。

InstCombine

现在采用SMT理论来进行化简寻找等介合并来实现。现在最新的superoptimizer就是在这一块。

digraph OPT {
   Canonicalization -> Simplification->Loop_Opts->inliner->Simplifcation;
}
  1. Canonicalization
    • Mem2Reg
    • InstCombine
    • CFGSimplify
  2. Scalar Simplifcation - InstCombine - CFGSimplify
  3. Simple Loop Opts - Loop Rotate - Loop Unswitch - Loop Delete - Loop Unroll
  4. Target Specialization - Loop Vectorization - Loop Distribution - SLP Vectorization

同时再加上不断的Inliner.

优化本身的问题

出错了,到底是 优化器错了,还是我的代码错了。 who knows. 把优化过程可视化来帮助人们来快速的troubleshot. 所以才有种troubleshot工具,

  1. bugpoint,快速的二分法信息收集与repro工具。
  2. llvm-diff 来对比, llvm structual diff, 主要是函数定义的不同。
  3. llvm-mc llvm machine code playground, 相当于各种平台的分析器。
  4. llvm-stress 可以filter,各种function的IR code.
  5. obj2yaml/yaml2obj 相当于机器码的直接修改了,再不用各种麻烦的解析工作了。
  6. llvm-symbolizer convert address into source code location
  7. FileCheck 一个类似于awk/sed,检查所有check pattern都满足。
如何利用profiling data来优化编译

代码指令的读取也是pipline也有cache,跳转会破坏预读取的数据。现在我们可以根据profiling的结果来进行编译。

如何在代码中利用profiling的数据里,这个数据接口是__builtin_expect来读取。

if (__builtin_expect (x,0))
   foo ();
// -fprofile-arcs

原理 -fprofile-generate生成收集指令,并且生成*.gcda文件。 重新编译的时候 -fprofile-use 就会读取这些文件来生成条件语句。 -fprofile-arcs, -fprofile-values. -fbranch-probabilities,-fvpt,-funroll-loops, -fpeel-loops, -ftracer. http://stackoverflow.com/questions/13881292/gcc-profile-guided-optimization-pgoo 利用运行时信息来进行优化。如果这些信息存储在meta data中,这样LLVM中就可以实现自包含的优化,也就实现了自我的演化功能。

unloop

并不是所有循环展开是有效的,例如下面这种展开就是无效的,并且逻辑也可能是错误的因为两者并非是等价的。 这也是优化难的原因,因为transfor有可能并非完全等价的,优化的另一个步骤就是验证结果的有效性。

for(i=0;i<10;++i){
 if(something==3){
     do_something;
 }
 else{
     do_something_else;
 }
 unswitched loop(according to what I've been able to gather from the clang documentation(gcc's crap).

 if(something=3){
  for(i=0;i<10;++i){
     do_something;
 }
 else{
  for(i=0;i<10;++i){
    do_something_else
  }
 }
如何用LLVM从编译分析重构代码

ClangTool 的使用教程。 https://kevinaboos.wordpress.com/2013/07/23/clang-tutorial-part-ii-libtooling-example/

Superoptimizer

如何用SMT的理论,在一个更大的范围内找到一个等价的更小的表达式。 目前采用的布尔可满足理论来做这个事情。 计算量的多少,在数学上不同方法,计算量是不一样的。如何找到等价表达式。数学上的化简。 从CPU的计算来看,那就是一大堆加减乘除再加逻辑运算。 如何从这堆的计算序列中进行化简,来简化计算量。 同时是不是可以利用群,环,域的知识进行简化计算。 LLVM让优化又回到了数学

函数参要

输入输出类型,以及需要时间与空间复杂度公式就够了。 在编译时会汇总每个函数摘要信息(procedure summary),附在LLVM IR中,在链接时就无需重新从源码中获取信息,直接使用函数摘要进行过程间分析即可。这种技术大大缩短了增量编译的时间。函数摘要一直是过程间分析的重点,因为这种技术在不过分影响精确性的前提下,大大提高静态分析的效率。我的本科毕设就是关于改写Clang以支持简单的基于函数摘要的静态分析,研究生毕设题目《基于函数摘要的过程间静态分析技术》。 http://scc.qibebt.cas.cn/docs/optimization/VTune(TM)%20User’s%20Guide/mergedProjects/analyzer_ec/CG_HH/About_Function_Summary.htm

IPO/CMO

过程间分析,分析跨module函数调用,然后根据hotpath的程度,来考虑是不是需要inline,inline就消除了函数边界。同时又添加了 上下文,同时就又可以指针引用的分析了。 而在传统的情况下,这些分析是需要LTO来做的。但是通过FDO(Feedback Directed Optimizations).从profiling data中收集数据直接来做IPO,这样可以避免compiling time增加的问题。 https://gcc.gnu.org/wiki/LightweightIpo#LIPO_-_Profile_Feedback_Based_Lightweight_IPO

Target code optimization

每一代的CPU都会一些新的特性,如何充分利用这些特性,就要有相应的编译器的支持,由于编译器与CPU的发布并不是同步的。 所以要想充分利用这些特性,还得现有的编译器做一些修改,有些只是一个编译选项的修改,有些需要从源代码处直接修改。

例如pld指令在ARM中的应用: http://stackoverflow.com/questions/16032202/how-to-use-pld-instruction-in-arm

重构

重构是基于代码的分析,同时对算法需求本身理解,还有实现的理解。 而二者搓合匹配就是重构的过程。 如何编译器能够读懂算法。 并且支持基本设计模式,而这些都在C#语言中实现了很多,LINQ的实现,就属于这种。编译器往下代码的优化,往上走那就是重构。 例如微软的 roslyn-ctp

依赖的分析

对于简单标量分析,都已经有很成熟的理论与方法,而复杂一些数组与结构体的依赖关系,就主要是下标分析,对于多维的结构下标分析就成了确定一个线性方程在满足一组线性不等式约束下是否有整数解。 线性方程的变量是循环索引变量,不等式约束由循环界产生。 对于一维数组只有一个方程须要测试。 当测试多维数组时,如果一个下标的循环索引不出现在其他的下标中我们称为这个下标的状态是可分的。

数据依赖问题是整数线性规则问题,因为它不可能一般的有效的解决方案。 例如GCD测试。

指针指向分析

指针指向分析是静态分析工作的一个重要课题。 也是各项优化技术和程序分析工作的基础。关键是精确度与性能的关系。 关争是也是建立有向图,进行还路检测。主要是分析各种赋值操作。 http://www.jos.org.cn/ch/reader/create_pdf.aspx?file_no=4025

对于指针分析,然后建立自动建立pooling 来提高局部性。

Point Aalias Rule

就是不同名字,但指的是同一块内存。这两个名字互称为alias. 并且对一段内存的使用频率如何统计出来,根据这个频率来进行内存结构的重新规划,从而最大化的利用寄存器与cache. 对于数组类型的分析主要是整数方程解来解决。而对于指针类型,比较随意,分析难度比较大。 目前大部分主要是采用基于类型分析,后台自己hook内存读写分配指令,自己来做进一步分析,最简单的那基于指令扫描,这样不管 控制流,例如循环与分析的情况。复杂就要考虑这些。 这个难点就在于算法复杂度很大,并且只有大的程序才需要这些优化。 找到一个算法复杂度很度,计算与精度是一对矛盾。

同时根据内存的使用情况,来设计一个好的垃圾回收机制,目前编译器对这一块还是比较弱的,LLVM已经开始在这一方面改进了。 http://llvm.org/docs/GarbageCollection.html#gcroot, 例如常规引用计数等等方式都已经实现只需要实现gc strategy.

对于这一块的研究,微软很多好的论文。 Points-to Analysis in Almost Linear Time

多级的cache的分析

每一级的cache的cost都是不一样的,如何根据cache的cost来自动进行内容的重排,要建立这样一个模型。例如circular queue,FIFO等等队列都是一种调度算法,效率如何是需要算法与物理模型之间的匹配,例如circule queue cache特别适合,多步之间临时数据的共享。

A Hybrid Circular Queue Method for Iterative Stencil Computations on GPUs

提出基于share memory与寄存器 circular queue,也就是异构的queue. 异构的queue不是简单的内容异构,还指实现介质异构。对于一维数组,主要是下标即指针分析。 而对于标量来说,那就是引用计算的分析。 而这些采用的是把数组分析转化为标量分析,从而设计出混合系统。 http://jcst.ict.ac.cn:8080/jcst/CN/10.1007/s11390-012-1206-3

Peephole的优化

如何去除没有必要的代码,来减少代码的长度,这就是peephole. 分析基础就是basic block. 什么是 basicBlock,也就是程序的片断只有一个入口与一个出口。 一般是程序的入口,跳转指令后的地址,call指令的地址。 结束的标志:程序的结束 ,call指令, JMP跳转指令。http://blog.csdn.net/zwh37333/article/details/2498195

那就是不断的用长指令来代替的指令,这也就是所谓的超级优化,这个问题应该说用图论更加容易来解决.并且在代码选择阶段更具也可以用。 并且采用了离线存储的方式来进行。

如何使用Polly在clang/opt中

http://polly.llvm.org/docs/UsingPollyWithClang.html 在 clang 中只在O3中支持。

并且直接在make 的CFLAGS中添加这些参数就可以了。

clang -O3 -mllvm -polly file.c

把编译的过程,可以通过 -mllvm把参数传递给 llvm.

clang 不需要invoke opt, clang与opt采用相同的LLVM infrastructure, opt只是优化器的wrapper. LLVM设计本身就是模块化的,opt只是一个exe的wrapper.

在loop中利用循环变量的单调性,直接利用相等代替<. 同时调整loop中scalar变量,尽可能减少其循环次数。 http://llvm.org/devmtg/2009-10/ScalarEvolutionAndLoopOptimization.pdf

归纳变量(Induction Variable,IV) 是指循环中每次增加或者减少固定数值或者与循环次数呈一定数学解析关系的变量。 分析这些可以形成CUDA的内核函数。 循环相当于差分方程。而如何找到一个通项公式, 而利用符号计算,并且加上符号计算工具包,并且加有向图结构的分析,就可以分析通项公式。 这样我可以循环计算变成了常量计算。

polly框架结构 http://polly.llvm.org/docs/Architecture.html#polly-in-the-llvm-pass-pipeline, 可以用在opt的各个阶段,在不同的阶段,效果各有不同。靠近前端,可读性好一些,放在后端优化的效果会更好,但是可读生差。

这里包括几个4方面: #. 数据依赖的分析 #. Dead code Elimination #. Scheduling #. Tiling Vectorization

把每一层的循环当做一个轴,三层的循环就是一个立方体,再多循环就是多面体。 通过循环的操作,来找到多面体中各个顶点之间关系。如果确实有局部性,与可分性。就可以并行处理。

是不是可以图像虑波问题,都可以polly来分进行分析优化。

对于特定的问题,可以模板化生成并行化代码,例如LoopGen可以生成C#循环代码: https://www.nuget.org/packages/LoopGen/

对于C优化的,直接读进C的循环,然后编译优化的算法。这种通过JIT编译+python是不是可以实现代码的自我演化。 http://web.cs.ucla.edu/~pouchet/software/pocc/

或者CLOOG用模板文件来生成循环代码。同时可以它生成解决一维线性方程整数解代码。并且可以作为整数约数解solver.

LLVM本身也有 Vectorization 的功能,一个是优化LOOP,另一个SLP把简单变量合并成array操作,特别是矩阵计算时 会特别有用,可充分利用cache的功能。 http://llvm.org/docs/Vectorizers.html

对于并行化的分析是难点,如果只是手工的并行化分析,效率太低,需要自动化的并行分析。 难点是依赖的分析,对于指针依赖分析其实也就是对链表的并行化分析,另外对数组的并行分析,其实就是分析坐标的依赖关系, 例如看是不是有有整数解依赖,以及单调性的判断也就是所谓的range test的方式。 并行调度的分析,例如CUDA的并行调度,就是利用thread来弥补数据存取的latency.

对于并行化的另一个难点,那就是自动优化数据结构,从而实现数据结构的重排,利用cache从而提高读写效率。 但是数据结构的重排可能会影响 代码的正确性。http://www.jos.org.cn/1000-9825/11/1268.pdf 这个难点如何充分利用多级的cache,主要是数据依赖的分析,以及变换,例如互换,偏斜,幺模变换来消除依赖。并且问题的规模以及循环苦址的轻微变化将导至预测cache性能差异极大。

对于并行化,一个重要应用,那就是对大量现有的legancy代码来进行并行化,虽然硬件已经有很好的模型,但是软件还没有很好的开发模型。 并且能够自适应各个层面的不同颗粒度的并行化。 所以出现一种算法描述语言,然后再算法描述语言来分析计算需求,最后再产生执行代码。 DiscoPoP: A Profiling Tool to Identify Parallelization Opportunities

https://software.intel.com/zh-cn/videos/episode-56-strip-mining-for-vectorization 这是一种直接把循环映射到指令集的并行,直接在代码层 利用硬件特性。但是这样的代码的局限性很大,只适用于特定的硬件。所以也要实现编程语言的层次化,实现一种通用的算法描述语言。 其本质就在于需要分配多少数据在cache里。这里是利用标量变量的方法来实现的。在写代码的时候,复用同一个变量,相当于复用计存器了。 所以在指令缩减的时候,可以a=b+c d =a+2,其实直接使用a=b+c+2,而减少一个d变量

PLUTO

在实现并行的时候,就是需要找到并行部分,这样就需要分块,就像SVM一样,分快可能是园型,多边型,而不是简单的矩形。区别就在于循环的那个变量 是如何增长的,只是简单的线性的,那就是矩阵,换一种增长方式就是另一种分块方法。The Promises of Hybrid Hexagonal/Classical Tiling for GPU 就采用六边型的分类方法,其实是ogl中,strip就是为了采用各种存取方法来加快。

对于现有代码加速,就是读取源码分析出并行依赖,然后直接转换成并行代码。

对于 stencil compute 有大量 stencil compiler 可以用。

对于分块,有切割分片,重叠分片,金字塔分片。在分片的时候,IO/Compute 比会影响分片的策略。 另一个方法,实现DSL语言生成并行代码,一般用函数式语言等高阶语言来描述算法,然后再生成并行代码。 例如http://compilers.cs.uni-saarland.de/papers/ppl14_web.pdf 在Rust里实现了一个Impala来这样的事情。所以这个事情,部分求值就非常重要了。

分块技术

固定分块技术

是需要通过重新编译来来改分块的大小。 多面体扫描技术,也就是说每一层的循环都是一个面,循环体代表了各种约束。 多面体的扫描做法,其实就是化简的过程,你可以给出一种表达式,然后利用化简出更加简单的表达。 其实也就是打到这些点,相当于重新路由这些点。找到最佳路径。由于路组的下标一般是整数,一般也就是基于整数的多面体抽取。 isl :An Integer Set Library for the Polyhedral Model 例如一个if 就代表不等式的约束。 每一个迭代点就是一个顶点,至于那些顶点如何连接的,边就由循环体的计算来决定了,那些逻辑运算与算术运算那就是边的线性质。 最简单的那线性了。每一种运算就代连接方式,现在算法模型的不同在于对不同的运算的支持。现在对于各种运算符并不是完全支持。 并且由于各种运算有access relation,以及数据依赖。 例如对于这种复杂的 A[i+1+in2[i]] 可能是太复杂了,就无法分析。三大分析 #. access relations,iteration domains, schedules. http://www.grosser.es/publications/grosser-2012–Polyhedral-Extraction-Tool–IMPACT.pdf

WRaP-IT是把一个基于Open64 loop表示方式转换 多面体模型。 http://xueshu.baidu.com/s?wd=paperuri:(b9d56c50f7ec1e591af804388d0543a4)&filter=sc_long_sign&sc_ks_para=q%3DSemi-Automatic+Composition+of+Loop+Transformations+for+Deep+Parallelism+and+Memory+Hierarchies&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&sc_us=17522034925718936250

难点在于如何分块的问题,边界框,相当于平行四边形,但是可能会有冗余的。 如何得到合适的分块方法。同时还要确定的每一层循环的计算要求。

也就是一个线性分间的分块与遍历方法,多级分块,对应着多级循环。 结合新存储架构和应用数据规模特点,全面精准地描述多核共享cache架构上数据访存行为,提供更加有效,简洁,紧溱的线线性空间多面体变换理论模型,在时空维度采用更灵活的混合分块策略,利用粗细粒度结合高并行度开发硬件资源高效的计算能力,针对优秀的代码生成器在代码质量,分块开销,可扩展性等方面进行相应的优化提升,同时构建分块代码性能的评估模型

Parameterized tiled loops for free 参数化的分块,其实就是ogl的shader如何写的问题,uniform的参数就是相当于并行化分块时的常量,但是在运行时每一次都是要变化的。 在ogl的buffer的结构分配,就是一个调整tiled的过程。inset,outset两种,相当于参数化列表,例如小波变换,2x2大小,计算体,128x128大小计算,根据需要选择最近那一种分配方式,还有一种完全参数方式,由于分块增加循环的overhead,这样通过区分full tiled,partial tile,来进行在full tile 中减少不必要checker从而减小overhead.

FME 分块算法是指数级运算量,效率太低。

重用距离

程序是否具有较好的局部特征体现在数据重用时是否命中cache或寄存器。 重用距离(resue distance) 用于描述这一重要特征。 重用距离越短,cache 中相关数据重用的机会就大。

采用FPGA直接实现神经网络是最高效与直观的方式,因为直接构造计算。但是对FPGA容量要求也不会太低吧。如何创建低计算量的算法。

但是成本也不是很低,例如http://cadlab.cs.ucla.edu/~cong/slides/HALO15_keynote.pdf用到VC709开发版就5000$,这种高速版。 120M的内存。90W的功率,

https://www.ll.mit.edu/HPEC/agendas/proc07/Day2/12_Dillon_Poster.pdf 可以直接用python来实现从算法到HDL生成。 MyHDL 用python来写HDL.

http://www.dilloneng.com/ 最好用FFT IP Core. 现在基本可以用各种语言写HDL,例如BSV用haskwell来写,JHDL用java来写,CHISEL用Scalar来写。Matlab的simulink 也是可以的。 同时用C/C++来转换成VHDL,github上都有现成的项目例如https://github.com/udif/ctoverilog 并且ALTERA 已经通过LLVM把opencl转换成FPGA代码了。http://llvm.org/devmtg/2014-10/Slides/Baker-CustomHardwareStateMachines.pdf

现在对于HLS,也有LLVM的支持了。http://llvm.org/devmtg/2010-11/Rotem-CToVerilog.pdf 把LLVM IR当做算法最终描述语言。例如 http://portablecl.org/ CPC 开发一种 Portable Computing Language.

另外还有一个基于LLVM c2hdl的开源编译器`Trident Compiler <https://sourceforge.net/projects/trident/>`_ 但是已经有3年没有人更新了。

如果还做独立的SOC,还得做一些简单kernel来做一些管理,当然这个可以用NiosII软核来实现。

In a CPU, the program is mapped to a fixed architecture  In an FPGA, there is NO fixed architecture  The program defines the architecture  Instead of the architecture constraining the program, the program is constrained by the available resources

TCE

对于TCE的安装,使用的LLVM是需要打补丁的,通过安装的LLVM是不能的。 可以通过 #. tools/scripts/try-install-llvm <llvm_install_dir> #. ./configure –with-llvm=<llvm_install_dir> //not use system llvm

http://tce.cs.tut.fi/tutorial_files/tce_tutorials.tar.gz

默认它会把安装在 /usr/local/lib但是直接用可能找不到库。

工作流程:http://tce.cs.tut.fi/user_manual/TCE/node7.html. #. 先纯软件的实现。 #. 相当于porting 到一个TTA的processor上。 #. 然后就是优化了。

C->bitcode->TTA processor code->优化,生成代码适配。并生成对应image文件。

性能优化最终极的目标那就是能耗比。而只有这样才能实现从上到下全局优化。 而之前的是扩张占用资源。

DL 时代的编程

  1. 基于大数据
  2. 基于自我演化

Indices and tables