全部文章 | 第5页

【吐槽2016,展望2017】因祸得福的一年

该回顾一下2016了。其实2016遇到的事情还是蛮多的,奇葩的事情也不少。

2016主要经历了几个事情:

1,两连逼宫的一哥们上书,老板接机换掉一直想换掉的人

2,各种啥鸟程序员逐渐浮出水面,各种坑爹事情逐渐躲起来。我一点点的变成了吃瓜群众,无力。。。。

3,改朝换代当天,摇到车牌。悲喜交加。。。。

4,2016年回顾了2015和之前做的音视频技术,整理了一部分音视频的技术点,收获不小。

5,图像处理的demo在不情愿被逼迫的情况下被成功捣腾出来,方法刚好和正确的套路一致。然后逐渐从demo捣腾成“玩具”。

6,2016下半年遇到各种不开心的事情,但刚好在年末时节,出现了神转折。

7,站在当前,回顾以往。当初坚持的持续学习,持续深入的目标没有错;之前的投入,如今已经一点点收见成效了。

8,常常被人说:“你怎么这么累?” ,“你想的简单点”,“不要总是愁眉不展”。起初我还出现了很严重的自我怀疑,现如今事实证明了,开始进入我阶段性收获的时节了

 

 

2016的总结:不要人云亦云,也不要太过于随大流,更不要过于随性。社会不简单,但也不是非常复杂。遵守和学习社会的规则重于其他任何要素。

2017展望:要么继续把音视频做好,要么就深入下去,把图像处理基础打好。未来不要想着两个都能做精,但两者融合到一个使用场景中会是一种趋势。

【程序结构、系统构架】有效且高效的程序结构/构架之一

首先看一下下图,下图是stun或者说是NAT穿越时需要用到的NAT检测服务器拓扑组网图:

 

 

目前,PC B和PC A需要进行p2p连接,首要要知道他们所在的网络是呈现哪一种NAT类型。因此就涉及到了NAT检测服务器(stun服务器)和客户端。

NAT检测服务器的功能实际上很简单,就是自身拥有2个或2个以上的IP,并每个IP监听至少2个UDP端口,一共就有4个可连端(即4个socket)。

当客户端与NAT服务器进行通讯时,NAT服务器和客户端都将会知道客户端外网IP:Port,和客户端内网的IP:Port。

但在实际设计NAT检测服务器时,就遇到了一个问题:NAT服务器是否需要记录客户端的IP:Port信息。如果需要记录出于什么目的?如果记录那需要多少存储能力和计算能力?

 

首先分别讨论一下存储和不存储的情况。对于存储客户端IP:Port信息来说,可以有效的统计和判断当前客户端群(带了个“群”字样)的NAT情况,并且能够间接节省掉一个统计数据上报的过程,数据的及时性和有效性也得到了保障。但带来的是服务器程序设计上的复杂性和性能损失,带宽成本相对于不存储的来说或许会更低一些。

对于不存储的情况来说,客户端可以设计的很简单,并专注于NAT类型检测的任务,单点抗压能力可以发挥到最大;但带来的是数据统计与反馈方式需要另辟蹊径,增加了客户端的复杂度,某种程度上来说会增加带宽的支出(需要将IP:Port数据上报)。为了该工作还需要配套一个完整的数据统计与上报模块(服务端+客户端),某种程度上增加数据反馈的不可靠性。

 

在看新版本webrtc的p2p模块时(nat检测服务器),发现服务端并不存储任何客户端的信息,只是由客户端来自行决定是否存储。于是想到了这个问题。

 

对于NAT服务器来说,确实不应该存储过多的客户端信息,但如果在需要可靠性较强的环境中时,NAT检测服务器还是存储一定的客户端信息。并将一些服务器不需要做的计算和存储工作保留在客户端可以有效的达到合理使用的目的。

 

例如:在NAT检测中,往往会出现两种情况,即:

1,多级NAT中的NAT在不同时间段呈现不同的NAT类型(不规则NAT)

2,在单级NAT中,由于IP、Port限制型场景中,由于检测次数的不够充分,将IP:Port误判断成其他类型的NAT。

在这两种情况中时,需要进行定量或者定性统计分析时,如果数据仅仅保存在客户端时,由于样本不够大(NAT检测次数不够多、该局域网内可能不只一个客户端),导致分析不充分,进而影响NAT穿越成功率以及间接影响分享率、分享公平性。

