## __VA_ARGS__

1.#
假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).
#incldue
#define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x))
int main(void)
{
   int y =4;
   PSQR(y);
   PSQR(2+4);
   return 0;
}
输出结果:
the square of y is 16.
the square of 2+4 is 36.
第一次调用宏时使用“y”代替#x;第二次调用时用“2+4"代#x。
2.##
##运算符可以使用类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:
#define XNAME(n) x##n
这样宏调用:
XNAME(4)
展开后:
x4
程序:
#include
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d\n",x##n)
int main(void)
{
   int XNAME(1)=12;//int x1=12;
   PXN(1);//printf("x1 = %d\n", x1);
   return 0;
}
3.可变宏 ...和__VA_ARGS__
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏__VA_ARGS__就可以被用在替换部分中,以表示省略号代表什么。比如:
#define PR(...) printf(__VA_ARGS__)
PR("hello");-->printf("hello");
PR("weight = %d, shipping = $.2f",wt,sp);
   -->printf("weight = %d, shipping = $.2f",wt,sp);
省略号只能代替最后面的宏参数。
#define W(x,...,y)错误!

Detect input coming from terminal


[edit] C

Use isatty() on file descriptor to determine if it's a TTY. To get the file descriptor from a FILE* pointer, use fileno:
#include  //for isatty()
#include  //for fileno()
 
int main(void)
{
 puts(isatty(fileno(stdin))
  ? "stdin is tty"
  : "stdin is not tty");
 return 0;
}
Output:
$ ./a.out
stdin is tty
$ ./a.out < /dev/zero
stdin is not tty
$ echo "" | ./a.out
stdin is not tty

[edit] D

import std.stdio;
 
extern(C) int isatty(int);
 
void main() {
    if (isatty(0))
        writeln("Input comes from tty.");
    else
        writeln("Input doesn't come from tty.");
}
Output:
C:\test
Input comes from tty.
C:\test < in.txt
Input doesn't come from tty.

[edit] Perl

use strict;
use warnings;
use 5.010;
if (-t) {
    say "Input comes from tty.";
}
else {
    say "Input doesn't come from tty.";
}
$ perl istty.pl
Input comes from tty.
$ true | perl istty.pl
Input doesn't come from tty.

[edit] Perl 6

say $*IN.t ?? "Input comes from tty." !! "Input doesn't come from tty.";
$ perl6 istty.p6
Input comes from tty.
$ true | perl6 istty.p6
Input doesn't come from tty.

[edit] Python

from sys import stdin
if stdin.isatty():
    print("Input comes from tty.")
else:
    print("Input doesn't come from tty.")
$ python istty.pl
Input comes from tty.
$ true | python istty.pl
Input doesn't come from tty.

[edit] REXX

/*REXX program determines if input comes from terminal or standard input*/
 
if queued()  then say 'input comes from the terminal.'
             else say 'input comes from the (stacked) terminal queue.'
 
                                       /*stick a fork in it, we're done.*/
 

[edit] Tcl

Tcl automatically detects whether stdin is coming from a terminal (or a socket) and sets up the channel to have the correct type. One of the configuration options of a terminal channel is -mode (used to configure baud rates on a real serial terminal) so we simply detect whether the option is present.
if {[catch {fconfigure stdin -mode}]} {
    puts "Input doesn't come from tty."
} else {
    puts "Input comes from tty."
}
Demonstrating:
$ tclsh8.5 istty.tcl 
Input comes from tty.
$ tclsh8.5 istty.tcl 

Redis latency problems troubleshooting


This document will help you understand what the problem could be if you are experiencing latency problems with Redis.
In this context latency is the maximum delay between the time a client issues a command and the time the reply to the command is received by the client. Usually Redis processing time is extremely low, in the sub microsecond range, but there are certain conditions leading to higher latency figures.

Measuring latency

If you are experiencing latency problems, probably you know how to measure it in the context of your application, or maybe your latency problem is very evident even macroscopically. However redis-cli can be used to measure the latency of a Redis server in milliseconds, just try:
redis-cli --latency -h `host` -p `port`

Latency induced by network and communication

Clients connect to Redis using a TCP/IP connection or a Unix domain connection. The typical latency of a 1 GBits/s network is about 200 us, while the latency with a Unix domain socket can be as low as 30 us. It actually depends on your network and system hardware. On top of the communication itself, the system adds some more latency (due to thread scheduling, CPU caches, NUMA placement, etc ...). System induced latencies are significantly higher on a virtualized environment than on a physical machine.
The consequence is even if Redis processes most commands in sub microsecond range, a client performing many roundtrips to the server will have to pay for these network and system related latencies.
An efficient client will therefore try to limit the number of roundtrips by pipelining several commands together. This is fully supported by the servers and most clients. Aggregated commands like MSET/MGET can be also used for that purpose. Starting with Redis 2.4, a number of commands also support variadic parameters for all data types.
Here are some guidelines:
  • If you can afford it, prefer a physical machine over a VM to host the server.
  • Do not systematically connect/disconnect to the server (especially true for web based applications). Keep your connections as long lived as possible.
  • If your client is on the same host than the server, use Unix domain sockets.
  • Prefer to use aggregated commands (MSET/MGET), or commands with variadic parameters (if possible) over pipelining.
  • Prefer to use pipelining (if possible) over sequence of roundtrips.
  • Future version of Redis will support Lua server-side scripting (experimental branches are already available) to cover cases that are not suitable for raw pipelining (for instance when the result of a command is an input for the following commands).
