Nginx 的线程池与性能剖析【转载】

正如我们所知,NGINX采用了异步、事件驱动的方法来处理连接。这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求。为此,NGINX工作在非阻塞的socket模式下,并使用了epoll 和 kqueue这样有效的方法。

因为满负载进程的数量很少(通常每核CPU只有一个)而且恒定,所以任务切换只消耗很少的内存,而且不会浪费CPU周期。通过NGINX本身的实例,这种方法的优点已经为众人所知。NGINX可以非常好地处理百万级规模的并发请求。

NGINX引入线程池 性能提升9倍

每个进程都消耗额外的内存,而且每次进程间的切换都会消耗CPU周期并丢弃CPU高速缓存中的数据。

但是,异步、事件驱动方法仍然存在问题。或者,我喜欢将这一问题称为“敌兵”,这个敌兵的名字叫阻塞(blocking)。不幸的是,很多第三方模块使用了阻塞调用,然而用户(有时甚至是模块的开发者)并不知道阻塞的缺点。阻塞操作可以毁掉NGINX的性能,我们必须不惜一切代价避免使用阻塞。

即使在当前官方的NGINX代码中,依然无法在全部场景中避免使用阻塞,NGINX1.7.11中实现的线程池机制解决了这个问题。我们将在后面讲述这个线程池是什么以及该如何使用。现在,让我们先和我们的“敌兵”进行一次面对面的碰撞。

2. 问题

首先,为了更好地理解这一问题,我们用几句话说明下NGINX是如何工作的。

通常情况下,NGINX是一个事件处理器,即一个接收来自内核的所有连接事件的信息,然后向操作系统发出做什么指令的控制器。实际上,NGINX干了编排操作系统的全部脏活累活,而操作系统做的是读取和发送字节这样的日常工作。所以,对于NGINX来说,快速和及时的响应是非常重要的。

NGINX引入线程池 性能提升9倍

工作进程监听并处理来自内核的事件

事件可以是超时、socket读写就绪的通知,或者发生错误的通知。NGINX接收大量的事件,然后一个接一个地处理它们,并执行必要的操作。因此,所有的处理过程是通过一个线程中的队列,在一个简单循环中完成的。NGINX从队列中取出一个事件并对其做出响应,比如读写socket。在多数情况下,这种方式是非常快的(也许只需要几个CPU周期,将一些数据复制到内存中),NGINX可以在一瞬间处理掉队列中的所有事件。

NGINX引入线程池 性能提升9倍

所有处理过程是在一个简单的循环中,由一个线程完成

但是,如果NGINX要处理的操作是一些又长又重的操作,又会发生什么呢?整个事件处理循环将会卡住,等待这个操作执行完毕。

因此,所谓“阻塞操作”是指任何导致事件处理循环显著停止一段时间的操作。操作可以由于各种原因成为阻塞操作。例如,NGINX可能因长时间、CPU密集型处理,或者可能等待访问某个资源(比如硬盘,或者一个互斥体,亦或要从处于同步方式的数据库获得相应的库函数调用等)而繁忙。关键是在处理这样的操作期间,工作进程无法做其他事情或者处理其他事件,即使有更多的可用系统资源可以被队列中的一些事件所利用。

我们来打个比方,一个商店的营业员要接待他面前排起的一长队顾客。队伍中的第一位顾客想要的某件商品不在店里而在仓库中。这位营业员跑去仓库把东西拿来。现在整个队伍必须为这样的配货方式等待数个小时,队伍中的每个人都很不爽。你可以想见人们的反应吧?队伍中每个人的等待时间都要增加这些时间,除非他们要买的东西就在店里。

NGINX引入线程池 性能提升9倍

队伍中的每个人不得不等待第一个人的购买

在NGINX中会发生几乎同样的情况,比如当读取一个文件的时候,如果该文件没有缓存在内存中,就要从磁盘上读取。从磁盘(特别是旋转式的磁盘)读取是很慢的,而当队列中等待的其他请求可能不需要访问磁盘时,它们也得被迫等待。导致的结果是,延迟增加并且系统资源没有得到充分利用。

NGINX引入线程池 性能提升9倍

一个阻塞操作足以显著地延缓所有接下来的操作