所以需要将此类数据进行服务端统计,并作筛选,才能有效的判断出该NAT环境下较大的概率会呈现哪一种NAT。

【脑筋急转弯】利用set和vector做排序、去重

在看webrtc的代码时,遇到一个ip地址池在set和vector中颠来倒去的代码,顶着看了一会,后来才反应过来。代码如下:

std::set<rtc::SocketAddress> addrs(all_servers_addrs_.begin(),all_servers_addrs_.end());
all_servers_addrs_.assign(addrs.begin(), addrs.end());

原先在all_servers_addrs_中已经保存了一个ip列表,通过导到set之后,首先去重了ip,接着再按照’>’算术运算符来进行排序,接着在通过assign将排序去重后的数据返回vector。

这样做的好处在于,时间复杂度为set排序复杂度+set随机访问的时间复杂度的总和,大致可以避免vector(类似线性表)的较高插入删除复杂度的问题。

【opencv、颜色存储方式】imread的IMREAD_COLOR方式解码后是BGR方式存储

最近的项目中需要用到将RGB空间转换成HSV空间,转换后一直都感觉不对劲,最后才发现imread中的IMREAD_COLOR参数。

在opencv中的注释中提到IMREAD_COLOR方式是将数据尝试解码成BGR方式存储。

数据解码后,实际在上内存中的数据存储方式确实为BGR,通过imwrite或者imshow时,数据应该会被默认转换成实际的图像格式(没有看代码去考证,因为bmp就是BGR的序列)。

在进行色彩空间转换时,例如:BGR(或RBG) -> HSV时,如果将CV_BGR2HSV和CV_RGB2HSV混淆,最后的结果会引起色彩区域置换。所以就有问题了!

【matlab、启动错误】在远程桌面中无法启动matlab问题

最近一直在通过远程桌面的方式使用笔记本,突然发现matlab死活都打不开,出现如下错误

555

 

起初以为安装出问题,后来网上看了下才知道是远程桌面的锅。直接用基本登录,问题解决。

最近手头需要做的几件事情

  1. 看nat穿越中的开源代码,优先看udt。先把udt实现看明白(主要是重发包这些)
  2. webrtc之前项目中没有用到的代码继续学习
  3. tcp单边和双边加速的原理理解和学习
  4. opencv部分代码深入看
  5. *caffe,实在没办法了,模式识别中的模板匹配越做越复杂了,要换个路子走了

【代码级优化、细枝末节、汇编】关于for一些写法细枝末节的优化

首先先看一下下面两段C++的代码

for_1

for_3

这两段代码唯一的区别在于条件区域的代码不同,一个是在外面先把end的游标获取到,一个是在for执行的过程中动态获取。如果实在想不明白,那就直接上汇编把。

for_2

for_4

实际上在《高效C++》中就提到过关于for条件区域代码每次哪一种写法性能好,哪一种写法性能差。由于end函数在这里的性能开销可以忽略不计,但如果不是调用end而是调用一个性能开销比较大的函数,那么这个for循环会很慢;在多线程模型中,如果for中的条件区域代码是线程间需要同步的,那么有可能这个条件区块代码的执行结果在每一次循环时都会发生变化。

对于这种代码级的优化,实际中最好少用或者不用,但并不是不带着优化的思维去写代码,而是在写的时候就要意识到这块代码可能会成性能瓶颈。

奇葩的笔试题,符号优先级

首先说明,本文章讲述的是C、C++方面的奇葩类笔试题,之所以奇葩是因为没有考到C、C++中真正需要关注的部分,也间接能看出出题人对C、C++的了解层度。

 

最近在外面试,有做笔试,也有不做笔试的。因为工作有一些时段,所以不做笔试也算说的去,但一遇到做笔试题往往弄得我哭笑不得。不是拿一些太基础的题来糊弄人,就是拿一些技巧性的题目来糊弄人。最为关键是招聘方所谓的技术人员还很自以为是。面试应该是互相尊重的过程,而不是耀武扬威的一个过程,下面就分享这么一题笔试题,也算是复习一下我自己的基础。

 


int count=0;
int iarraytestA[10] = {11};
int iarraytestB[10] = {11};
int iarraytestC[10] = {11};

while(count < 10)
{
iarraytestA[count++] = count;
}