On Linux, some people can achieve better latencies by playing with process placement (taskset), cgroups, real-time priorities (chrt), NUMA configuration (numactl), or by using a low-latency kernel. Please note vanilla Redis is not really suitable to be bound on a single CPU core. Redis can fork background tasks that can be extremely CPU consuming like bgsave or AOF rewrite. These tasks must never run on the same core as the main event loop.
In most situations, these kind of system level optimizations are not needed. Only do them if you require them, and if you are familiar with them.

Single threaded nature of Redis

Redis uses a mostly single threaded design. This means that a single process serves all the client requests, using a technique calledmultiplexing. This means that Redis can serve a single request in every given moment, so all the requests are served sequentially. This is very similar to how Node.js works as well. However, both products are often not perceived as being slow. This is caused in part by the small about of time to complete a single request, but primarily because these products are designed to not block on system calls, such as reading data from or writing data to a socket.
I said that Redis is mostly single threaded since actually from Redis 2.4 we use threads in Redis in order to perform some slow I/O operations in the background, mainly related to disk I/O, but this does not change the fact that Redis serves all the requests using a single thread.

Latency generated by slow commands

A consequence of being single thread is that when a request is slow to serve all the other clients will wait for this request to be served. When executing normal commands, like GET or SET or LPUSH this is not a problem at all since this commands are executed in constant (and very small) time. However there are commands operating on many elements, like SORTLREMSUNION and others. For instance taking the intersection of two big sets can take a considerable amount of time.
The algorithmic complexity of all commands is documented. A good practice is to systematically check it when using commands you are not familiar with.
If you have latency concerns you should either not use slow commands against values composed of many elements, or you should run a replica using Redis replication where to run all your slow queries.
It is possible to monitor slow commands using the Redis Slow Log feature.
Additionally, you can use your favorite per-process monitoring program (top, htop, prstat, etc ...) to quickly check the CPU consumption of the main Redis process. If it is high while the traffic is not, it is usually a sign that slow commands are used.

Latency generated by fork

In order to generate the RDB file in background, or to rewrite the Append Only File if AOF persistence is enabled, Redis has to fork background processes. The fork operation (running in the main thread) can induce latency by itself.
Forking is an expensive operation on most Unix-like systems, since it involves copying a good number of objects linked to the process. This is especially true for the page table associated to the virtual memory mechanism.
For instance on a Linux/AMD64 system, the memory is divided in 4 KB pages. To convert virtual addresses to physical addresses, each process stores a page table (actually represented as a tree) containing at least a pointer per page of the address space of the process. So a large 24 GB Redis instance requires a page table of 24 GB / 4 KB * 8 = 48 MB.
When a background save is performed, this instance will have to be forked, which will involve allocating and copying 48 MB of memory. It takes time and CPU, especially on virtual machines where allocation and initialization of a large memory chunk can be expensive.

Fork time in different systems

Modern hardware is pretty fast to copy the page table, but Xen is not. The problem with Xen is not virtualization-specific, but Xen-specific. For instance using VMware or Virutal Box does not result into slow fork time. The following is a table that compares fork time for different Redis instance size. Data is obtained performing a BGSAVE and looking at the latest_fork_usec filed in the INFO command output.
  • Linux beefy VM on VMware 6.0GB RSS forked in 77 milliseconds (12.8 milliseconds per GB).
  • Linux running on physical machine (Unknown HW) 6.1GB RSS forked in 80 milliseconds (13.1 milliseconds per GB)
  • Linux running on physical machine (Xeon @ 2.27Ghz) 6.9GB RSS forked into 62 millisecodns (9 milliseconds per GB).
  • Linux VM on 6sync (KVM) 360 MB RSS forked in 8.2 milliseconds (23.3 millisecond per GB).
  • Linux VM on EC2 (Xen) 6.1GB RSS forked in 1460 milliseconds (239.3 milliseconds per GB).
  • Linux VM on Linode (Xen) 0.9GBRSS forked into 382 millisecodns (424 milliseconds per GB).
As you can see a VM running on Xen has a performance hit that is between one order to two orders of magnitude. We believe this is a severe problem with Xen and we hope it will be addressed ASAP.

Latency induced by swapping (operating system paging)

