【译】提高nginx9倍性能的线程池

news/2023/12/1 8:28:17

在nginx的官网看到一篇介绍nginx原理的文章,这篇文章比较老了是15年发布的,国内有人翻译过但是有些小瑕疵,这里更正出来发布在我个人的文章里供大家参考,这篇文章详细的介绍了nginx线程池的原理以及设计思路,在最后通过详细的实验数据来告诉我们通过线程池提升的性能以及分析了应该使用线程池的场景。在日后的其他领域依然很有借鉴意义。

点我看源文

大家都知道NGINX使用异步以及事件驱动的方式来处理请求,这意味着,我们不会为每个请求创建另一个专用的进程或线程(比如像那些使用了传统架构的服务器)。而是选择一个工作进程来处理多个连接请求。为了实现这样的特性,NGINX使用非阻塞模式下的socket以及选择了更有效率的系统调用比如epoll和kqueue。
满负载的进程数量很少(通常是每个cpu核心只占一个)而且是恒定的,这样消耗了更少的内存以及cpu时间片没有被浪费在任务切换上。这个方法的优点可以通过nginx这个例子来反映出来。它可以非常好的并发处理上百万的请求规模并且处理的效果还不错。

clipboard.png
每个进程消耗额外的内存,每次进程之间的切换会消耗CPU周期以及产生cpu缓存垃圾

但是异步,事件驱动这类模型同样存在问题。或者我更喜欢把这样的问题称作敌人。敌人的名字叫做:阻塞。不幸的是,许多第三方模块都使用了阻塞式的调用,而且用户(有时候甚至模块的开发者)没有意识到这么做的弊端。阻塞式的操作会毁掉NGINX的性能所以无论如何一定要被阻止。

但是在现在的官方版本的NGINX源代码中也不可能在任何情况下避免阻塞,为了解决这个问题新的“线程池”特性被引入到NGINX version 1.7.11以及NGINX PLUS Release 7当中来.它是什么以及它如何使用这个我们稍后讨论,现在我们来面对我们的敌人了。

编辑注-想对NGINX PLUS R7有个大概了解可以在我们的博客看到更多
想看NGINX PLUS R7其他特性的具体分析,可以看下边列出来的博客:

  • HTTP/2 Now Fully Supported in NGINX Plus

  • Socket Sharding in NGINX

  • The New NGINX Plus Dashboard in Release 7

  • TCP Load Balancing in NGINX Plus R7

问题

首先,为了更好的了解NGINX我们会用几句话解释一下它是如何工作的。
大体来说,NGINX是一个事件处理器,一个从内核接收目前的连接信息然后发送接下来做的什么命令给操作系统的控制器。实际上NGINX做的脏活累活是通过协调操作系统来做的,本质上是操作系统在做周期性的读或者写。所以对NGINX来说反应的快速及时是很重要的。

clipboard.png
工作进程监听以及处理从内核传过来的事件

这些事件可能会超时,通知socket读就绪或者写就绪,或者通知一个错误的产生。NGINX接收到一串事件接着一个一个的处理它们。所以所有的操作可以在一个线程的一个队列的一次简单循环当中完成。NGINX从队列当中弹出一个事件然后通过比如读写socket来做后续处理。在大多数情况下,这个操作会很快(也许这个操作只需要很少的cpu时间片去从内存当中copy一些数据)并且NGINX可以用很短的时间在这个队列当中处理完所有的事件。

clipboard.png
所有的操作都在一个线程的简单循环当中做完了。
但是如果一个长时间并且重量级的操作到来会发生啥呢?答案显而易见,整个事件处理的循环都会被这个操作所阻塞直到这个操作完成。

因此,所谓“阻塞操作”是指任何导致事件处理循环显著停止一段时间的操作。操作会因为各种各样的原因而被阻塞。比如说,NGINX可能忙于处理冗长的CPU密集型处理,或者可能需要等待访问资源(例如硬盘驱动器,或一个库函数以同步方式从数据库获取响应,等等等等)。关键的问题在于,处理这样的事情,工作线程不能做其他别的事情,即使其他的系统资源可以获取到而且队列当中的其他一些事件会用到这些资源。