count = 0;
while(count < 10)
{
iarraytestB[++count] = count;
}

count = 0;
iarraytestC[++count] = count;

 

求iarraytestA和iarraytestB的结果。用vs跑了一下,结果一样:

iarraytestA:0,1,2,3,4,5,6,7,8,9
iarraytestB:11,1,2,3,4,5,6,7,8,9
iarraytestB:11,1,0,0,0,0,0,0,0,0

后来仔细想想,这个应该是算术优先级的问题。这里面的符号有几个:

赋值、自增(前向和后向)、数组寻址

查了一下符号优先级表,发现++运算符的优先级和[]一样,而=的优先级就不用多说,应该是最低的。
由此可以得出上述结论。

首先在这套题里,主要考了笔试C、C++中对这几个符号的认识,另一个这道题最蠢的还在于使用了不该用的技巧在考人。在C、C++规范中对相同优先级的符号做过的约束并不是很明确(C++11有所改善),在高效C++中有大量例子说明不应该使用技巧性过强的写法,这样会导致不同编译或不同编译参数编译出来的代码有不同行为。

这也同样说明了出该笔试题的人对C++也不是那么了解,真正在C++中笔试中有很多类型的题目可以出,但也不是都能出,因为语言规范的原因,对于单纯考算法的问题,也有很多大神做过讨论。我只是觉得学以致用,考什么都不要紧,关键和工作实质内容有没有关联?

ps:另外强烈鄙视钓鱼招聘,叫人过去面试就是为了打探技术方法和商业信息!

【pthread、多线程、同步】关于pthread条件变量的疑惑

由于最近在看开源代码,手里的开源代码为了能够比较好的可移植性,在代码中加了很多宏,并在linux用了pthread库。

其中对于pthread的条件变量理解很是疑惑,看pthread手册后还是有不解,感觉条件变量用起来太奇怪,在pthread_cond_wait时,互斥到底有没有释放等问题上没搞明白,上代码调试后遍便豁然开朗。先贴一个修改过的官方代码例子:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#include <iostream>

#include <unistd.h>

using namespace std;

#define NUM_THREADS  3
#define TCOUNT 10
#define COUNT_LIMIT 12

int     count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;

void *inc_count(void *t)
{
  int i;
  long my_id = (long)t;

  for (i=0; i < TCOUNT; i++) {
    pthread_mutex_lock(&count_mutex);
    count++;

    /*
    Check the value of count and signal waiting thread when condition is
    reached.  Note that this occurs while mutex is locked.
    */
    /*if (count == COUNT_LIMIT)*/ {
      printf("inc_count(): thread %ld, count = %d  Threshold reached. ",
             my_id, count);
      pthread_cond_signal(&count_threshold_cv);
      printf("Just sent signal.\n");
      }
    printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",
	   my_id, count);
    pthread_mutex_unlock(&count_mutex);

    /* Do some work so threads can alternate on mutex lock */
    sleep(1);
    }
  pthread_exit(NULL);
}

void *watch_count(void *t)
{
  long my_id = (long)t;

  printf("Starting watch_count(): thread %ld\n", my_id);

  /*
  Lock mutex and wait for signal.  Note that the pthread_cond_wait routine
  will automatically and atomically unlock mutex while it waits.
  Also, note that if COUNT_LIMIT is reached before this routine is run by
  the waiting thread, the loop will be skipped to prevent pthread_cond_wait
  from never returning.
  */
  pthread_mutex_lock(&count_mutex);
  while (1) {
    printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id,count);
    pthread_cond_wait(&count_threshold_cv, &count_mutex);
    printf("watch_count(): thread %ld Condition signal received. Count= %d\n", my_id,count);
    printf("watch_count(): thread %ld Updating the value of count...\n", my_id,count);
    //count += 125;
    printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
    }
  printf("watch_count(): thread %ld Unlocking mutex.\n", my_id);
  pthread_mutex_unlock(&count_mutex);
  pthread_exit(NULL);
}

