剖析代码性能时通常需要计时。下面记录不同语言的各种计时方法。
C++计时方法
传统计时方法的代码如下:
1 | #include <ctime> |
C++11 标准的”最佳计时方法“的代码:
1 | #include <chrono> |
Python计时方法
在Jupyter Notebook中,计时使用一个magic command:%timeit。
参考链接
- C++11 新的计时方法——std::chrono 大法好,by sicolex.
剖析代码性能时通常需要计时。下面记录不同语言的各种计时方法。
传统计时方法的代码如下:
1 | #include <ctime> |
C++11 标准的”最佳计时方法“的代码:
1 | #include <chrono> |
在Jupyter Notebook中,计时使用一个magic command:%timeit。
最近需要用C++编写一个网络程序,因此将网络编程的相关知识整理学习一下。
Socket 是对 TCP/IP 协议族的一种封装,是应用层与TCP/IP协议族通信的中间软件抽象层。从设计模式的角度看来,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
Socket 还可以认为是一种网络间不同计算机上的进程通信的一种方法,利用三元组(ip地址,协议,端口)就可以唯一标识网络中的进程,网络中的进程通信可以利用这个标志与其它进程进行交互。
Socket 起源于 Unix ,Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开(open) –> 读写(write/read) –> 关闭(close)”模式来进行操作。因此 Socket 也被处理为一种特殊的文件。
创建 socket 的时候需要指定 socket 的类型,一般有三种:
不同的计算机对数据的存储格式不一样,比如 32 位的整数 0x12345678,可以在内存里从高到低存储为 12-34-56-78 或者从低到高存储为 78-56-34-12。
但是这对于网络中的数据来说就带来了一个严重的问题,当机器从网络中收到 12-34-56-78 的数据时,它怎么知道这个数据到底是什么意思?
解决的方案也比较简单,在传输数据之前和接受数据之后,必须调用 htonl/htons 或 ntohl/ntohs 先把数据转换成网络字节序或者把网络字节序转换为机器的字节序。
常用的服务器应用分类如下:
同步阻塞迭代模型是最简单的一种IO模型,其核心代码如下:
1 | bind(srvfd); |
上面的程序存在如下一些弊端:
多进程并发模型在同步阻塞迭代模型的基础上进行了一些改进,以避免是程序阻塞在read系统调用上。核心代码如下:
1 | bind(srvfd); |
上述程序在accept系统调用时,如果没有客户端来建立连接,会阻塞在accept处。一旦某个客户端连接建立起来,则立即开启一个新的进程来处理与这个客户的数据交互。避免程序阻塞在read调用,而影响其他客户端的连接。
在多进程并发模型中,每一个客户端连接开启fork一个进程,若客户端连接较大,则系统依然将不堪负重。通过多线程(或线程池)并发模型,可以在一定程度上改善这一问题。
在服务端的线程模型实现方式一般有三种:
以第一种为例,其核心代码如下:
1 | void *thread_callback( void *args ) //线程回调函数 |
服务端分为主线程和工作线程,主线程负责accept()连接,而工作线程负责处理业务逻辑和流的读取等。因此,即使在工作线程阻塞的情况下,也只是阻塞在线程范围内,对继续接受新的客户端连接不会有影响。
第二种实现方式,通过线程池的引入可以避免频繁的创建、销毁线程,能在很大程序上提升性能。
但多线程模型先天具有如下缺点:
多进程模型和多线程(线程池)模型每个进程/线程只能处理一路IO,在服务器并发数较高的情况下,过多的进程/线程会使得服务器性能下降。而通过多路IO复用,能使得一个进程同时处理多路IO,提升服务器吞吐量。这是一种进程预先告知内核的能力,让内核发现进程指定的一个或多个IO条件就绪了,就通知进程。使得一个进程能在一连串的事件上等待。
IO复用的实现方式目前主要有select、poll和epoll。
select和poll的原理基本相同:
注册待侦听的fd(这里的fd创建时最好使用非阻塞)
每次调用都去检查这些fd的状态,当有一个或者多个fd就绪的时候返回
返回结果中包括已就绪和未就绪的fd
相比select,poll解决了单个进程能够打开的文件描述符数量有限制这个问题:select受限于FD_SIZE的限制,如果修改则需要修改这个宏重新编译内核;而poll通过一个pollfd数组向内核传递需要关注的事件,避开了文件描述符数量限制。此外,select和poll共同具有的一个很大的缺点就是包含大量fd的数组被整体复制于用户态和内核态地址空间之间,开销会随着fd数量增多而线性增大。
epoll的出现,解决了select、poll的缺点:
基于事件驱动的方式,避免了每次都要把所有fd都扫描一遍。
epoll_wait只返回就绪的fd。
epoll使用nmap内存映射技术避免了内存复制的开销。
epoll的fd数量上限是操作系统的最大文件句柄数目,这个数目一般和内存有关,通常远大于1024。
总结:
地形数据是能够表示地球表面高低起伏状态的数据,即具有高程信息的数据。数字高程模型(DEM)是一种对空间起伏变化的连续表示方法,是一种特殊的 DatasetGrid 数据模型,每个网格的值为高程值,而且有标准的颜色表来表示,这对分幅 DEM 图像的合成很有帮助。
数字地面模型(digital terrain model,DTM)就是以数字的形式来表示实际地形特征的空间分布。有时所指的地形特征点仅指地面点的高程,就将这种数字地形描述称为数字高程模型(digital elevation model,DEM)。最初是于1958年由美国麻省理工学院Miller教授提出。数字地面模型广泛用于遥感,地理信息系统,大地测量和电子地图等领域。
常用的地形数据主要有:etopo、GTOPO30、GEBCO以及SRTM。按照精度从低到高排序如下:
数据源 | 空间分辨率 | 覆盖范围 | 陆地/海洋 |
---|---|---|---|
etopo5 | 5 弧分 | 全球 | 陆地 + 海洋 |
etopo2 | 2 弧分 | 全球 | 陆地 + 海洋 |
etopo1 | 1 弧分 | 全球 | 陆地 + 海洋 |
GEBCO | 1 弧分 / 30 弧秒 | 全球 | 陆地 + 海洋 |
GTOPO30 | 30 弧秒 | 全球 | 陆地 |
SRTM30_PLUS | 30 弧秒 (约 1km) | 纬度 [-81,81] | 陆地 + 海洋 |
SRTM15_PLUS | 15 弧秒 (约 500m) | 纬度 [-81,81] | 陆地 + 海洋 |
SRTM | 3 弧秒 (约 90m) | 纬度 [-60,60] | 陆地 |
ASTER GDEM | 1 弧秒 (约 30m) | 纬度 [-83,83] | 陆地 |
ETOPO是一种地形高程数据。该数据由NGDC美国地球物理中心发布(U.S. National Geophysical Data Center )。与srtm、aster gdem一样,均为高程数据,所不同的是它还包括海洋海底地形数据。
ETOPO地形数据有五种规格, ETOPO1的效果最好,ETOPO2、ETOPO5数据尽管可以使用,但目前已不推荐采用。其中ETOPO1约三百多兆的压缩包, ETOPO2约九十兆。
ETOPO1包含了全球地形和海洋深度,采样间隔为1弧分,是目前精度最高的global relief数据;其分为两个版本,Ice Surface和Bedrock,两个版本基本一致。不同之处在于在处理南极洲和Greenland地形时,前者给出的是加上冰盖层之后的高程,后者给出的是岩床的高程。
对于每个版本又细分为 grid-registered和cell-registered,其中grid-registered是权威版本,cell-registered是衍生版本,因而推荐下载使用grid-registered版本。
GEBCO数据的全称是General Bathymetric Chart of the Oceans( 全球海洋通用水深数据) ,是由国际海道测量组织(IHO) 和政府间海洋学委员会(IOC) 联合发布的最全面的世界大洋海底地形数据,也是当今海洋模式中最常用的海洋水深数据之一。
SRTM 即航天飞机雷达地形测绘使命。航天地形测绘是指以人造地球卫星、宇宙飞船、航天飞机等航天器为工作平台,对地球表面所进行的遥感测量。以往的航天测绘由于其精度有限,一般只能制作中、小比例尺地图。SRTM则是美国太空总署(NASA)和国防部国家测绘局(NIMA)以及德国与意大利航天机构共同合作完成联合测量,由美国发射的“奋进”号航天飞机上搭载SRTM系统完成。本次测图任务从2000年2月11日开始至22日结束,共进行了11天总计222小时23分钟的数据采集工作,获取北纬60度至南纬56度之间总面积超过1.19亿平方公里的雷达影像数据,覆盖地球80%以上的陆地表面。
SRTM系统获取的雷达影像的数据量约9.8万亿字节,经过两年多的数据处理,制成了数字地形高程模型(DEM)。SRTM产品2003年开始公开发布,经历多修订,目前最新的版本为V4.1版本。
SRTM地形数据主要包含两类数据:SRTM3和SRTM1。
所有的数据被分为1度1度的单元,共计上万个数据文件。文件名代表了该单元左下角的位置;不同网站下载的数据文件名不同,但文件名类似N37W105的 数据文件,其单元左下角的坐标为(37N,105W);每个SRTM3数据文件包含了12011201个采样点;高程数据单位为m,参考水准面为 WGS84大地水准面模型;网格划分采用gridline registration方式,因而单元的南北边及左右边与相邻单元的边重合。
中国海拔高度(DEM)空间分布数据来源于美国奋进号航天飞机的雷达地形测绘SRTM(Shuttle Radar Topography Mission,SRTM)数据。SRTM数据有现实性强、免费获取等优点,全球许多应用研究都采用SRTM数据开展环境分析。该数据集为基于最新的SRTM V4.1数据经重采样生成,包括1km、500m和250m三种精度的全国一张图数据。数据采用WGS84椭球投影。
1 | gdal_translate -of GTiff N40E120.hgt N40E120.tif |
ASTER GDEM,即先进星载热发射和反射辐射仪全球数字高程模型,与SRTM一样为数字高程DEM,其全球空间分辨率为30米。该数据是根据 NASA的新一代对地观测卫星Terra的详尽观测结果制作完成的。其数据覆盖范围为北纬83°到南纬83°之间的所有陆地区域,达到了地球陆地表面的99%。号称是“迄今最完整的全球地形数据”。
ASTER GDEM:空间分辨率:1弧度秒 (约30 米),精度:垂直精度20米,水平精度30米,SRTM数据的纬度覆盖范围是[-60,60],ASTER GDEM数据的纬度覆盖范围为[-83,83];SRTM的空间分辨率一般为90m,只有美国境内存在空间分辨率为30m的数据;ASTER GDEM的空间分辨率为30m;
通过地图下载软件LocaSpaceViewer下载卫星影像拼接成Tif文件后,会出现黑边问题。此外在Cesium中浏览瓦片地图会出现蓝色区域。下面即分析这两个问题出现的原因和解决方法。
LocaSpaceViewer中可选择行政区域下载卫星影像,这种方式拼接后的tif文件必然会出现黑边,如图1所示。
推荐在LocaSpaceViewer中以瓦片矩形区域下载卫星影像。具体方法为:
该方法也可能会出现黑边,如图2所示。
此外,将tif文件转换成标准瓦片后,在Cesium中浏览瓦片地图会出现蓝色区域,如图3所示。
对问题1,下载不规则边界的地图时,地图下载软件不会自动填充白色或变成透明,所以导致黑边问题1的产生。
对问题2,产生原因目前不清楚,但因黑边的产生很有规则,所以解决方法会比较简单。
对问题3,产生原因是蓝边对应的瓦片不存在。
对问题1,可采用链接用LSV下载的高清地图去黑边教程指明的方法去除黑边,但使用如下命令:
1 | gdal2tiles.py <image> <tilesdir> |
生成瓦片时会报错,因此建议采用瓦片矩形区域下载卫星影像,不要下载行政区域卫星地图。
对问题2,使用如下命令去除黑边:
1 | // 查看tif文件元数据信息 |
对问题3,使用LocaSpaceViewer下载指定级别指定区域的卫星影像即可。
Qt集成OSG开发是指使用Qt开发图形用户界面,封装OSG为Qt的Widget,然后由OSG负责三维展示的解决方案。具体方法如下:
具体配置过程请参考Windows下QT与OSG开发环境配置。
封装OSG为Widget可参考osgQt。osgQt的编译链接与OSG类似。
osgQt仅将OSG封装成简单的Widget,还需研究将OSG封装成自定义控件,从而在QT Designer里方便使用。
SQL Server 2008中有datetime、datetime2、datetimeoffset三种数据类型用于存储时间类型数据。它们之间的差别主要有:
因此,在存储时间信息时,优先使用datetimeoffset数据类型。当使用datetime和datetime2数据类型时,因没有时区信息,很多客户端链接库会将存储的时间默认为UTC时间。例如:
SQL Server2008中存储某人的出生日期为“1970-01-01 00:00:00”,通过sequelizejs框架读取得到出生日期将是“1970-01-01T00:00:00Z”,这里 T 仅仅是分隔日期和时间的符号,没有其他含义,而 Z 表示 UTC 时间。
这时需要根据自己所在时区对时间进行修正。最简单的方法是在SQL语句中进行修正,代码如下:
1 | select birthday1, dateadd(hour,-8,birthday) from students |
有时时间会以字符串类型存储在数据库中,当需要对时间进行比较时,需要先将字符串类型时间转换成日期类型,可使用convert函数进行转换,例如:
1 | # 字符串转日期 |
其中date是要转换的数据的类型,’2005-12-31 23:59:59.9999999’是被转换的数据。
在mssql中比较两个日期的大小,可采用DATEDIFF函数,其语法如下:
1 | DATEDIFF ( datepart , startdate , enddate ) |
其中,datepart是时间单位,startdate和enddate是合法的日期表达式。datediff用法示例如下:
1 | SELECT DATEDIFF(year, '2005-12-31 23:59:59.9999999', '2006-01-01 00:00:00.0000000'); |
JavaScript并没有提供对象的复制方法,只能借助第三方库或自己实现对象的深度克隆。具体方法如下:
实现JavaScript库的深度克隆的第三方库主要有:
在 Underscore 中有这样一个方法:_.clone(),这个方法实际上是一种浅复制 (shallow-copy),所有嵌套的对象和数组都是直接复制引用而并没有进行深复制。其代码如下:
1 | // Create a (shallow-cloned) duplicate of an object. |
在 jQuery 中也有这么一个叫 $.clone() 的方法,可是它并不是用于一般的 JS 对象的深复制,而是用于 DOM 对象。与 Underscore 类似,可以通过 $.extend() 方法来完成深复制。值得庆幸的是,在 jQuery 中可以通过添加一个参数来实现递归extend。调用$.extend(true, {}, …)就可以实现深复制。
1 | var x = { |
在lodash中关于复制的方法有两个,分别是.clone()和.cloneDeep()。其中.clone(obj, true)等价于.cloneDeep(obj)。
针对纯 JSON 数据对象的深复制,使用 JSON 全局对象的 parse 和 stringify 方法来实现深复制也算是一个简单讨巧的方法,但它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。
1 | function jsonClone(obj) { |
下面是递归实现对象深度克隆的可用方法。
1 | function clone(obj) { |
图片、声音、视频等大文件在后台数据库中的存储管理方式通常有两种:
这两种文件存储管理方式有各自优缺点, 选择哪种方法存储大文件应根据具体情况而定。
文件系统存储管理方式将文件存储到磁盘中,在数据库中使用varchar类型记录文件路径。该方法的缺点主要有:
当删除文件路径时,不支持自动删除对应文件。
如果改变文件内容或删除文件,这些操作将立刻被其他客户端看到。
数据库记录能够恢复,但文件删除不能。
文件不支持数据库的备份工具
文件不支持SQL的访问权限设置
文件不是SQL数据类型
数据库不会验证文件路径是否有效。当文件移动、重命名、删除时,数据不会自动更新路径。
数据库通常支持BLOB类型,可用于存储任何二进制数据。数据库中存储文件,其优点对应文件系统管理存储方式的缺点。同样,数据库中存储文件也存在一些缺点:
当只涉及较小规模图片、音频的存储时,建议直接存放到数据库中。当涉及视频大文件存储时,建议还是用文件系统存储。
最近想尝试使用QT开发图形用户界面,OSG负责三维展示的解决方案,于是研究了一下Windows下OSG的编译安装配置,以及在QT中集成OSG的配置过程。过程记录如下:
QT+OSG开发环境配置将使用QT Creator集成环境进行图形用户界面开发,使用Desktop Qt 5.9.8 MSVC2017 64构建套件编译链接调试C++程序。
下载安装Visual Studio Community 2017的“使用C++的桌面开发”工作负载。这将为QT提供MSVC2017 64bit编译套件。
没有安装CDB调试器,QT的Desktop Qt 5.9.8 MSVC2017 64构建套件前会出现感叹号,并且使用该构建套件调试时会报“Unable to create a debugging engine”错误。CDB调试器下载安装方法参考QT - OSG 开发环境配置
QT 5.9.8是 LTS 版本,其下载安装请参考Qt5配置开源GSL数学库。需要注意的是,QT 5.9.8安装过程,选择MSVC2017 64bit编译组件,如图1所示。
到 cmake 官网下载安装 cmake,用于osg的编译安装。
Windows平台编译安装OSG需下载从OSG官网以下资料:
打开cmake,选择osg源代码路径和osg源代码构建的路径,再点击“configure”按钮,选择Visual Studio 15 2017 Win64编译套件,配置osg如图2所示。
需要配置的地方主要有:
配置完成后,再持续点击“configure”按钮,知道红色警告消失。然后点击“Generate”按钮生成vs2017工程。
在cmake中点击“Open Project”按钮,即使用 VS2017 打开 build 文件夹下的工程,点击:生成 -> 批生成 -> 生成 Debug 和 Release 版本的 All_BUILD 即可。
右击解决方案的 Install 项目,点击生成,然后 Debug 平台的库文件就开始安装了,同理选择 Release 平台再重复安装。之后就可以在安装路径中看到编译的库文件了,安装路径由变量CMAKE_INSTALL_PREFIX的配置决定。
OSG官方文档 Windows Compiling with Visual Studio指出,OSG安装完成后需要配置如下环境变量:
1 | OSG_ROOT points to the base of the OSG file structure (the directory that contains include, src etc. subdirectories) |
打开 cmd 命令行,输入osgversion,输出当前 osg 版本说明环境变量配置成功了,再来看看能不能导入模型文件,接着键入:
1 | osgviewer cow.osg |
这里可能会提示系统缺少 zlibd.dll,解决方法很简单:
这时重新键入上面的命令,就可以看到一头经典的 3D 牛了,说明 OSG 安装成功了。
新建一个 QT 的控制台项目,编辑 .pro 文件,加入 OSG 库的路径:
1 | win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../../../OpenSceneGraph/lib/ -lOpenThreads -losg -losgDB -losgUtil -losgGA -losgViewer -losgText |
main.cpp 如下:
1 | #include <osgViewer/Viewer> |
运行,出来一头 3D 牛,即配置成功。
突然需要在一台很久不用的Win7计算机上差一点资料,可忘记了登录密码,只能重置密码。过程记录如下:
借助手头的一张linux live cd,从光盘启动,进入linux系统,然后找到windows系统盘分区,在C:\Windows\System32下,将cmd.exe重命名为Magnify.exe,然后系统重启,登录Windows操作系统,在登录界面中,调用辅助工具放大镜,即打开命令提示符,输入如下命令重置Windows账户密码:
1 | // 查看Windows账户 |
或者采用如下命令新建登录账户:
1 | // 创建用户 |