Algorithm 每周一道算法题
本周算法题为整数转罗马数字
本题有意思的点在于对于边界值如何处理,我采用的方式是将所有可数的罗马数字跟对应的值保存在 map 中,注意 Java 中需要使用 LinkedHashMap 保证顺序,然后对 map 进行遍历即可,有点取巧
Review 阅读一篇英文文章
本周继续阅读The C10K problem
1. Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification
使用一个线程服务多个 client,并使用非阻塞 I/O 以及通过水平级别触发准备好的通知
set nonblocking mode on all network handles, and use select() or poll() to tell which network handle has data waiting. This is the traditional favorite. With this scheme, the kernel tells you whether a file descriptor is ready, whether or not you've done anything with that file descriptor since the last time the kernel told you about it. (The name 'level triggered' comes from computer hardware design; it's the opposite of 'edge triggered'. Jonathon Lemon introduced the terms in his BSDCON 2000 paper on kqueue().)
在所有的网络处理中设置非阻塞模式,并且使用 select() 和 poll() 方法通知哪个网络处理器有数据可以处理了。这是最传统的方式。使用这种模式,内核会告诉你文件描述符是否已经准备好,无论你自上次内核告诉你后是否对文件描述符做了任何操作。(level triggered 这个名字来自于计算机硬件设计,与之相反的是 edge triggered)。Jonathon Lemon 在他的BSDCON 2000 paper on kqueue()文章中对此有介绍。
Note: it's particularly important to remember that readiness notification from the kernel is only a hint; the file descriptor might not be ready anymore when you try to read from it. That's why it's important to use nonblocking mode when using readiness notification.
注意:记住来自内核的准备好的通知信号仅仅是一个提示,这是非常重要的;文件描述符可能在你准备读取时并没有准备好。那就是在使用通知信号时为什么使用非阻塞模式是非常重要的原因。
An important bottleneck in this method is that read() or sendfile() from disk blocks if the page is not in core at the moment; setting nonblocking mode on a disk file handle has no effect. Same thing goes for memory-mapped disk files. The first time a server needs disk I/O, its process blocks, all clients must wait, and that raw nonthreaded performance goes to waste.
当你使用 read() 或者 sendfile() 从磁盘操作数据时发现当前被操作页并不在内核中,这就会导致一个非常重要的性能瓶颈;在硬盘文件处理设置非阻塞模式并不会有任何帮助。相同的情况在 mmap 中也存在。当服务器首次需要从硬盘进行 I/O 操作,进程都会阻塞,所有 client 必须等待,cpu 性能将会被浪费。
This is what asynchronous I/O is for, but on systems that lack AIO, worker threads or processes that do the disk I/O can also get around this bottleneck. One approach is to use memory-mapped files, and if mincore() indicates I/O is needed, ask a worker to do the I/O, and continue handling network traffic. Jef Poskanzer mentions that Pai, Druschel, and Zwaenepoel's 1999 Flash web server uses this trick; they gave a talk at Usenix '99 on it. It looks like mincore() is available in BSD-derived Unixes like FreeBSD and Solaris, but is not part of the Single Unix Specification. It's available as part of Linux as of kernel 2.3.51, thanks to Chuck Lever.
这就是异步 I/O 的作用,但是如果系统缺少异步 I/O,进行磁盘 I/O 的工作线程或者进程同样也可以绕过该瓶颈。一个方式是试用 mmap 文件,如果 mincore() 显示需要 I/O 操作,通知一个工作线程进行 I/O 操作,并急需处理网络流。Jef Poskanzer 提到 Pai, Druschel 和 Zwaenepoel's 在 1999 年的 Flash web 网站就使用了该技巧。他们在 Usenix '99 上讨论了它。似乎 mincore() 方法在 BSD 驱动的类 Unix 系统,比如 FreeBSD 和 Solaris 上是可用的,但是它不是标准 Single Unix Specification 的一部分。感谢Chuck Lever,在 Linux 内核 2.3.51 版本中它是可用的。
But in November 2003 on the freebsd-hackers list, Vivek Pei et al reported very good results using system-wide profiling of their Flash web server to attack bottlenecks. One bottleneck they found was mincore (guess that wasn't such a good idea after all) Another was the fact that sendfile blocks on disk access; they improved performance by introducing a modified sendfile() that return something like EWOULDBLOCK when the disk page it's fetching is not yet in core. (Not sure how you tell the user the page is now resident... seems to me what's really needed here is aio_sendfile().) The end result of their optimizations is a SpecWeb99 score of about 800 on a 1GHZ/1GB FreeBSD box, which is better than anything on file at spec.org.
然而在 2003 年 11 月的 freebsd-hacker 邮件列表中,Vivek Pei et al 等人报告说,他们使用系统范围的剖析对 flash web 服务进行了优化,获得了非常好的结果,并解决了性能瓶颈问题。他们发现一个瓶颈是 mincore(猜测那并不是一个好主意),另一个问题是 sendfile 在磁盘访问时会阻塞;他们通过介绍一个被修改的 sendfile 方法来提升性能,该方法在磁盘也还没被加载到内核时返回如 EWOULDBLOCK(不确定你如何通知用户磁盘也还未准备好……对我来说真正需要的是异步的 sendfile 方法)。他们优化的结果是 SpecWeb99 分数大概是 800,在 1GHZ/1GB 规格的 FreeBSD 系统,这比 spec.org 上任何成绩都要好。
There are several ways for a single thread to tell which of a set of nonblocking sockets are ready for I/O:
以下是对一个线程告知有哪些非阻塞的 sockets 准备好了的几种方式:
传统的 select Unfortunately, select() is limited to FD_SETSIZE handles. This limit is compiled in to the standard library and user programs. (Some versions of the C library let you raise this limit at user app compile time.)
See Poller_select (cc, h) for an example of how to use select() interchangeably with other readiness notification schemes.
不幸的是 select 方法仅限于处理 FD_SETSIZE 这个句柄。这个限制是编译进标准库和用户程序的(一些 C 库允许用户在应用程序编译时提高这个限制)
参见文章Poller_select (cc, h) 中的示例,了解如何与其他标准通知方案交替使用 select
-
The traditional poll()
There is no hardcoded limit to the number of file descriptors poll() can handle, but it does get slow about a few thousand, since most of the file descriptors are idle at any one time, and scanning through thousands of file descriptors takes time.Some OS's (e.g. Solaris 8) speed up poll() et al by use of techniques like poll hinting, which was implemented and benchmarked by Niels Provos for Linux in 1999.
See Poller_poll (cc, h, benchmarks) for an example of how to use poll() interchangeably with other readiness notification schemes.
对 poll() 方法能处理的文件描述符的数量没有硬编码的限制,但是在上千个文件描述符时性能就比较慢了,特别是大多数文件描述符在同一时刻都空闲的情况下,扫描上千个文件描述符是很耗时的。
一些操作系统(比如 Solaris 8)通过试用类似 poll hinting 的技术来加快 poll 方法,这被 Niels Provos 在 1999 年在 Linux 系统中实现并测试。参见Poller_poll (cc, h, benchmarks)中的示例了解如何与其他标准通知方案交替使用 select
-
/dev/poll
This is the recommended poll replacement for Solaris.The idea behind /dev/poll is to take advantage of the fact that often poll() is called many times with the same arguments. With /dev/poll, you get an open handle to /dev/poll, and tell the OS just once what files you're interested in by writing to that handle; from then on, you just read the set of currently ready file descriptors from that handle.
It appeared quietly in Solaris 7 (see patchid 106541) but its first public appearance was in Solaris 8; according to Sun, at 750 clients, this has 10% of the overhead of poll().
Various implementations of /dev/poll were tried on Linux, but none of them perform as well as epoll, and were never really completed. /dev/poll use on Linux is not recommended.
See Poller_devpoll (cc, h benchmarks ) for an example of how to use /dev/poll interchangeably with many other readiness notification schemes. (Caution - the example is for Linux /dev/poll, might not work right on Solaris.)
对于 Solaris 来说这是推荐的替换 poll 的方法。
/dev/poll 的背后的思想是利用通常情况下 poll() 函数多次以相同参数调用的特点。使用 /dev/poll,您可以打开一个指向 /dev/poll 的句柄,只需向该句柄写一次以告知操作系统您感兴趣的文件;从那时起,您只需从该句柄读取当前就绪文件描述符的集合。
在 Solaris 7 就悄然出现,但是正式发布是在 Solaris 8,在 750 个客户端情况下,有 10% 的性能提升。
在 Linux 上有不同的 /dev/poll 的变种实现,但是他们都没有 epoll 性能好,并且都处于未完成状态。在 Linux 上使用 /dev/poll 是不推荐的
-
kqueue()
This is the recommended poll replacement for FreeBSD (and, soon, NetBSD).See below. kqueue() can specify either edge triggering or level triggering.
对于 FreeBSD(以及不久后的 NetBSD)来说,使用 kqueue 替换 poll 是推荐的。
请见下面链接:www.kegel.com/c10k.html#n… kqueue() 函数可以指定边沿触发或水平触发。
Techniques/Tips 分享一个小技巧
Java 中泛型 List<?> 和 List<Object> 并不相同,List<?> 表示一个未知类型的列表,可以接受任意类型的元素,但是不能往 List<?> 中添加任何元素,因为你无法确定它的确切类型,除非使用 extends 或者 super 来对 ? 进行限定。List<Object> 则表示一个元素类型为 Object 的列表,可以接受任何类型元素,并将其当做 Object 类型处理。你可以往 List<Object> 中添加任何类型的元素。
Share 分享一个观点
最近在学习《从 0 开始学架构》,学到了如何保证系统可用性,这其实是系统设计部分最难的点,因为没有一个完美的解决方案,目前现有的解决方案无非是通过存储系统的可用性保证系统可用性,存储系统可用性又由于物理特性导致无法做到完美的一致性,因此归根到底,没法完全做到一个完美的不存在可用性问题的系统,但是我们可以尽可能提升可用性。
提升方法无非是冗余,比如同城异区多活,跨城异地多活、跨国异地多活等。最终还是会落到存储系统冗余上,由于存储系统无法保证严格一致性,因此有些数据可以做异地多活,有些不能,只能做异区多活,希望后面随着技术的演进,比如量子技术,能解决数据一致性问题,这样就能实现真正的可用性完美的系统