静态语言和动态语言
静态语言:C、C++
动态语言:Java、Objective-C、C#等
动态语言和静态语言的区别
动态语言如Objective-C,可以在程序运行的时候动态的添加一个函数;而C、C++他们的类型和逻辑是在编译时就确定的,无法更改。
如图:
我有一段objective-C代码,这段代码只有一个函数funtion(), 而我可以在程序运行的时候动态读取myfuntiom.xml,像内存中增加两个新函数:fun2()、fun3(),这在C++是无法做到的。
那它是怎么实现的呢?
它会在内存中创建一个数组,存我需要的所有函数。而Objective-C在运行的时候并不是直接调用含数的,而是通过一个称为objc_msg的中间层间接寻找和调用函数的。
那动态语言为什么可以动态的这么做呢?
它靠一种技术称为反射(Reflection),像Java、C#都是这样,还有一种称为依赖注入(Dependency Injection),这些都是程序设计的一些实践方式,大家可以了解一下。
class Myclass {
int a;
int b;
}
看上面这段函数,在C++里,我有没有办法知道他是什么类型的呢,答案是不能,这在C++是无法做到的。只有程序设计者知道,在编译之后,它生成了二进制文件,没有人知道里面是什么。
而Objective-C 里面是可以知道的,那我怎么知道呢,所以还需要另外一套数据,称之为元数据Metadata,来告诉我a is integer ,b is integer。它这里面会告诉我值和类型。那它做这件事的代价就是查询,需要额外的时间,因为是在程序运行时决定。
C++的设计思想是一切以速度为主,为了速度可以牺牲任何东西。所以它并没有元数据。那你说我自己给它加一个行不行。当然可以,它没有在程序设计上支持,而是留给程序员自己去设计。
C++经过编译之后,会编译死在二进制文件之中,不再允许修改,在windows体系里,是.exe的可执行文件,这里面会有不同的数据段。
关于编译技术,大家可以了解这几本书:
《程序员的自我修养》
《高级C》
《高级C C++编译技术》
内存管理上的区别
堆与栈
在程序当中,咱们会划分两个不同的内存区域,一个是堆,一个是栈。堆的空间比较大,栈的空间比较小,内存本质是没有任何区别的,但为什么操作系统、程序、运行时会把内存分成栈和堆这有的概念呢。堆的空间更大,我全部用堆不就好了。其实不是这有的。
对于C++或其他现代编程语言来说,很多情况我们有调用堆栈。函数a调用b,b调用c,这个函数的调用惯例、数据和层次结构,都是存在stack栈里面的。为什么会这样?
大家如果了解数据结构,你就知道了,stack的内存是连续的,那么如果你访问类一块短小精悍的内存,那么它的速度就会很快。我函数调用,当然是希望它更快。因为c执行完了要回到b,b执行完了要回到a,如果我能快速的查询到它是不是整件事都不一样了。当然这只是一个方面,这里还涉及指针和引用的概念,我们下次再谈。
heap虽然空间大,但内存不连续,那我怎么样找到它呢,C++通过pointer指针、或引用。我经常会在stack pointer 两个地址,指向heap中的某个区域,这是指针的经典用法,但不代表指针只能做这件事。
到此我们引入了指针的概念,很多大学、教育机构讲Java、Python,大家可能只关心程序的业务逻辑,并不关心程序下面到底怎么回事。C++就不一样了,指针对C++非常重要,理解好指针对大家写C++程序来说非常重要。大家如果明白指针是怎么回事,它到底是怎么做的,在写C、C++的时候就不会迷茫了,看不懂,这到底是怎么做的,原来在Java、Python中完全没有考虑过这样的问题,指针非常重要,下次我会专门写一篇来谈指针。
垃圾回收
在Java当中,我们创建一个对象,用完了不需要管理,你可能也从来没考虑过这个问题,反正用完了就不管了。动态语言在这方面确实非常方便,但方便这件事情是有代价的,这里就引出了一个概念,垃圾回收Garage Collection。Java有JVM,C# 有.net framework,Objctective-C有Runtime,它们统一帮程序员做垃圾回收,而不用你来处理。
垃圾回收又是一个非常深奥的话题,简单来说,它也用了一个C++里面智能指针—引用计数这样一个理念。它会在后台开一个线程,不断的索引不同的程序员分配的引用计数,它会去分析,有不同的算法,算法有很多。它会去分析这个对象到底还有没有人用了,如果没有,它去负责把这个对象从内存里面剔出去。这就是垃圾回收干的事情。
这不是挺好的吗,C++为什么不做这些事情呀,C++不管这些事情,你分配了,你不去释放,这个内存就变为垃圾内存、野地址。或者说内存泄漏了,你再也找不到这块内存在哪里了。你不用他了,你也不知道再哪里去释放。C++当中非常重要的一件事是我们使用了内存,最后也要我们自己去删除。
上次我们提到了智能指针,智能指针在一定程度上能帮我们解决了这个问题,我分配好,智能指针帮我去分析,要不要删除这个对象。它内部会有一个引用计数的概念,当引用计数为0的时候,智能指针会帮我们把内存释放掉。
自制与智能指针的缺陷
那智能指针没有缺陷,不是能完美的解决我们的问题了吗?
我们回到主题上来,现在机器学习这么热,有足够的大数据来支撑我们进行机器学习,机器学习需要依托海量数据,如果我们用C++程序来处理海量数据的话,这些数据在内存当中是不是我们都可以包装成一个一个的对象。这些对象呢,我全都包装成智能指针,通过智能指针去分配。不用的时候智能指针帮我们去计算,帮我们去删除。那么缺陷到底在哪里,智能指针有引用计数的概念,所以我们会在程序的运行当中去不断的计算引用计数。加加减减,数据少了还可以,如果我们有几百万甚至上亿的数据,这个时间是无法接受的。如果有几亿次,这个是无法接受的,而这正是因为完全依赖智能指针而导致的。所以最后回到了自制。后面数据量不大的情况下我们确实可以选择用智能指针,但如果数据量很大,或开发一个platform ,一个平台的情况下,我们还是要选择自己管理内存,这样才能充分发挥C++的特点,或者说最强的优势所在。所以智能指针能解决问题,但在机器学习领域就不一定了。
为什么C++效率这么高
C、和C++相较其他高级语言来说(这里面我们把C和C++放到一起说,事实上还是有区别的),他们是最接近硬件执行水平的,因此我们可以推断,除了机器语言和汇编之外,我们可以粗略的认为C和C++的执行效率是最高的。为什么这么高,之前咱们也简单谈过,首先它是一门静态语言,所的有东西在写代码的时候就已经确定下来了。不像Java、Objective-C、C#在运行的时候再去决定一些事情,其次,内存管理在绝大多数情况下都是自制的,也就是我们要自己去管理内存的分配和释放,不依赖于后台垃圾回收这样一个东西,这些功能像是负罪,负责的东西越多,负担越大,如果我们自己去设计,很多不必要的开销都能节省掉。这并不是C++执行效率高的全部原因,这两个是我认为两个主要的原因。
我不知道大家有没有看过Objective-C Runtime的代码,Objective-C Runtime 本质上是使用C++实现的,这个就很有意思了,Objective-C 它的运行是基于C++实现的,反推Java、C#原理也是这个样子,JVM本身本质上也是C++实现的。所以,大家可以看到,当考虑非常底层、非常重要的模组的时候,在考虑速度、performance,执行效率的情况之下,C++仍然在现在,甚至很长一段时间,它都是最重要的,而机器学习之于C++就更重要了。