一些操作系统为读写文件提供了异步接口,NGINX可以使用这样的接口(见AIO指令)。FreeBSD就是个很好的例子。不幸的是,我们不能在Linux上得到相同的福利。虽然Linux为读取文件提供了一种异步接口,但是存在明显的缺点。其中之一是要求文件访问和缓冲要对齐,但NGINX很好地处理了这个问题。但是,另一个缺点更糟糕。异步接口要求文件描述符中要设置O_DIRECT标记,就是说任何对文件的访问都将绕过内存中的缓存,这增加了磁盘的负载。在很多场景中,这都绝对不是最佳选择。

为了有针对性地解决这一问题,在NGINX 1.7.11中引入了线程池。默认情况下,NGINX+还没有包含线程池,但是如果你想试试的话,可以联系销售人员,NGINX+ R6是一个已经启用了线程池的构建版本。

现在,让我们走进线程池,看看它是什么以及如何工作的。

3. 线程池

让我们回到那个可怜的,要从大老远的仓库去配货的售货员那儿。这回,他已经变聪明了(或者也许是在一群愤怒的顾客教训了一番之后,他才变得聪明的?),雇用了一个配货服务团队。现在,当任何人要买的东西在大老远的仓库时,他不再亲自去仓库了,只需要将订单丢给配货服务,他们将处理订单,同时,我们的售货员依然可以继续为其他顾客服务。因此,只有那些要买仓库里东西的顾客需要等待配货,其他顾客可以得到即时服务。

NGINX引入线程池 性能提升9倍

传递订单给配货服务不会阻塞队伍

对NGINX而言,线程池执行的就是配货服务的功能。它由一个任务队列和一组处理这个队列的线程组成。
当工作进程需要执行一个潜在的长操作时,工作进程不再自己执行这个操作,而是将任务放到线程池队列中,任何空闲的线程都可以从队列中获取并执行这个任务。

NGINX引入线程池 性能提升9倍

工作进程将阻塞操作卸给线程池

那么,这就像我们有了另外一个队列。是这样的,但是在这个场景中,队列受限于特殊的资源。磁盘的读取速度不能比磁盘产生数据的速度快。不管怎么说,至少现在磁盘不再延误其他事件,只有访问文件的请求需要等待。

“从磁盘读取”这个操作通常是阻塞操作最常见的示例,但是实际上,NGINX中实现的线程池可用于处理任何不适合在主循环中执行的任务。

目前,卸载到线程池中执行的两个基本操作是大多数操作系统中的read()系统调用和Linux中的sendfile()。接下来,我们将对线程池进行测试(test)和基准测试(benchmark),在未来的版本中,如果有明显的优势,我们可能会卸载其他操作到线程池中。

4. 基准测试

现在让我们从理论过度到实践。我们将进行一次模拟基准测试(synthetic benchmark),模拟在阻塞操作和非阻塞操作的最差混合条件下,使用线程池的效果。

另外,我们需要一个内存肯定放不下的数据集。在一台48GB内存的机器上,我们已经产生了每文件大小为4MB的随机数据,总共256GB,然后配置NGINX,版本为1.9.0。

配置很简单:

worker_processes 16;events {accept_mutex off;
}http {include mime.types;default_type application/octet-stream;access_log off;sendfile on;sendfile_max_chunk 512k;server {listen 8000;location / {root /storage;}}
}

如上所示,为了达到更好的性能,我们调整了几个参数:禁用了logging和accept_mutex,同时,启用了sendfile并设置了sendfile_max_chunk的大小。最后一个指令可以减少阻塞调用sendfile()所花费的最长时间,因为NGINX不会尝试一次将整个文件发送出去,而是每次发送大小为512KB的块数据。

这台测试服务器有2个Intel Xeon E5645处理器(共计:12核、24超线程)和10-Gbps的网络接口。磁盘子系统是由4块西部数据WD1003FBYX 磁盘组成的RAID10阵列。所有这些硬件由Ubuntu服务器14.04.1 LTS供电。

NGINX引入线程池 性能提升9倍

为基准测试配置负载生成器和NGINX

客户端有2台服务器,它们的规格相同。在其中一台上,在wrk中使用Lua脚本创建了负载程序。脚本使用200个并行连接向服务器请求文件,每个请求都可能未命中缓存而从磁盘阻塞读取。我们将这种负载称作随机负载。