Linux (and many other modern operating systems) is able to relocate memory pages from the memory to the disk, and vice versa, in order to use the system memory efficiently.
If a Redis page is moved by the kernel from the memory to the swap file, when the data stored in this memory page is used by Redis (for example accessing a key stored into this memory page) the kernel will stop the Redis process in order to move the page back into the main memory. This is a slow operation involving random I/Os (compared to accessing a page that is already in memory) and will result into anomalous latency experienced by Redis clients.
The kernel relocates Redis memory pages on disk mainly because of three reasons:
  • The system is under memory pressure since the running processes are demanding more physical memory than the amount that is available. The simplest instance of this problem is simply Redis using more memory than the one available.
  • The Redis instance data set, or part of the data set, is mostly completely idle (never accessed by clients), so the kernel could swap idle memory pages on disk. This problem is very rare since even a moderately slow instance will touch all the memory pages often, forcing the kernel to retain all the pages in memory.
  • Some processes are generating massive read or write I/Os on the system. Because files are generally cached, it tends to put pressure on the kernel to increase the filesystem cache, and therefore generate swapping activity. Please note it includes Redis RDB and/or AOF background threads which can produce large files.
Fortunately Linux offers good tools to investigate the problem, so the simplest thing to do is when latency due to swapping is suspected is just to check if this is the case.
The first thing to do is to checking the amount of Redis memory that is swapped on disk. In order to do so you need to obtain the Redis instance pid:
$ redis-cli info | grep process_id
process_id:5454
Now enter the /proc file system directory for this process:
$ cd /proc/5454
Here you'll find a file called smaps that describes the memory layout of the Redis process (assuming you are using Linux 2.6.16 or newer). This file contains very detailed information about our process memory maps, and one field called Swap is exactly what we are looking for. However there is not just a single swap field since the smaps file contains the different memory maps of our Redis process (The memory layout of a process is more complex than a simple linear array of pages).
Since we are interested in all the memory swapped by our process the first thing to do is to grep for the Swap field across all the file:
$ cat smaps | grep 'Swap:'
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                 12 kB
Swap:                156 kB
Swap:                  8 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  4 kB
Swap:                  4 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
Swap:                  0 kB
If everything is 0 kb, or if there are sporadic 4k entries, everything is perfectly normal. Actually in our example instance (the one of a real web site running Redis and serving hundreds of users every second) there are a few entries that show more swapped pages. To investigate if this is a serious problem or not we change our command in order to also print the size of the memory map:
$ cat smaps | egrep '^(Swap|Size)'
Size:                316 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  0 kB
Size:                 40 kB
Swap:                  0 kB
Size:                132 kB
Swap:                  0 kB
Size:             720896 kB
Swap:                 12 kB
Size:               4096 kB
Swap:                156 kB
Size:               4096 kB
Swap:                  8 kB
Size:               4096 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:               1272 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                 16 kB
Swap:                  0 kB
Size:                 84 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  8 kB
Swap:                  4 kB
Size:                  8 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  4 kB
Size:                144 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  4 kB
Size:                 12 kB
Swap:                  4 kB
Size:                108 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
Size:                272 kB
Swap:                  0 kB
Size:                  4 kB
Swap:                  0 kB
As you can see from the output, there is a map of 720896 kB (with just 12 kB swapped) and 156 kb more swapped in another map: basically a very small amount of our memory is swapped so this is not going to create any problem at all.
If instead a non trivial amount of the process memory is swapped on disk your latency problems are likely related to swapping. If this is the case with your Redis instance you can further verify it using the vmstat command:
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0  0   3980 697932 147180 1406456    0    0     2     2    2    0  4  4 91  0
 0  0   3980 697428 147180 1406580    0    0     0     0 19088 16104  9  6 84  0
 0  0   3980 697296 147180 1406616    0    0     0    28 18936 16193  7  6 87  0
 0  0   3980 697048 147180 1406640    0    0     0     0 18613 15987  6  6 88  0
 2  0   3980 696924 147180 1406656    0    0     0     0 18744 16299  6  5 88  0
 0  0   3980 697048 147180 1406688    0    0     0     4 18520 15974  6  6 88  0
C
The interesting part of the output for our needs are the two columns si and so, that counts the amount of memory swapped from/to the swap file. If you see non zero counts in those two columns then there is swapping activity in your system.
Finally, the iostat command can be used to check the global I/O activity of the system.
$ iostat -xk 1
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          13.55    0.04    2.92    0.53    0.00   82.95

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
sda               0.77     0.00    0.01    0.00     0.40     0.00    73.65     0.00    3.62   2.58   0.00
sdb               1.27     4.75    0.82    3.54    38.00    32.32    32.19     0.11   24.80   4.24   1.85
If your latency problem is due to Redis memory being swapped on disk you need to lower the memory pressure in your system, either adding more RAM if Redis is using more memory than the available, or avoiding running other memory hungry processes in the same system.

Latency due to AOF and disk I/O