想象一下商店销售员面前有长长的一长队人。 队列中的第一个人需要一个不在商店但是在仓库里的东西。 销售人员跑到仓库去提货。 现在,整个队列必须等待几个小时才能进行交付,排队当中的每个人都不满意。 想想如果是你你会如何反应? 队员中的每个人的等待时间都增加了这几个小时,但他们打算买的东西有可能就在店里。

clipboard.png
队列里的每个人都必须因为第一个的订单而等待。

相同的情况发生在NGINX当中,想想当它想要读一个没有缓存在内存中的文件而不得不去访问硬盘的时候。硬盘驱动器很慢(特别是机械硬盘),而等待队列中的其他请求可能不需要访问驱动器,所以它们也是被迫等待的。 因此,延迟增加,系统资源未得到充分利用。

clipboard.png
只有一个阻塞会大幅延迟所有的接下来所有的操作

一些操作系统提供用于读取和发送文件的异步接口,NGINX可以使用此接口(请参阅aio指令)。 这里有个好例子就是FreeBSD。坑爹的是,Linux可能不如左边这位那么友好。 虽然Linux提供了一种用于读取文件的异步接口,但它有一些显著的缺点。 其中一个是文件访问和缓冲区的对齐要求,当然NGINX可以把这个问题处理得很好。 但第二个问题更糟糕,异步接口需要在文件描述符上设置O_DIRECT标志,这意味着对文件的任何访问将绕过内存中的缓存并增加硬盘上的负载。这无形中干掉了很多原本可以使用这个调用的场景。

为了特别解决这个问题,NGINX 1.7.11和NGINX Plus Release 7中引入了线程池。

现在我们来看看什么线程池是关于它们以及它们的工作原理。

线程池

让我们回到上个问题,倒霉的销售助理从遥远的仓库配货这个用例。 这次他变得更聪明(也许是被愤怒的客户群殴后变得更聪明了),他雇佣了送货服务。 现在,当有人需要遥远的仓库里的一些东西的时候,他不会亲自去仓库而只不过下了一个订单到送货服务,他们会处理订单,而我们的销售助理会继续为其他客户服务。 因此,只有那些货物不在商店的客户正在等待交货,而售货员可以马上继续为其他客户提供服务。

图片描述
将订单传递给运送服务从而解除阻塞队列

在NGINX方面,线程池正在执行运送服务的功能。 它由一个任务队列和多个处理队列的线程组成。 当一个工作进程需要做一个潜在的长时间操作时,它不会自己处理这个操作,而是将一个任务放在线程池的队列中,任何空闲的线程都可以从中进行处理。

clipboard.png
工作进程将阻塞操作装载到线程池

看来我们还有一个队列。是的,但是在这种情况下,队列受到特定资源的限制。我们从磁盘读取资源速度永远比磁盘生成数据要慢。但是现在至少磁盘操作不会延迟其他事件的处理,只有需要访问文件的请求正在等待。

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

目前,提交到线程池仅用于三个基本操作:大多数操作系统上的read()系统调用,Linux上的sendfile()和Linux上的在编写一些临时文件比如缓存时使用到的aio_write()。我们将继续测试和评估,如果有明显的好处,我们可能会在未来的版本中将其他操作也提交到线程池。

编辑注: 在NGINX 1.9.13和NGINX Plus R9中添加了对aio_write()系统调用的支持。

评估基准

现在到了理论通往实践的时候了。 为了演示使用线程池的效果,我们将执行一个合成基准,模拟阻塞和非阻塞操作的最糟糕组合。

它需要一个确保不适合内存贮存的数据集。 在具有48 GB内存的机器上,我们已经生成了256 GB的随机4M分割数据,然后配置了NGINX version 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不会一次尝试发送整个文件,而是分割成512 KB的数据块来执行相应操作。

该机器有两块Intel Xeon E5645(12核24线程)处理器和10 Gbps网络接口。 磁盘子系统由安装在RAID10阵列中的四个西数WD1003FBYX硬盘驱动器表示。 操作系统是Ubuntu Server 14.04.1 LTS。

clipboard.png
相应基准下负载生成和NGINX的配置。

客户由两台相同的规格的机器组成。其中一个机器上,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