在另一台客户端机器上,我们将运行wrk的另一个副本,使用50个并行连接多次请求同一个文件。因为这个文件将被频繁地访问,所以它会一直驻留在内存中。在正常情况下,NGINX能够非常快速地服务这些请求,但是如果工作进程被其他请求阻塞的话,性能将会下降。我们将这种负载称作恒定负载。

性能将由服务器上ifstat监测的吞吐率(throughput)和从第二台客户端获取的wrk结果来度量。

现在,没有使用线程池的第一次运行将不会带给我们非常振奋的结果:

% ifstat -bi eth2
eth2
Kbps in  Kbps out
5531.24  1.03e+06
4855.23  812922.7
5994.66  1.07e+06
5476.27  981529.3
6353.62  1.12e+06
5166.17  892770.3
5522.81  978540.8
6208.10  985466.7
6370.79  1.12e+06
6123.33  1.07e+06

如上所示,使用这种配置,服务器产生的总流量约为1Gbps。从下面所示的top输出,我们可以看到,工作进程的大部分时间花在阻塞I/O上(它们处于top的D状态):

top - 10:40:47 up 11 days,  1:32,  1 user,  load average: 49.61, 45.77 62.89
Tasks: 375 total,  2 running, 373 sleeping,  0 stopped,  0 zombie
%Cpu(s):  0.0 us,  0.3 sy,  0.0 ni, 67.7 id, 31.9 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:  49453440 total, 49149308 used,   304132 free,    98780 buffers
KiB Swap: 10474236 total,    20124 used, 10454112 free, 46903412 cached MemPID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND4639 vbart    20   0   47180  28152     496 D   0.7  0.1  0:00.17 nginx4632 vbart    20   0   47180  28196     536 D   0.3  0.1  0:00.11 nginx4633 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.11 nginx4635 vbart    20   0   47180  28136     480 D   0.3  0.1  0:00.12 nginx4636 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.14 nginx4637 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.10 nginx4638 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx4640 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx4641 vbart    20   0   47180  28324     540 D   0.3  0.1  0:00.13 nginx4642 vbart    20   0   47180  28208     536 D   0.3  0.1  0:00.11 nginx4643 vbart    20   0   47180  28276     536 D   0.3  0.1  0:00.29 nginx4644 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.11 nginx4645 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.17 nginx4646 vbart    20   0   47180  28204     536 D   0.3  0.1  0:00.12 nginx4647 vbart    20   0   47180  28208     532 D   0.3  0.1  0:00.17 nginx4631 vbart    20   0   47180    756     252 S   0.0  0.1  0:00.00 nginx4634 vbart    20   0   47180  28208     536 D   0.0  0.1  0:00.11 nginx4648 vbart    20   0   25232   1956    1160 R   0.0  0.0  0:00.08 top
25921 vbart    20   0  121956   2232    1056 S   0.0  0.0  0:01.97 sshd
25923 vbart    20   0   40304   4160    2208 S   0.0  0.0  0:00.53 zsh

在这种情况下,吞吐率受限于磁盘子系统,而CPU在大部分时间里是空闲的。从wrk获得的结果也非常低:

Running 1m test @ http://192.0.2.1:8000/1/1/112 threads and 50 connectionsThread Stats   Avg    Stdev     Max  +/- StdevLatency     7.42s  5.31s   24.41s   74.73%Req/Sec     0.15    0.36     1.00    84.62%488 requests in 1.01m, 2.01GB read
Requests/sec:      8.08
Transfer/sec:     34.07MB

请记住,文件是从内存送达的!第一个客户端的200个连接创建的随机负载,使服务器端的全部的工作进程忙于从磁盘读取文件,因此产生了过大的延迟,并且无法在合理的时间内处理我们的请求。

现在,我们的线程池要登场了。为此,我们只需在location块中添加aio threads指令:

location / {root /storage;aio threads;
}

接着,执行NGINX reload重新加载配置。

然后,我们重复上述的测试:

% ifstat -bi eth2
eth2
Kbps in  Kbps out
60915.19  9.51e+06
59978.89  9.51e+06
60122.38  9.51e+06
61179.06  9.51e+06
61798.40  9.51e+06
57072.97  9.50e+06
56072.61  9.51e+06
61279.63  9.51e+06
61243.54  9.51e+06
59632.50  9.50e+06

现在,我们的服务器产生的流量是9.5Gbps,相比之下,没有使用线程池时只有约1Gbps!