Another source of latency is due to the Append Only File support on Redis. The AOF basically uses two system calls to accomplish its work. One is write(2) that is used in order to write data to the append only file, and the other one is fdatasync(2) that is used in order to flush the kernel file buffer on disk in order to ensure the durability level specified by the user.
Both the write(2) and fdatasync(2) calls can be source of latency. For instance write(2) can block both when there is a system wide sync in progress, or when the output buffers are full and the kernel requires to flush on disk in order to accept new writes.
The fdatasync(2) call is a worse source of latency as with many combinations of kernels and file systems used it can take from a few milliseconds to a few seconds to complete, especially in the case of some other process doing I/O. For this reason when possible Redis does the fdatasync(2) call in a different thread since Redis 2.4.
We'll see how configuration can affect the amount and source of latency when using the AOF file.
The AOF can be configured to perform an fsync on disk in three different ways using the appendfsync configuration option (this setting can be modified at runtime using the CONFIG SET command).
  • When appendfsync is set to the value of no Redis performs no fsync. In this configuration the only source of latency can be write(2). When this happens usually there is no solution since simply the disk can't cope with the speed at which Redis is receiving data, however this is uncommon if the disk is not seriously slowed down by other processes doing I/O.
  • When appendfsync is set to the value of everysec Redis performs an fsync every second. It uses a different thread, and if the fsync is still in progress Redis uses a buffer to delay the write(2) call up to two seconds (since write would block on Linux if an fsync is in progress against the same file). However if the fsync is taking too long Redis will eventually perform the write(2) call even if the fsync is still in progress, and this can be a source of latency.
  • When appendfsync is set to the value of always an fsync is performed at every write operation before replying back to the client with an OK code (actually Redis will try to cluster many commands executed at the same time into a single fsync). In this mode performances are very low in general and it is strongly recommended to use a fast disk and a file system implementation that can perform the fsync in short time.
Most Redis users will use either the no or everysec setting for the appendfsync configuration directive. The suggestion for minimum latency is to avoid other processes doing I/O in the same system. Using an SSD disk can help as well, but usually even non SSD disks perform well with the append only file if the disk is spare as Redis writes to the append only file without performing any seek.
If you want to investigate your latency issues related to the append only file you can use the strace command under Linux:
sudo strace -p $(pidof redis-server) -T -e trace=fdatasync
The above command will show all the fdatasync(2) system calls performed by Redis in the main thread. With the above command you'll not see the fdatasync system calls performed by the background thread when the appendfsync config option is set to everysec. In order to do so just add the -f switch to strace.
If you wish you can also see both fdatasync and write system calls with the following command:
sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write
However since write(2) is also used in order to write data to the client sockets this will likely show too many things unrelated to disk I/O. Apparently there is no way to tell strace to just show slow system calls so I use the following command:
sudo strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished

Latency generated by expires

Redis evict expired keys in two ways:
  • One lazy way expires a key when it is requested by a command, but it is found to be already expired.
  • One active way expires a few keys every 100 milliseconds.
The active expiring is designed to be adaptive. An expire cycle is started every 100 milliseconds (10 times per second), and will do the following:
  • Sample REDIS_EXPIRELOOKUPS_PER_CRON keys, evicting all the keys already expired.
  • If the more than 25% of the keys were found expired, repeat.
Given that REDIS_EXPIRELOOKUPS_PER_CRON is set to 10 by default, and the process is performed ten times per second, usually just 100 keys per second are actively expired. This is enough to clean the DB fast enough even when already expired keys are not accessed for a log time, so that the lazy algorithm does not help. At the same time expiring just 100 keys per second has no effects in the latency a Redis instance.
However the algorithm is adaptive and will loop if it founds more than 25% of keys already expired in the set of sampled keys. But given that we run the algorithm ten times per second, this means that the unlucky event of more than 25% of the keys in our random sample are expiring at least in the same second.
Basically this means that if the database contains has many many keys expiring in the same second, and this keys are at least 25% of the current population of keys with an expire set, Redis can block in order to reach back a percentage of keys already expired that is less than 25%.
This approach is needed in order to avoid using too much memory for keys that area already expired, and usually is absolutely harmless since it's strange that a big number of keys are going to expire in the same exact second, but it is not impossible that the user used EXPIREATextensively with the same Unix time.
In short: be aware that many keys expiring at the same moment can be a source of latency.

Redis software watchdog

Redis 2.6 introduces the Redis Software Watchdog that is a debugging tool designed to track those latency problems that for one reason or the other esacped an analysis using normal tools.
The software watchdog is an experimental feature. While it is designed to be used in production enviroments care should be taken to backup the database before proceeding as it could possibly have unexpected interactions with the normal execution of the Redis server.
It is important to use it only as last resort when there is no way to track the issue by other means.
This is how this feature works:
  • The user enables the softare watchdog using te CONFIG SET command.
  • Redis starts monitoring itself constantly.
  • If Redis detects that the server is blocked into some operation that is not returning fast enough, and that may be the source of the latency issue, a low level report about where the server is blocked is dumped on the log file.
  • The user contacts the developers writing a message in the Redis Google Group, including the watchdog report in the message.