如你所见,上述的配置可以产生一共1G的流量,从top命令上我们可以看到所有的工作线程在阻塞io上花费了大量的时间(下图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 nginx<4648 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.5 Gbps的流量,对比之前没有线程池时的1 Gbps高下立判!

理论上还可以产生更多的流量,但是这已经达到了机器的最大网络吞吐能力,所以在这次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资源和网络容量,从内存中读取,以服务于上述的第二个客户端的请求。

没有什么灵丹妙药

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

实际上很幸运大多数的读或者写文件操作都不会和硬盘打交道。如果我们有足够的内存来存储数据集,那么操作系统会聪明地在被称作“页面缓存”的地方缓存那些频繁使用的文件。

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

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

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

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

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

是的,问题的本质就是操作系统没有这样的特性。2010年人们第一次试图把这个功能作为fincore()系统调用加入到Linux当中,但是没有成功。后来还有一些是使用RWF_NONBLOCK标记作为preadv2()系统调用来实现这一功能的尝试(详情见LWN.net上的非阻塞缓冲文件读取操作和异步缓冲读操作)。但所有这些补丁的命运目前还不明朗。悲催的是,这些补丁尚没有被内核接受的主要原因你可以看这里:(bikeshedding)。

译者著:我觉得没加入内核完全就是开发组里面一派人有类似“要啥自行车”这样的想法.....

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

配置线程池

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

线程池的配置非常简单、灵活。首先,获取NGINX 1.7.11或更高版本的源代码,使用--with-threads配置参数编译。在最简单的场景中,配置也看起来很简单。所有你需要的所有事就是在合适的情况下把aio线程的指令include进来:

# in the 'http', 'server', or 'location' context
aio threads;

这是线程池的最简配置。实际上边的配置是下边的精简版:

# in the 'main' context
thread_pool default threads=32 max_queue=65536;# in the 'http', 'server', or 'location' context
aio threads=default;

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

thread pool "NAME" queue overflow: N tasks waiting

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

正如你已经注意到的,你可以使用thread_pool指令,配置线程的数量、队列的最大长度,以及特定线程池的名称。最后要说明的是,可以配置多个相互独立的线程池,并在配置文件的不同位置使用它们来满足不同的用途:

# in the 'main' context
thread_pool one threads=128 max_queue=0;
thread_pool two threads=32;http {server {location /one {aio threads=one;}location /two {aio threads=two;}}#...
}

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

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

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

# We assume that each of the hard drives is mounted on one of these directories:
# /mnt/disk1, /mnt/disk2, or /mnt/disk3# in the 'main' context
thread_pool pool_1 threads=16;
thread_pool pool_2 threads=16;
thread_pool pool_3 threads=16;http {proxy_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;split_clients $request_uri $disk {33.3%     1;33.3%     2;*         3;}server {#...location / {proxy_pass http://backend;proxy_cache_key $request_uri;proxy_cache cache_$disk;aio threads=pool_$disk;sendfile on;}}
}

在这份配置中,使用了3个独立的缓存,每个缓存专用一块硬盘,另外,3个独立的线程池也各自专用一块硬盘,proxy_cache_path指令在每个磁盘定义了一个专用、独立的缓存

split_clients模块用于高速缓存之间的负载平衡(以及磁盘之间的结果),它完全适合这类任务。

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

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

我们相信你的客户会喜欢这种量身定制的方法。请确保你的磁盘撑得住。

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

总结

综上所述,线程池是个好功能,它将NGINX的性能提高到新的高度并且干掉了一个众所周知的长期隐患:阻塞,尤其是当我们真正面对大量吞吐的情况下这种优势更加明显。

但是还有更多的惊喜。正如前面所述,这种全新的接口可能允许装载任何耗时和阻塞的操作而不会造成任何性能的损失。 NGINX在大量新模块和功能方面开辟了新的天地。 许多受欢迎的库仍然没有提供异步非阻塞接口,以前这使得它们与NGINX不兼容。 我们可能花费大量的时间和精力来开发自己的非阻塞原型库,但是这么做可能并不值得。 现在,使用线程池,我们可以相对容易地使用这些库,并且这些模块不会对性能产生影响。

敬请期待下篇文章。


http://www.ppmy.cn/news/203002.html

相关文章

Linux音频驱动-WAV文件格式分析

概述 WAV文件格式是Microsoft的RIFF规范的一个子集&#xff0c;用于存储多媒体文件。WAV(RIFF)文件由若干个Chunk组成&#xff0c;分别为: RIFF WAVE Chunk&#xff0c;Format Chunk&#xff0c;Fact Chunk(可选)&#xff0c;Data Chunk。具体格式如下&#xff1a; RIFF Chunk…

Google Coral:谷歌机器学习 Edge TPU 定制产品终于要来了

在去年于旧金山召开的Google Next大会上&#xff0c;Injong Rhee通过主题演讲披露了谷歌公司的两款全新硬件产品&#xff1a;一款开发单片&#xff0c;外加一款USB加速棒。作为这两款产品的核心&#xff0c;谷歌的Edge TPU扮演着关键角色——这款专用ASIC旨在将机器学习推理能力…

2万汉字编码大全

一,一,4e00 丁,丁,4e01 丂,丂,4e02 七,七,4e03 丄,丄,4e04 丅,丅,4e05 丆,丆,4e06 万,万,4e07 丈,丈,4e08 三,三,4e09 上,上,4e0a 下,下,4e0b 丌,丌,4e0c 不,不,4e0d 与,与,4e0e 丏,丏,4e0f 丐,丐,4e10 丑,丑,4e11 丒,丒,4e12 专,专,4e13 且,且,4e14 丕,丕,4e15 世,世,4e16 丗,…

使用贝叶斯进行新闻分类

贝叶斯新闻分类任务 新闻数据集处理 爬取的新闻数据&#xff0c;需要我们对文本数据进行很多预处理才能使用 文本分词 通常我们处理的都是词而不是一篇文章 去停用词 停用词会对结果产生不好的影响&#xff0c;所以一定得把他们去剔除掉 构建文本特征 如何构建合适特征…

Android音频驱动学习(一) Audio HAL

Hal加载过程 加载audio hal需要分三步 1、hw_get_module_by_class &#xff1a;加载hal module 2、audio_hw_device_open&#xff1a;调用audio device open 3、open_output_stream&#xff1a;打开output DevicesFactory::loadAudioInterface(const char *if_name, audio_hw_…

【译】提高nginx9倍性能的线程池 thread_pool

在nginx的官网看到一篇介绍nginx原理的文章&#xff0c;这篇文章比较老了是15年发布的&#xff0c;国内有人翻译过但是有些小瑕疵&#xff0c;这里更正出来发布在我个人的文章里供大家参考&#xff0c;这篇文章详细的介绍了nginx线程池的原理以及设计思路&#xff0c;在最后通过…

Go语言开发实战课后编程题

Go语言开发实战课后编程题 第一章第三章第四章第五章第六章第七章第八章 自己做的课后习题&#xff0c;不保证完全正确&#xff0c;侵删 第一章 尝试创建一个项目&#xff0c;新建一个程序&#xff0c;输出字符串“我爱Go语言”。 package mainimport "fmt"func mai…

软考——下午题部分,例题一,二,三,六

例题一 11年上半年 病人&#xff0c;护理人员&#xff0c;医生 D 生命体征范围文件 日志文件 病历文件 治疗意见文件 14年上 E1 巴士司机,2 机械师,3 会计,4 主管,5 库存管理系统 D 巴士列表文件 维修记录文件 部件清单 人事档案 14年下 1 客户 2 供应商 D 销售订单表 库存…

信号处理-盲源分离基础【1】

信号处理-盲源分离基础【1】 1. 盲源分离的定义1.1 滤波器对信号的筛选作用1.2 盲源分离相比于滤波器设计的差异1.3 盲源分离的基本技术1.3.1 PCA方法1.3.2 ICA方法1.4 盲源分离问题的欠定性1. 盲源分离的定义 百度百科的定义上,盲源分离其实就类似于字面意思,从一个混合的信…

【C++】成员对象和成员函数分开存储

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、详解 3、代码清单 1 3.1、类中定义成员变量 3.2、类中定义成员函数 4、代码清单 2 5、总结 1、缘起 “成员变量…

使用python的pygame做的小游戏项目:小船打鱼

python小游戏项目&#xff1a;小船打鱼 成果展示代码解析go_fishing.pygame_function.pygame_stats.pyscoreboard.pyalien.pysettings.pyship.pybullet.pybutton.py 存在的问题 代码都在这里&#xff0c;只需要创建好项目&#xff0c;将对应的代码保存在对应文件名的文件中即可…

基于java8的捕鱼达人小游戏

整体框架 #mermaid-svg-kwIdYePfSf2yisnx .label{font-family:trebuchet ms, verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-kwIdYePfSf2yisnx .label text{fill:#333}#mermaid-svg-kwIdYePfSf2yisnx .node rect,#mermaid-svg-kwI…

《全民捕鱼》游戏分析

文章目录 分析环境逆向分析日志分析法字符串分析法去除弹窗添加个人信息的破解 总结 分析环境 系统:Android 4.4 工具:Windows 10 64bit 夜神模拟器 Android Killer 游戏:全民捕鱼V1.7 文件: fkby.apk 大小: 14450756 bytes 修改时间: 2016年3月 11日, 14:53:34 MD5: C67C9D…

浏览器网页闪退原因分析

浏览器中网页闪退可能由多种原因引起。以下是一些可能的原因&#xff1a; 内存问题&#xff1a;如果浏览器占用过多内存&#xff0c;系统可能会强制关闭某些页面或整个浏览器以保护系统的稳定性。 扩展或插件冲突&#xff1a;某些浏览器扩展或插件可能与网页的代码或其他扩展产…

锐龙r75800h和i711800h哪个好 锐龙r75800h和i711800h的核显

酷睿i7-11800H采用的是Intel最新的10nm制程工艺&#xff0c;虽然在数字上看似比不上7nm&#xff0c;但是其大家都知道Intel的10nm不会弱于台积电的7nm和三星的5nm。基于Tiger Lake-H构架的i7-11800H处理器是规格大改后的产物&#xff0c;其中二级缓存容量高达10MB&#xff0c;上…

达人评测 锐龙r5 6600h和r5 6600u区别

R5-6600H配置参数&#xff1a;6nm的工艺&#xff0c;6核12线程&#xff0c;3.3GHz的主频&#xff0c;4.5GHz的睿频&#xff0c;三级缓存16MB&#xff0c;功耗TDP为45W&#xff0c;核显为radeon 660m&#xff0c;6CU 笔记本cpu选r5 6600h还是r5 6600u这些点很重要看过你就懂了 h…

锐龙R75800X和R5 5600X 哪个好

锐龙7 5800X&#xff0c;单CCD&#xff0c;8核心16线程&#xff0c;4MB二级缓存&#xff0c;32MB三级缓存&#xff0c;基准频率3.8GHz&#xff0c;最高加速4.7GHz&#xff0c;热设计功耗为105W。 选R7 5800X还是R5 5600X这些点很重要!看完你就知道了https://list.jd.com/list.h…

卧室投影仪需要多少流明比较好?流明怎么看?

家用投影仪流明&#xff0c;简单来说&#xff0c;就是亮度&#xff0c;它是衡量投影机性能的一个重要指标&#xff0c;流明越高&#xff0c;就算是白天强光的影响下&#xff0c;也能看的清 楚&#xff0c;那么家用投影仪多少流明合适&#xff1f;流明太高或太低都不太适合于照明…

python中Requests发送json格式的post请求方法

问题&#xff1a;做requests请求时遇到如下报错&#xff1a; {“code”:“500”,“message”:"JSON parse error: Cannot construct instance of com.bang.erpapplication.domain.User (although at least one Creator exists): no String-argument constructor/factory …

5寸android智能手机,5寸刚入门 六款巨屏安卓智能手机盘点(5)

要说目前销量最高的手机恐怕非三星i9500(GALAXY S4)莫属&#xff0c;该机沿袭了前作i9300的经典外观&#xff0c;5英寸1080P全高清屏幕的显示效果极为清晰细腻&#xff0c;1.6GHz Exynos 5处理器加2GB内存更是强悍无比。目前该机的售价在3480元左右&#xff0c;虽然价格不算便宜…
最新文章