理论上还可以产生更多的流量,但是这已经达到了机器的最大网络吞吐能力,所以在这次NGINX的测试中,NGINX受限于网络接口。工作进程的大部分时间只是休眠和等待新的时间(它们处于top的S状态):

top - 10:43:17 up 11 days,  1:35,  1 user,  load average: 172.71, 93.84, 77.90
Tasks: 376 total,  1 running, 375 sleeping,  0 stopped,  0 zombie
%Cpu(s):  0.2 us,  1.2 sy,  0.0 ni, 34.8 id, 61.5 wa,  0.0 hi,  2.3 si,  0.0 st
KiB Mem:  49453440 total, 49096836 used,   356604 free,    97236 buffers
KiB Swap: 10474236 total,    22860 used, 10451376 free, 46836580 cached MemPID USER     PR  NI    VIRT    RES     SHR S  %CPU %MEM    TIME+ COMMAND4654 vbart    20   0  309708  28844     596 S   9.0  0.1  0:08.65 nginx4660 vbart    20   0  309748  28920     596 S   6.6  0.1  0:14.82 nginx4658 vbart    20   0  309452  28424     520 S   4.3  0.1  0:01.40 nginx4663 vbart    20   0  309452  28476     572 S   4.3  0.1  0:01.32 nginx4667 vbart    20   0  309584  28712     588 S   3.7  0.1  0:05.19 nginx4656 vbart    20   0  309452  28476     572 S   3.3  0.1  0:01.84 nginx4664 vbart    20   0  309452  28428     524 S   3.3  0.1  0:01.29 nginx4652 vbart    20   0  309452  28476     572 S   3.0  0.1  0:01.46 nginx4662 vbart    20   0  309552  28700     596 S   2.7  0.1  0:05.92 nginx4661 vbart    20   0  309464  28636     596 S   2.3  0.1  0:01.59 nginx4653 vbart    20   0  309452  28476     572 S   1.7  0.1  0:01.70 nginx4666 vbart    20   0  309452  28428     524 S   1.3  0.1  0:01.63 nginx4657 vbart    20   0  309584  28696     592 S   1.0  0.1  0:00.64 nginx4655 vbart    20   0  30958   28476     572 S   0.7  0.1  0:02.81 nginx4659 vbart    20   0  309452  28468     564 S   0.3  0.1  0:01.20 nginx4665 vbart    20   0  309452  28476     572 S   0.3  0.1  0:00.71 nginx5180 vbart    20   0   25232   1952    1156 R   0.0  0.0  0:00.45 top4651 vbart    20   0   20032    752     252 S   0.0  0.0  0:00.00 nginx
25921 vbart    20   0  121956   2176    1000 S   0.0  0.0  0:01.98 sshd
25923 vbart    20   0   40304   3840    2208 S   0.0  0.0  0:00.54 zsh

如上所示,基准测试中还有大量的CPU资源剩余。

wrk的结果如下:

Running 1m test @ http://192.0.2.1:8000/1/1/112 threads and 50 connectionsThread Stats   Avg      Stdev     Max  +/- StdevLatency   226.32ms  392.76ms   1.72s   93.48%Req/Sec    20.02     10.84    59.00    65.91%15045 requests in 1.00m, 58.86GB read
Requests/sec:    250.57
Transfer/sec:      0.98GB

服务器处理4MB文件的平均时间从7.42秒降到226.32毫秒(减少了33倍),每秒请求处理数提升了31倍(250 vs 8)!

对此,我们的解释是请求不再因为工作进程被阻塞在读文件,而滞留在事件队列中,等待处理,它们可以被空闲的进程处理掉。只要磁盘子系统能做到最好,就能服务好第一个客户端上的随机负载,NGINX可以使用剩余的CPU资源和网络容量,从内存中读取,以服务于上述的第二个客户端的请求。

5. 依然没有银弹

在抛出我们对阻塞操作的担忧并给出一些令人振奋的结果后,可能大部分人已经打算在你的服务器上配置线程池了。先别着急。

实际上,最幸运的情况是,读取和发送文件操作不去处理缓慢的硬盘驱动器。如果我们有足够多的内存来存储数据集,那么操作系统将会足够聪明地在被称作“页面缓存”的地方,缓存频繁使用的文件。

“页面缓存”的效果很好,可以让NGINX在几乎所有常见的用例中展示优异的性能。从页面缓存中读取比较快,没有人会说这种操作是“阻塞”。而另一方面,卸载任务到一个线程池是有一定开销的。