Note that this feature can not be enabled using the redis.conf file, because it is designed to be enabled only in already running instances and only for debugging purposes.
To enable the feature just use the following:
CONFIG SET watchdog-period 500
The period is specified in milliseconds. In the above example I specified to log latency issues only if the server detects a delay of 500 milliseconds or greater. The minimum configurable period is 200 milliseconds.
When you are done with the software watchdog you can turn it off setting the watchdog-period parameter to 0. Important: remember to do this because keeping the instance with the watchdog turned on for a longer time than needed is generally not a good idea.
The following is an example of what you'll see printed in the log file once the software watchdog detects a delay longer than the configured one:
[8547 | signal handler] (1333114359)
--- WATCHDOG TIMER EXPIRED ---
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libpthread.so.0(+0xf8f0) [0x7f16b5f158f0]
/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]
/lib/libc.so.6(usleep+0x34) [0x7f16b5c62844]
./redis-server(debugCommand+0x3e1) [0x43ab41]
./redis-server(call+0x5d) [0x415a9d]
./redis-server(processCommand+0x375) [0x415fc5]
./redis-server(processInputBuffer+0x4f) [0x4203cf]
./redis-server(readQueryFromClient+0xa0) [0x4204e0]
./redis-server(aeProcessEvents+0x128) [0x411b48]
./redis-server(aeMain+0x2b) [0x411dbb]
./redis-server(main+0x2b6) [0x418556]
/lib/libc.so.6(__libc_start_main+0xfd) [0x7f16b5ba1c4d]
./redis-server() [0x411099]
------
Note: in the example the DEBUG SLEEP command was used in order to block the server. The stack trace is different if the server blocks in a different context.
If you happen to collect multiple watchdog stack traces you are encouraged to send everything to the Redis Google Group: the more traces we obtain, the simpler it will be to understand what the problem with your instance is.

APPENDIX A: Experimenting with huge pages

Latency introduced by fork can be mitigated using huge pages at the cost of a bigger memory usage during persistence. The following appeindex describe in details this feature as implemented in the Linux kernel.
Some CPUs can use different page size though. AMD and Intel CPUs can support 2 MB page size if needed. These pages are nicknamed huge pages. Some operating systems can optimize page size in real time, transparently aggregating small pages into huge pages on the fly.
On Linux, explicit huge pages management has been introduced in 2.6.16, and implicit transparent huge pages are available starting in 2.6.38. If you run recent Linux distributions (for example RH 6 or derivatives), transparent huge pages can be activated, and you can use a vanilla Redis version with them.
This is the preferred way to experiment/use with huge pages on Linux.
Now, if you run older distributions (RH 5, SLES 10-11, or derivatives), and not afraid of a few hacks, Redis requires to be patched in order to support huge pages.
The first step would be to read Mel Gorman's primer on huge pages
There are currently two ways to patch Redis to support huge pages.
  • For Redis 2.4, the embedded jemalloc allocator must be patched. patch by Pieter Noordhuis. Note this patch relies on the anonymous mmap huge page support, only available starting 2.6.32, so this method cannot be used for older distributions (RH 5, SLES 10, and derivatives).
  • For Redis 2.2, or 2.4 with the libc allocator, Redis makefile must be altered to link Redis with the libhugetlbfs library. It is a straightforwardchange
Then, the system must be configured to support huge pages.
The following command allocates and makes N huge pages available:
$ sudo sysctl -w vm.nr_hugepages=
The following command mounts the huge page filesystem:
$ sudo mount -t hugetlbfs none /mnt/hugetlbfs
In all cases, once Redis is running with huge pages (transparent or not), the following benefits are expected:
  • The latency due to the fork operations is dramatically reduced. This is mostly useful for very large instances, and especially on a VM.
  • Redis is faster due to the fact the translation look-aside buffer (TLB) of the CPU is more efficient to cache page table entries (i.e. the hit ratio is better). Do not expect miracle, it is only a few percent gain at most.
  • Redis memory cannot be swapped out anymore, which is interesting to avoid outstanding latencies due to virtual memory.
Unfortunately, and on top of the extra operational complexity, there is also a significant drawback of running Redis with huge pages. The COW mechanism granularity is the page. With 2 MB pages, the probability a page is modified during a background save operation is 512 times higher than with 4 KB pages. The actual memory required for a background save therefore increases a lot, especially if the write traffic is truly random, with poor locality. With huge pages, using twice the memory while saving is not anymore a theoretical incident. It really happens.
The result of a complete benchmark can be found here.

深入理解浏览器兼容性模式


关于各种浏览器模式,网上已经有许多文档和资料了,但是很少有能够完全将几个概念阐述清楚的。大部分的资料稍显过时,有些内容可能已经不再适用了。本文中笔者将尽可能将几个概念阐述清楚,并去掉一些过时的内容,仅保留必要的干货。

想必你一定知道浏览器有个标准(Standards)模式和一个怪异(Quirks)模式,或许你还听说过有个“准标准(Almost Standards)”模式。而当你打开 Internet Explorer 的时候,又看到了什么浏览器模式、文档模式,还有什么兼容性视图等等...

这些都是什么?啥是浏览器模式,啥是文档模式?标准模式和准标准的模式有什么区别?IE9兼容性视图和真正的IE9有什么区别?什么情况下会触发这些模式,又该怎样才能检测到浏览器当前处于哪种模式中呢?本文将详细为你解答这些疑问。