void cond_test()
{
	cout << "---------- cond_test ----------" << endl;

	int i, rc;
	long t1=1, t2=2, t3=3;
	pthread_t threads[3];
	pthread_attr_t attr;

	/* Initialize mutex and condition variable objects */
	pthread_mutex_init(&count_mutex, NULL);
	pthread_cond_init (&count_threshold_cv, NULL);

	/* For portability, explicitly create threads in a joinable state */
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

	pthread_create(&threads[0], &attr, watch_count, (void *)t1);
	usleep(1000*20);
	pthread_create(&threads[1], &attr, inc_count, (void *)t2);
	//pthread_create(&threads[2], &attr, inc_count, (void *)t3);

	/* Wait for all threads to complete */
	for (i = 0; i < NUM_THREADS; i++) {
		pthread_join(threads[i], NULL);
	}
	printf ("Main(): Waited and joined with %d threads. Final value of count = %d. Done.\n",
	          NUM_THREADS, count);

	/* Clean up and exit */
	pthread_attr_destroy(&attr);
	pthread_mutex_destroy(&count_mutex);
	pthread_cond_destroy(&count_threshold_cv);
	//pthread_exit (NULL);
}

首先说一下例子,主线程创建一个inc线程和watch线程,每次inc线程激活条件变量,这时watch线程被激活。
为了测试在pthread_cond_wait函数进入是,互斥有没有被释放掉。因此注释掉高亮部分的代码,发现inc线程永远不会被激活。说明在pthread_cond_wait时会将互斥进行释放,接下来等有空一定要抽空看看pthread的实现代码。以及比较一下vs是如何将pthread库进行伪封装后在windows上提供使用的。

【chromium、webrtc、gn】引入gn之后下载代码和生成vs工程的方法

按照惯例,还是一如既往的通过代理进行下代码,并生成工程。因为webrtc是chromium里面的一个组件,所以webrtc和chromium的下载和生成工程的方法大同小异。

具体步骤如下:

1. 首先把depot_tools装好。

2. 配置代理

netsh set winhttp proxy 127.0.0.1:10808
set http_proxy=http://127.0.0.1:10808
set https_proxy=https://127.0.0.1:10808

3. 按照官方的步骤,下载代码

mkdir webrtc-checkout
cd webrtc-checkout
fetch --nohooks webrtc
gclient sync

4*. 如果gclient sync过程出现异常,或建议最好执行本步骤

gclient runhooks

5. 通过gn生成vs2015的工程

cd src
gn gen --ide=vs2015 /out/Default

结束。

这个过程中比较头疼的问题就是需要一个稳定的代理

【线性代数、分块矩阵】用图形的方法间接理解分块矩阵的转置

如图,左边的是分块矩阵(转置前),右边是分块矩阵转置后。之所以可以这样写,是因为矩阵关于的行数和列数中最大值的对角线进行互换元素。因此对于子矩阵这些个体来说,他们就可以写成右边的形式,但至于右边的形式展开后是否真的就等于左边的转置。其实理解起来也很简单。

首先,对于每个子矩阵来说,他们的转置方式(或性质)都和母矩阵A一样。即,没有一个子矩阵均是关于同一个平行的对角线互换元素。如果把母矩阵中的每个元素抽象成点,由此最外围的点构成的四边形进行折叠,即可得出形为A转置后将元素抽象成点的图像。证明下图中右边的式子就是A的转置。

 

 

img_20161003_150002

【线性代数、逆矩阵、困惑、笔记】逆矩阵变换的知其然不知其所以然

再看高教版线性代数中的逆矩阵变换的证明,突然看到了一个困惑点实在不知道如何想明白。

如下图红框框部分。如何能够证明,在矩阵两端左乘一个矩阵以后还能保持等号的意义?

如果通过设   Y1=A* AX (均为矩阵)      的方式去证明,似乎能够说明红框框中公式左乘的意义。但那又会不会有不成立的情况呢?

这本线性代数,看到这里时,我又反复回顾前面的一些例证:除了通过推到明确证明的公式或定理外,对于一些用归纳法证明的定理和公式“投机取巧”的成分很大,看来后面看完这本书,要找一本讲的相对清楚的补一下。

 

3

【有向图、迭代、抽象、线性代数、数学】有向图用矩阵理论化计算可能路径

在数据结构和数学中,都有着有向图的理论路径计算和迭代。一下是线性代数中对有向图的一个抽象。

 

 

%e6%9c%89%e5%90%91%e5%9b%be

%e6%9c%89%e5%90%91%e5%9b%be1

 

 

 

 

可见, 如果需要枚举出某个断点到另一个断点的可能路径的话,那就是对A矩阵进行做幂计算。例如计算1号端点到4号断点4次中转以内的可能路径,那么只需要对A矩阵做四次幂运算(1,2,3,4),然后求和4种幂运算中的a14,即可得到可能的路径数。