因此,如果内存有合理的大小并且待处理的数据集不是很大的话,那么无需使用线程池,NGINX已经工作在最优化的方式下。

卸载读操作到线程池是一种适用于非常特殊任务的技术。只有当经常请求的内容的大小,不适合操作系统的虚拟机缓存时,这种技术才是最有用的。至于可能适用的场景,比如,基于NGINX的高负载流媒体服务器。这正是我们已经模拟的基准测试的场景。

我们如果可以改进卸载读操作到线程池,将会非常有意义。我们只需要知道所需的文件数据是否在内存中,只有不在内存中时,读操作才应该卸载到一个单独的线程中。

再回到售货员那个比喻的场景中,这回,售货员不知道要买的商品是否在店里,他必须要么总是将所有的订单提交给配货服务,要么总是亲自处理它们。

人艰不拆,操作系统缺少这样的功能。第一次尝试是在2010年,人们试图将这一功能添加到Linux作为fincore()系统调用,但是没有成功。后来还有一些尝试,是使用RWF_NONBLOCK标记作为preadv2()系统调用来实现这一功能(详情见LWN.net上的非阻塞缓冲文件读取操作和异步缓冲读操作)。但所有这些补丁的命运目前还不明朗。悲催的是,这些补丁尚没有被内核接受的主要原因,貌似是因为旷日持久的撕逼大战(bikeshedding)。

另一方面,FreeBSD的用户完全不必担心。FreeBSD已经具备足够好的读文件取异步接口,我们应该用这个接口而不是线程池。

6. 配置线程池

所以,如果你确信在你的场景中使用线程池可以带来好处,那么现在是时候深入了解线程池的配置了。

线程池的配置非常简单、灵活。首先,获取NGINX 1.7.11或更高版本的源代码,使用–with-threads配置参数编译。在最简单的场景中,配置看起来很朴实。我们只需要在http、 server,或者location上下文中包含aio threads指令即可:

aio threads;

这是线程池的最简配置。实际上的精简版本示例如下:

thread_pool default threads=32 max_queue=65536;
aio threads=default;

这里定义了一个名为“default”,包含32个线程,任务队列最多支持65536个请求的线程池。如果任务队列过载,NGINX将输出如下错误日志并拒绝请求:

thread pool "NAME" queue overflow: N tasks waiting

错误输出意味着线程处理作业的速度有可能低于任务入队的速度了。你可以尝试增加队列的最大值,但是如果这无济于事,那么这说明你的系统没有能力处理如此多的请求了。

正如你已经注意到的,你可以使用thread_pool指令,配置线程的数量、队列的最大值,以及线程池的名称。最后要说明的是,可以配置多个独立的线程池,将它们置于不同的配置文件中,用做不同的目的:

http {thread_pool one threads=128 max_queue=0;thread_pool two threads=32;server {location /one {aio threads=one;}location /two {aio threads=two;}}
…
}

如果没有指定max_queue参数的值,默认使用的值是65536。如上所示,可以设置max_queue为0。在这种情况下,线程池将使用配置中全部数量的线程,尽可能地同时处理多个任务;队列中不会有等待的任务。

现在,假设我们有一台服务器,挂了3块硬盘,我们希望把该服务器用作“缓存代理”,缓存后端服务器的全部响应信息。预期的缓存数据量远大于可用的内存。它实际上是我们个人CDN的一个缓存节点。毫无疑问,在这种情况下,最重要的事情是发挥硬盘的最大性能。

我们的选择之一是配置一个RAID阵列。这种方法毁誉参半,现在,有了NGINX,我们可以有其他的选择:

# 我们假设每块硬盘挂载在相应的目录中:/mnt/disk1、/mnt/disk2、/mnt/disk3proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024Guse_temp_path=off;
proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024Guse_temp_path=off;
proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024Guse_temp_path=off;thread_pool pool_1 threads=16;
thread_pool pool_2 threads=16;
thread_pool pool_3 threads=16;split_clients $request_uri $disk {33.3%     1;33.3%     2;*         3;
}location / {proxy_pass http://backend;proxy_cache_key $request_uri;proxy_cache cache_$disk;aio threads=pool_$disk;sendfile on;
}

在这份配置中,使用了3个独立的缓存,每个缓存专用一块硬盘,另外,3个独立的线程池也各自专用一块硬盘。