三种模式

首先我们要知道,为什么会有这么多模式。其实这是个历史遗留问题,在浏览器大战时期,网景浏览器(Netscape Navigator)和微软的IE浏览器(Microsoft Internet Explorer)对网页分别有不同的实现方式,那个时候的网页要针对这两种浏览器分别开发不同的版本。而到了 W3C 制定标准之后,这些浏览器就不能继续使用这种页面了,因而会导致大部分现有站点都不能使用。基于这个原因,浏览器才引入两种模式来处理一些遗留的站点。

现在的浏览器排版引擎支持三种模式:怪异(Quirks)模式、准标准(Almost Standards)和标准(Standards)模式。在怪异模式中,排版引擎会模拟 网景4 和 Windows 中的 IE5 的行为;在完全标准的模式中,会尽量执行 HTML 和 CSS 规范所指定的行为;而在准标准模式中,则只包含很少的一部分怪异模式中的行为。

那么所谓标准模式,就一定都“标准”吗?答案当然是否定的,因为各个浏览器厂商实现标准的阶段不同,所以各个浏览器的“标准模式”之间也会有很大的不同。

Firefox、Safari、Chrome、Opera (自 7.5 以后)、 IE8 和 IE9 都有一个准标准模式。那么既然标准模式都不那么标准,准标准的模式肯定就更不标准了。最初的准标准模式只会影响表格中的图像,而后来各个浏览器又或多或少地进行了修改。那么什么情况下会触发准标准模式呢?是的,正如你所想到的,某些 DOCTYPE 会触发准标准模式,例如:

"-//W3C//DTD XHTML 1.0 Transitional//EN"
"-//W3C//DTD XHTML 1.0 Frameset//EN"
"-//W3C//DTD HTML 4.01 Transitional//EN"
"-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd"
一个完整的 DOCTYPE 例子如下:

                      "http://www.w3.org/TR/html4/loose.dtd">
如果在 Firefox 中插入这种 DOCTYPE,并在页面中插入一个空的 span 标签,那么在 Firebug 中查看元素的布局就会发现不同:

准标准模式中元素的 line-height 被忽略了,元素既没有宽度也没有高度:



标准模式中元素仍然保留了 line-height,拥有 18px 的高度:



在 Firefox 浏览器中,使用鼠标右键 -> 查看页面信息 可以看到当前浏览器运行在何种模式(只能看到“混杂模式”和“标准规范模式”两种表示):



有位大神 Henri Sivonen 曾写过一篇文章叫做 Activating Browser Modes with Doctype,里面包含了一个完整的表格,展示了各种 DOCTYPE 设置将会使浏览器以何种方式渲染。这里还有一篇 秦歌 的译文 《用doctype激活浏览器模式》。

鉴于目前一些最新版本的浏览器已经放弃了准标准模式,所以关于准标准模式的细节这里就不再赘述了,感兴趣的同学可以详细阅读以下资料:

Gecko's "Almost Standards" Mode

Line Height Calculations in Almost Standards Mode

Images, Tables, and Mysterious Gaps

almost-standards test

DOCTYPE Switches support in Opera Presto 2.10

那么,既然这么多的 DOCTYPE 都会触发非标准的模式,那么如何才能触发标准模式呢?对了!要使用 HTML5 DOCTYPE,即:


注意:如果文档中没有包含 DOCTYPE 或者包含了一个无法识别的 DOCTYPE,则浏览器就会进入怪异模式。

下面简单说一下怪异模式。怪异模式有许多“怪异”的行为,主要是为了兼容那些遗留的古老页面而保留的模式。不同浏览器的怪异模式也不尽相同,它们都有自己的实现方式。怪异模式与标准模式的差异主要体现在 盒模型(box model)、表格单元格高度的处理等。例如 IE 的怪异模式中,元素的 width 包含了 padding 和 border,而标准模式中 padding 和 border 并不属于宽度的一部分。

若想详细了解浏览器在怪异模式下的行为,可以参看下面两篇文章。不过不建议在这上面花太多的精力,这是个历史遗留问题,而且我们也尽量保证新开发的页面不要进入到怪异模式:

Mozilla Quirks Mode Behavior

What happens in Quirks Mode?

Compatability Mode Test

小结: 至此我们需要了解,浏览器有三种运行模式,即标准模式、准标准模式和怪异模式,要使用 来正确地触发标准模式。千万不要丢掉 DOCTYPE 声明,因为这会导致浏览器进入怪异模式。

IE的浏览器模式

IE8有4种模式:IE5.5怪异模式、IE7标准模式、IE8 准标准模式和IE8标准模式,而IE9有7种模式: IE5.5怪异模式、IE7标准模式、IE8准标准模式、IE8标准模式、IE9准标准模式、IE9标准模式、XML模式。