【teamwork、队友】停留在嘴上或形式上的“团队合作”

早在很早以前,我一直在和我的合作伙伴(和我身处同一个团队,与我一样处于负责人头衔的的)在灌输一种观点”团队合作“。

团队中的每一个人在工作上做的事情都有可能给项目带来正面或者负面的影响,大家一定要学会收敛自己的个性,要学会凡事都商量一下,不要什么时候都很喜欢主观判断然后下结论或者判断。

可惜的是,我这句话有点抽象,要解释清楚“它”;需要“失败”与“成功”的经历,并自身多次去反思才行。

就在最近的一周,合作不协调的团队开始出现了裂痕,加之外部压力,团队开始出现被动分裂。其实我本以为,之前我说过那么多就算大家不明白,这次的经历和经验已经能够让大家足以学会试着收敛和团结。可惜的是,没有见到多大的改观,可谓树倒候鸟飞;越是危机关头越能看得出一个人的修养与品质。

就在最近的大规模人事变动里,渐渐的就证实了曾经那些称兄道弟的人究竟是什么样的修养。凡事没有处在危机时,人对待该事的态度大多都不愿意去观察负面的成分,有甚者还出现厌恶的心理。所以当遇到负面成分占主导时,大部分人也寻求逃避、本能反应等(跳槽、内耗、内斗等);实为不择不扣的蠢货和傻子。

在很多事情上,人很难做出逃避,多数要选择面对;例如极端大到国际争端,小到两个人之间的口角之争。逃避仅仅是人的本能反应之一,并不能解决问题。尤其是当有可能再次遇到的事情时,如果不去解决,问题还在那里。

所以,总结一下上面叙述不完整的事件来看看。

一个配合存在问题的团队,且没有绝对大神参与项目的情况下,导致项目磕磕碰碰(已经是最理想情况),最后刚好公司内部人事斗争,接着导致人事大调整。

大家平时积累了长时间的“气”,终于在同一个时间爆发出来,然后绝大部分人选择以自己的利益为出发点行事,虽然没有出现出格的事情,但人心已经散掉了,各奔东西。

如果站在一个看客(局外人)的角度去看这群人和事,只有“可笑”可以形容,很明显的问题,大家不仅没有意识到,而且事后也不加收敛的乱来。

可见“团队合作”的重要性,但也不是仅仅停留在嘴上的“团队合作”,而是从认知上的习得什么叫做团队合作。认知缺失是一种越来越普遍的现象,现代的技术在让人变的越来越懒,也在让人的智力水平起点越来越高,但也在让人的认知能力呈逐渐下降的趋势!

 

 

【要统治宇宙吗?】论某某语言或者开发框架最好的情况

早在刚刚参加工作时,对工作和C++、MFC抱着满腔的热情,非常热衷于讨论某某框架牛逼,某某语言很厉害。

随着工作经验的增长,逐渐认识到,潜下心来研究C++和用这门语言开发的东西是正确的,但也要理性看待问题。

现在新型崛起的语言或者库有很多,例如python(若干年前还没有现在火)、ruby(貌似一直都很火)、h5、css3/4、jquery等等。这些语言或者库的开发者们,为了迎合一些使用场景,于是做出了很多伪或真的native lib,能够使得这些原本需要解析器跑的语言,可以流化或直接编译成二进制或嵌入一个浏览器来跑。

有很多人渐渐的就认为了这个东西好,那个东西坏什么什么的。其实在我看来,用我最近正在看的心理学书来解释:“认知缺失造成的刻板现象。”

每一种语言或框架的崛起也好,衰落也罢。其实只是当前码农市场上为了解决一些实际工程问题而想出的招数,当这些工程问题解决的逐渐成熟了以后,那接下来下一类工程问题是不是又会发生新的变化呢?

很早以前我很排斥java、c#和web方面的开发,我甚至认为数据库SQL开发都是没啥意思的;但随着现在一些实际的工程问题在逐渐凸显以后,我发现这些还真不能缺,不过作为一个不甘“无知”的人,我还是既然继续选择研究c/c++语言开发的技术,因为这些技术恰恰很可能是python、ruby、h5等分工领域的基石。掌握了基石,难道还怕上层的变化吗?