缓存之间(其结果就是磁盘之间)的负载均衡使用split_clients模块,split_clients非常适用于这个任务。

在 proxy_cache_path指令中设置use_temp_path=off,表示NGINX会将临时文件保存在缓存数据的同一目录中。这是为了避免在更新缓存时,磁盘之间互相复制响应数据。

这些调优将带给我们磁盘子系统的最大性能,因为NGINX通过单独的线程池并行且独立地与每块磁盘交互。每块磁盘由16个独立线程和读取和发送文件专用任务队列提供服务。

我敢打赌,你的客户喜欢这种量身定制的方法。请确保你的磁盘也持有同样的观点。

这个示例很好地证明了NGINX可以为硬件专门调优的灵活性。这就像你给NGINX下了一道命令,让机器和数据用最佳姿势来搞基。而且,通过NGINX在用户空间中细粒度的调优,我们可以确保软件、操作系统和硬件工作在最优模式下,尽可能有效地利用系统资源。

7. 总结

综上所述,线程池是一个伟大的功能,将NGINX推向了新的性能水平,除掉了一个众所周知的长期危害——阻塞——尤其是当我们真正面对大量内容的时候。

甚至,还有更多的惊喜。正如前面提到的,这个全新的接口,有可能没有任何性能损失地卸载任何长期阻塞操作。NGINX在拥有大量的新模块和新功能方面,开辟了一方新天地。许多流行的库仍然没有提供异步非阻塞接口,此前,这使得它们无法与NGINX兼容。我们可以花大量的时间和资源,去开发我们自己的无阻塞原型库,但这么做始终都是值得的吗?现在,有了线程池,我们可以相对容易地使用这些库,而不会影响这些模块的性能。

 

原文地址:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt158

转载于:https://www.cnblogs.com/andrew-xie/p/5299410.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/491045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Halcon算子学习:XLD几种边缘连接函数

Halcon算子学习:XLD几种边缘连接函数 1.union_cotangential_contours_xld (Contours : UnionContours : FitClippingLength, FitLength, MaxTangAngle, MaxDist, MaxDistPerp, MaxOverlap, Mode : ) 根据输入的轮廓的端点处的局部曲率,连接曲率满足一定…

交换机与路由器大战已结束,超大规模计算公司胜出

来源:云头条作者:Timothy Prickett Morgan是The Next Platform的联合创始人兼合作编辑。很难说会先发生什么:交换和路由将合并,还是说会出现两种功能兼具的独立网络操作系统。Arrcus去年7月推出了一种可在商用芯片上同时提供交换功…

MATLAB的GUI中给坐标轴四周加上边框

在MATLAB的GUI中如何为坐标轴四边加上方框,效果如下图: 首先打开一个GUI文件,建立一个坐标轴,如下图所示: 双击该坐标轴,在弹出的检查器中,在Box那一栏后面勾上,如下图所示&#xff…

Halcon学习笔记:Halcon标定步骤-3d_coordinates.hdev示例

Halcon标定步骤 1.设置相机内部参数的初始值 StartCamPar : [0.016,0,0.0000074,0.0000074,326,247,652,494] set_calib_data_cam_param (CalibDataID, 0, ‘area_scan_division’, StartCamPar) 1.1 相机型号 (1)面阵(2)线阵…

马斯克的脑机接口能如愿以偿吗?

来源:都世民科学网博客摘要:本文主要讨论马斯克的脑机接口设备更新的要点,他发布的计划真能如愿以偿吗?可能会出现什么问题,将如何应对?是能治病,还是导致新的疾病?关键词&#xff1…

360度全景制作

最近总有人说要做360全景啥的。趁着有空研究了下,首先需要一张全景图,然后用pano2vr这个工具生成。 转载于:https://www.cnblogs.com/c1ndy/p/5301043.html

MATLAB的GUI如何清空坐标轴的图像

下面这两句即可实现清空GUI中的坐标轴 axes(handles.axes1); %指定需要清空的坐标轴 cla reset; 但假如你之前的坐标轴四周是有边框的,而且是没有坐标刻度和坐标值的,如下图所示,你会发现通过上面这两句代码虽然清空了坐标轴的内容&#xf…

Halcon学习笔记:1D Measuring一维测量_fuse.hdev灯丝测量示例