其中 XML模式 是针对 XML 文档的,这里不打算阐述,细节可以看这篇文章[Defining Document Compatibility](http://msdn.microsoft.com/en-us/library/cc288325(v=vs.85).aspx) 中有详细阐述。

在 IE8 及以后的的 IE 浏览器中,支持 X-UA-Compatible 头,可以通过在服务器端设置 HTTP 头,或者在页面中插入 标签来实现:

HTTP:
Header set X-UA-Compatible "IE=8"

Meta:

这种方法主要是防止老的页面在较新的浏览器中显示不正常的情况的, 比如上面的代码可以强制 IE8 以上版本的浏览器以IE7的模式进行渲染。

注意,不要在新开发的网页中使用这种技术,这种技术只应该作为新老网页更替过程中的过渡方案。由于目前新开发的网页都是尽量支持最新版本的浏览器的,所以这种技术也会慢慢被淘汰,感兴趣的同学可以详细阅读 微软的这篇文档。

小结: 这里我们需要知道有这种方式可以强制浏览器以某种模式运行,但只应作为过渡方案,不应在新开发的网页中使用。

IE9 兼容性视图 与 IE9 标准视图

如果你使用的是 IE9,那么按下 F12 键就会出现开发者工具,上面有两个下拉菜单:浏览器模式 和 文档模式。那么什么是浏览器模式?什么又是文档模式?二者有何区别?

浏览器模式用于切换IE针对该网页的默认文档模式、对不同版本浏览器的条件注释解析、以及发送给网站服务器的用户代理(User-Agent)字符串的值。网站可以根据浏览器返回的不同用户代理字符串判断浏览器的版本和及安装的功能,这样就可以根据不同的浏览器返回不同的页面内容了。

文档模式用于指定IE的页面排版引擎(Trident)以哪个版本的方式来解析并渲染网页代码。切换文档模式会导致网页被刷新,但不会更改用户代理字符串中的版本号,也不会从服务器重新下载网页。切换浏览器模式的同时,浏览器也会自动切换到相应的文档模式。

一言以蔽之,浏览器模式会影响服务器端对客户端浏览器版本的判断,对条件注释也有影响;而文档模式会影响IE的排版引擎,对网页渲染会有影响,对 CSS hack 也会产生影响。因此,通过条件注释可以判断浏览器模式,而使用 CSS hack 可以判断文档模式。

如果我们使用一句简单的 JavaScript 语句来查看用户代理(User-Agent)字符串的值,则可以看到 IE9 兼容性视图 与 IE9 的区别:


输出结果如下所示,注意其中的 MSIE 版本号已经不同。判断浏览器模式就是判断 User-Agent 中的版本号,即 MSIE 后面的数值:

// IE9
UA:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)

// IE9 兼容性视图
UA:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
话说 IE9 兼容性视图 是模拟IE7的行为,那么 IE9 兼容性视图 与 IE7 有没有区别呢?肯定是有区别的,即使是 IE9 中的 IE7标准模式,与原生的IE7在渲染上也是有区别的,具体我们暂不去深究。

那么既然 IE9 兼容性视图 的版本号跟 IE7 相同,如何才能判断当前是 IE9 兼容性视图,还是纯正的 IE7 呢?其实很简单,只需要判断浏览器的用户代理(User-Agent)字符串中是否包含 Trident 即可。首先检测 MSIE 的版本号是否为 7.0,然后再判断是否含有 Trident 字串,若包含则为 IE9 兼容性视图,否则则为纯正的 IE7。

小结: 至此,你应该了解了什么是浏览器模式、什么是文档模式以及它们之间的区别了,另外还了解了 IE9 兼容性视图 与 IE9 以及 IE7 的区别。

控制默认的渲染方式

当 Internet Explorer 9 遇到未包含 X-UA-Compatible 标头的网页时,它将使用 指令来确定如何显示该网页。 如果该指令丢失或未指定基于标准的文档类型,则 Internet Explorer 9 将以 IE5 模式(怪异模式)来显示该网页。

如果 指令指定了基于标准的文档类型,则 Internet Explorer 9 将以 IE9 模式显示该网页,但出现以下情况时除外:

为该网页启用了兼容性视图。
该网页是在 Intranet 区域中加载的,并且已将 Internet Explorer 9 配置为使用兼容性视图来显示 Intranet 区域中的网页。
已将 Internet Explorer 9 配置为使用兼容性视图来显示所有网站。
已将 Internet Explorer 9 配置为使用兼容性视图列表(其实是个黑名单,其中指定了一组始终使用兼容性视图显示的网站)。
已使用开发人员工具覆盖在该网页中指定的设置。
该网页遇到了页面布局错误,并且已将 Internet Explorer 9 配置为,通过在兼容性视图中重新打开网页来自动从此类错误中恢复。
此外,可以使用下面的注册表项来控制 Internet Explorer 对未包含 X-UA-Compatible 标头的页面的处理方式。

HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
     SOFTWARE
          Microsoft
               Internet Explorer
                    Main
                         FeatureControl
                              FEATURE_BROWSER_EMULATION
                                   iexplore.exe = (DWORD)
其中 DWORD 值必须等于下列值之一:

说明
7000 包含基于标准的 指令的页面将以 IE7 模式显示。
8000 包含基于标准的 指令的页面以 IE8 模式显示。
8888 页面始终以 IE8 模式显示,而不考虑 指令。 (这可绕过前面列出的例外情况。)
关于IE浏览器确定文档模式的整个流程,可以参看这篇文章 How IE8 Determines Document Mode,文中详细阐述了整个流程与内部机制。

小结: 仍然坚持使用 ,可最大程度减小发生错误的几率。

文档模式的检测

在 JavaScript 中可以通过 documentMode 来检测文档模式,在 IE6 和 IE7 中是使用 compatMode 来确定文档模式的,这个属性自 IE8 开始已经被 documentMode 所替代。

那么,如果需要兼容 IE6 和 IE7 的话(必须的 ...),则相应的检测代码大致如下:

engine = null;
if (window.navigator.appName == "Microsoft Internet Explorer")
{
   // This is an IE browser. What mode is the engine in?
   if (document.documentMode) // IE8 or later
      engine = document.documentMode;
   else // IE 5-7
   {
      engine = 5; // Assume quirks mode unless proven otherwise
      if (document.compatMode)
      {
         if (document.compatMode == "CSS1Compat")
            engine = 7; // standards mode
      }
      // There is no test for IE6 standards mode because that mode
      // was replaced by IE7 standards mode; there is no emulation.
   }
   // the engine variable now contains the document compatibility mode.
}
IE6 和 IE7 中的 compatMode 有两个可能的值“CSS1Compat”和“BackCompat ”,分别对应了 IE6 和 IE7 中的标准模式和怪异模式。上面的代码首先假定是怪异模式,然后再试图推翻假设。这里没有包含“IE6 标准模式”,因为它已经被 IE7标准模式 所替代,没有模拟的情况。

这里要注意,不同的文档模式对 JavaScript 也有一些影响,我们不必去深究不同文档模式对 JavaScript 有何种不同影响,只需要在编码时进行特定的 特性检测 即可。

小结: 一般情况下是没必要进行文档模式检测的,对于样式兼容我们可以写 CSS hack,而对于 JavaScript 来说,则更加推荐特性检测,而不是检测浏览器本身。

浏览器模式与文档模式之间的关系

浏览器模式可以决定页面默认的文档模式,但文档模式可能会受其他因素影响而改变,如上文所述。如果浏览器模式与文档模式设置不同的话,会不会有什么影响呢?

我们已经知道浏览器模式主要用于标识浏览器本身,原则上不会对页面渲染产生影响。但是我们又知道,浏览器模式可以影响条件注释,所以如果你的页面中有条件注释的话,那么浏览器模式的变化就会影响到页面渲染。

服务器端只能通过浏览器模式所标识的版本来确定客户端浏览器的版本,如果你将浏览器模式标识为IE9,但文档模式选择为IE7标准的话,就可能会有问题。不过这还要看服务器端是否有针对不同浏览器的处理策略,如果服务器端并未对不同浏览器的输出做差异化处理的话,那么这两个模式选项不同就不会有问题。

小结: 如果服务器端对不同浏览器的输出做了差异化处理,那么浏览器模式和文档模式不一致就可能产生问题。

结语

本文参考了大量现有文献,详细阐述了各种模式的区别以及它们之间的关系。相信通过上面的叙述,你已经能够区分这些浏览器模式或者文档模式以及它们之间的关系了,每节的结论在小结中已有阐述,希望能够对你有所帮助。

参考文献

http://en.wikipedia.org/wiki/Browser_wars

http://meyerweb.com/eric/thoughts/2008/01/24/almost-target/

https://developer.mozilla.org/en-US/docs/Images,_Tables,_and_Mysterious_Gaps

http://en.wikipedia.org/wiki/Document_Type_Declaration

http://dev.w3.org/html5/spec/#the-doctype

http://blogs.msdn.com/b/ie/archive/2010/03/02/how-ie8-determines-document-mode.aspx

http://kangax.github.com/cft/

http://blogs.msdn.com/b/ie/archive/2011/03/24/ie9-s-document-modes-and-javascript.aspx

http://msdn.microsoft.com/en-us/library/cc288325(v=vs.85).aspx

https://developer.mozilla.org/en-US/docs/Quirks_Mode_and_Standards_Mode

https://developer.mozilla.org/en-US/docs/Gecko's_Almost_Standards_Mode

http://blogs.msdn.com/b/ie/archive/2009/02/16/just-the-facts-recap-of-compatibility-view.aspx

http://www.quirksmode.org/css/quirksmode.html

http://hsivonen.iki.fi/doctype/

http://blogs.msdn.com/b/ie/archive/2011/03/24/ie9-s-document-modes-and-javascript.aspx

https://developer.mozilla.org/en-US/docs/Mozilla_Quirks_Mode_Behavior

https://developer.mozilla.org/en-US/docs/Gecko's_Almost_Standards_Mode

http://meyerweb.com/eric/css/tests/almost-standards.html

http://www.opera.com/docs/specs/doctype/