Halcon学习笔记:1D Measuring一维测量_fuse.hdev灯丝测量示例 一.基本概念 基本流程为: 获取图像对图像进行灰度值标定 (针对相机获取的图像灰度和输入能量之间的非线性关系,获得相机对输入能量的线性函数)排列感兴趣…

尘埃落定!苹果 10 亿美元入局 5G,Intel 挥别 9 年基带业务

来源: 雷锋网 雷锋网消息,2019 年 7 月 25 日,苹果公司和 Intel 共同宣布,双方已经正式达成收购协议,Apple 将收购 Intel 智能手机调节解调器业务的绝大部分,收购价格正是此前《华尔街日报》爆料出的 10 亿…

[原创]android使用代码生成LayerDrawable的方法和注意事项

为了有更好的UI体验,一般我们会把button、textview等控件的背景设置上阴影。传统的做法是美工提供一张具有阴影效果的nine patch图,然后将其在xml文件中添加到background属性。这种做法没有问题,不过缺乏灵活性。 图1.使用代码生成的具有“阴…

Visual Studio 2013运行的结果一闪而过

在Visual Studio 2013中运行一个程序,发现输出结果的窗口DOS一闪而过,解决的办法是通过按CtrlF5键来运行程序即可

数字图像处理:四连通域与八连通域

四连通域与八连通域 1、所谓四连通区域或四邻域,是指对应像素位置的上、下、左、右,是紧邻的位置。共4个方向,所以称之为四连通区域,又叫四邻域。 2、所谓八连通区域或八邻域,是指对应位置的上、下、左、右、左上、右…

深度学习奠基人特伦斯:美国学界已经找到了解释人工智能“黑盒子”的方法...

(图片来源:壹图网)来源:今日头条经济观察网 记者 宋笛 “是的,目前美国数学家已经找到了解释深度学习黑盒子的方法,但是还没有发表,所以我不能透露更多。”7月25日 ,美国“四院院士”…

this和static

1 【this】 2 指的是明确的标记本类的结构3 当前正在调用类中方法的对象,不是一个固定的4 java中以“{}”为界限。如果现在属性名称和参数名称重名,那么默认情况下,如果没有加任何的限制,指的是最近的“{}”内的变量名…

vs2013配置opencv2.4.9后出现找不到opencv_core249d.dll和opencv_highgui249d.dll问题

问题:vs2013配置opencv2.4.9后,运行程序,出现找不到opencv_core249d.dll和opencv_highgui249d.dll问题 解决办法:首先将opencv_core249d.dll;opencv_highgui249d.dll;opencv_imgproc249d.dll三个文件从“OpenCV的安装…

数字图像处理:图像的频域

数字图像处理:图像的频域 一、图像高频信号和低频信号的理解 1.1 图像中的低频信号和高频信号也叫做低频分量和高频分量。 1)空间频率指的是图像中灰度值相对它的邻居点变化方式。如果一副图像中灰度从一边到另一边变化很小,那就说这副图像…

点击Cell中的按钮时,如何取所在的Cell

点击Cell中的按钮时,如何取所在的Cell: -(void)OnTouchBtnInCell:(UIButton *)btn { CGPoint point btn.center; point [table convertPoint:point fromView:btn.superview]; NSIndexPath* indexpath [table indexPathForRowAtPoint:point]; UITableV…

opencv2.4.9报错找不到opencv_video249d.dll

解决方法:将opencv_video249d.dll从“opencv安装路径\opencv\build\x86\vc12\bin”复制到“C:\Windows\SysWOW64”(若是32位系统则复制到C:\Windows\System32) opencv_video249d.dll中的249是版本号,代表OpenCV版本是2.4.9

Halcon算子学习:图像阈值分割-threshold、binary_threshold、dyn_threshold算子

Halcon算子学习:图像阈值分割算子前言1.threshold-全局固定阈值分割2.Binary Threshold-自动全局阈值分割3.dyn_threshold-局部动态阈值分割小结:4.var_threshold算子-均值和标准偏差局部阈值分割5.dual_threshold-双重阈值分割(有符号图像的…

起底软银帝国:芯片、机器人、棒球队无所不投

来源:巴伦全球投资企鹅号划重点:孙正义喜欢将软银的投资组合比作银河系中的星星,称它们“将持续闪耀300年。”孙正义对阿里巴巴的投资可能是所有投资人做过的最好的投资之一。软银并非真正意义上的企业集团,其结构也不是封闭式基金…