mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-03-21 02:10:11 +08:00
Merge remote-tracking branch 'LCTT/master'
This commit is contained in:
commit
04c9325d46
@ -1,402 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (LazyWolfLin)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Why const Doesn't Make C Code Faster)
|
||||
[#]: via: (https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html)
|
||||
[#]: author: (Simon Arneaud https://theartofmachinery.com)
|
||||
|
||||
Why const Doesn't Make C Code Faster
|
||||
======
|
||||
|
||||
In a post a few months back I said [it’s a popular myth that `const` is helpful for enabling compiler optimisations in C and C++][1]. I figured I should explain that one, especially because I used to believe it was obviously true, myself. I’ll start off with some theory and artificial examples, then I’ll do some experiments and benchmarks on a real codebase: Sqlite.
|
||||
|
||||
### A simple test
|
||||
|
||||
Let’s start with what I used to think was the simplest and most obvious example of how `const` can make C code faster. First, let’s say we have these two function declarations:
|
||||
|
||||
```
|
||||
void func(int *x);
|
||||
void constFunc(const int *x);
|
||||
```
|
||||
|
||||
And suppose we have these two versions of some code:
|
||||
|
||||
```
|
||||
void byArg(int *x)
|
||||
{
|
||||
printf("%d\n", *x);
|
||||
func(x);
|
||||
printf("%d\n", *x);
|
||||
}
|
||||
|
||||
void constByArg(const int *x)
|
||||
{
|
||||
printf("%d\n", *x);
|
||||
constFunc(x);
|
||||
printf("%d\n", *x);
|
||||
}
|
||||
```
|
||||
|
||||
To do the `printf()`, the CPU has to fetch the value of `*x` from RAM through the pointer. Obviously, `constByArg()` can be made slightly faster because the compiler knows that `*x` is constant, so there’s no need to load its value a second time after `constFunc()` does its thing. It’s just printing the same thing. Right? Let’s see the assembly code generated by GCC with optimisations cranked up:
|
||||
|
||||
```
|
||||
$ gcc -S -Wall -O3 test.c
|
||||
$ view test.s
|
||||
```
|
||||
|
||||
Here’s the full assembly output for `byArg()`:
|
||||
|
||||
```
|
||||
byArg:
|
||||
.LFB23:
|
||||
.cfi_startproc
|
||||
pushq %rbx
|
||||
.cfi_def_cfa_offset 16
|
||||
.cfi_offset 3, -16
|
||||
movl (%rdi), %edx
|
||||
movq %rdi, %rbx
|
||||
leaq .LC0(%rip), %rsi
|
||||
movl $1, %edi
|
||||
xorl %eax, %eax
|
||||
call __printf_chk@PLT
|
||||
movq %rbx, %rdi
|
||||
call func@PLT # The only instruction that's different in constFoo
|
||||
movl (%rbx), %edx
|
||||
leaq .LC0(%rip), %rsi
|
||||
xorl %eax, %eax
|
||||
movl $1, %edi
|
||||
popq %rbx
|
||||
.cfi_def_cfa_offset 8
|
||||
jmp __printf_chk@PLT
|
||||
.cfi_endproc
|
||||
```
|
||||
|
||||
The only difference between the generated assembly code for `byArg()` and `constByArg()` is that `constByArg()` has a `call constFunc@PLT`, just like the source code asked. The `const` itself has literally made zero difference.
|
||||
|
||||
Okay, that’s GCC. Maybe we just need a sufficiently smart compiler. Is Clang any better?
|
||||
|
||||
```
|
||||
$ clang -S -Wall -O3 -emit-llvm test.c
|
||||
$ view test.ll
|
||||
```
|
||||
|
||||
Here’s the IR. It’s more compact than assembly, so I’ll dump both functions so you can see what I mean by “literally zero difference except for the call”:
|
||||
|
||||
```
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @byArg(i32*) local_unnamed_addr #0 {
|
||||
%2 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
|
||||
tail call void @func(i32* %0) #4
|
||||
%4 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @constByArg(i32*) local_unnamed_addr #0 {
|
||||
%2 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
|
||||
tail call void @constFunc(i32* %0) #4
|
||||
%4 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
### Something that (sort of) works
|
||||
|
||||
Here’s some code where `const` actually does make a difference:
|
||||
|
||||
```
|
||||
void localVar()
|
||||
{
|
||||
int x = 42;
|
||||
printf("%d\n", x);
|
||||
constFunc(&x);
|
||||
printf("%d\n", x);
|
||||
}
|
||||
|
||||
void constLocalVar()
|
||||
{
|
||||
const int x = 42; // const on the local variable
|
||||
printf("%d\n", x);
|
||||
constFunc(&x);
|
||||
printf("%d\n", x);
|
||||
}
|
||||
```
|
||||
|
||||
Here’s the assembly for `localVar()`, which has two instructions that have been optimised out of `constLocalVar()`:
|
||||
|
||||
```
|
||||
localVar:
|
||||
.LFB25:
|
||||
.cfi_startproc
|
||||
subq $24, %rsp
|
||||
.cfi_def_cfa_offset 32
|
||||
movl $42, %edx
|
||||
movl $1, %edi
|
||||
movq %fs:40, %rax
|
||||
movq %rax, 8(%rsp)
|
||||
xorl %eax, %eax
|
||||
leaq .LC0(%rip), %rsi
|
||||
movl $42, 4(%rsp)
|
||||
call __printf_chk@PLT
|
||||
leaq 4(%rsp), %rdi
|
||||
call constFunc@PLT
|
||||
movl 4(%rsp), %edx # not in constLocalVar()
|
||||
xorl %eax, %eax
|
||||
movl $1, %edi
|
||||
leaq .LC0(%rip), %rsi # not in constLocalVar()
|
||||
call __printf_chk@PLT
|
||||
movq 8(%rsp), %rax
|
||||
xorq %fs:40, %rax
|
||||
jne .L9
|
||||
addq $24, %rsp
|
||||
.cfi_remember_state
|
||||
.cfi_def_cfa_offset 8
|
||||
ret
|
||||
.L9:
|
||||
.cfi_restore_state
|
||||
call __stack_chk_fail@PLT
|
||||
.cfi_endproc
|
||||
```
|
||||
|
||||
The LLVM IR is a little clearer. The `load` just before the second `printf()` call has been optimised out of `constLocalVar()`:
|
||||
|
||||
```
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @localVar() local_unnamed_addr #0 {
|
||||
%1 = alloca i32, align 4
|
||||
%2 = bitcast i32* %1 to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %2) #4
|
||||
store i32 42, i32* %1, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 42)
|
||||
call void @constFunc(i32* nonnull %1) #4
|
||||
%4 = load i32, i32* %1, align 4, !tbaa !2
|
||||
%5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %2) #4
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
Okay, so, `constLocalVar()` has sucessfully elided the reloading of `*x`, but maybe you’ve noticed something a bit confusing: it’s the same `constFunc()` call in the bodies of `localVar()` and `constLocalVar()`. If the compiler can deduce that `constFunc()` didn’t modify `*x` in `constLocalVar()`, why can’t it deduce that the exact same function call didn’t modify `*x` in `localVar()`?
|
||||
|
||||
The explanation gets closer to the heart of why C `const` is impractical as an optimisation aid. C `const` effectively has two meanings: it can mean the variable is a read-only alias to some data that may or may not be constant, or it can mean the variable is actually constant. If you cast away `const` from a pointer to a constant value and then write to it, the result is undefined behaviour. On the other hand, it’s okay if it’s just a `const` pointer to a value that’s not constant.
|
||||
|
||||
This possible implementation of `constFunc()` shows what that means:
|
||||
|
||||
```
|
||||
// x is just a read-only pointer to something that may or may not be a constant
|
||||
void constFunc(const int *x)
|
||||
{
|
||||
// local_var is a true constant
|
||||
const int local_var = 42;
|
||||
|
||||
// Definitely undefined behaviour by C rules
|
||||
doubleIt((int*)&local_var);
|
||||
// Who knows if this is UB?
|
||||
doubleIt((int*)x);
|
||||
}
|
||||
|
||||
void doubleIt(int *x)
|
||||
{
|
||||
*x *= 2;
|
||||
}
|
||||
```
|
||||
|
||||
`localVar()` gave `constFunc()` a `const` pointer to non-`const` variable. Because the variable wasn’t originally `const`, `constFunc()` can be a liar and forcibly modify it without triggering UB. So the compiler can’t assume the variable has the same value after `constFunc()` returns. The variable in `constLocalVar()` really is `const`, though, so the compiler can assume it won’t change — because this time it _would_ be UB for `constFunc()` to cast `const` away and write to it.
|
||||
|
||||
The `byArg()` and `constByArg()` functions in the first example are hopeless because the compiler has no way of knowing if `*x` really is `const`.
|
||||
|
||||
But why the inconsistency? If the compiler can assume that `constFunc()` doesn’t modify its argument when called in `constLocalVar()`, surely it can go ahead an apply the same optimisations to other `constFunc()` calls, right? Nope. The compiler can’t assume `constLocalVar()` is ever run at all. If it isn’t (say, because it’s just some unused extra output of a code generator or macro), `constFunc()` can sneakily modify data without ever triggering UB.
|
||||
|
||||
You might want to read the above explanation and examples a few times, but don’t worry if it sounds absurd: it is. Unfortunately, writing to `const` variables is the worst kind of UB: most of the time the compiler can’t know if it even would be UB. So most of the time the compiler sees `const`, it has to assume that someone, somewhere could cast it away, which means the compiler can’t use it for optimisation. This is true in practice because enough real-world C code has “I know what I’m doing” casting away of `const`.
|
||||
|
||||
In short, a whole lot of things can prevent the compiler from using `const` for optimisation, including receiving data from another scope using a pointer, or allocating data on the heap. Even worse, in most cases where `const` can be used by the compiler, it’s not even necessary. For example, any decent compiler can figure out that `x` is constant in the following code, even without `const`:
|
||||
|
||||
```
|
||||
int x = 42, y = 0;
|
||||
printf("%d %d\n", x, y);
|
||||
y += x;
|
||||
printf("%d %d\n", x, y);
|
||||
```
|
||||
|
||||
TL;DR: `const` is almost useless for optimisation because
|
||||
|
||||
1. Except for special cases, the compiler has to ignore it because other code might legally cast it away
|
||||
2. In most of the exceptions to #1, the compiler can figure out a variable is constant, anyway
|
||||
|
||||
|
||||
|
||||
### C++
|
||||
|
||||
There’s another way `const` can affect code generation if you’re using C++: function overloads. You can have `const` and non-`const` overloads of the same function, and maybe the non-`const` can be optimised (by the programmer, not the compiler) to do less copying or something.
|
||||
|
||||
```
|
||||
void foo(int *p)
|
||||
{
|
||||
// Needs to do more copying of data
|
||||
}
|
||||
|
||||
void foo(const int *p)
|
||||
{
|
||||
// Doesn't need defensive copies
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int x = 42;
|
||||
// const-ness affects which overload gets called
|
||||
foo(&x);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
On the one hand, I don’t think this is exploited much in practical C++ code. On the other hand, to make a real difference, the programmer has to make assumptions that the compiler can’t make because they’re not guaranteed by the language.
|
||||
|
||||
### An experiment with Sqlite3
|
||||
|
||||
That’s enough theory and contrived examples. How much effect does `const` have on a real codebase? I thought I’d do a test on the Sqlite database (version 3.30.0) because
|
||||
|
||||
* It actually uses `const`
|
||||
* It’s a non-trivial codebase (over 200KLOC)
|
||||
* As a database, it includes a range of things from string processing to arithmetic to date handling
|
||||
* It can be tested with CPU-bound loads
|
||||
|
||||
|
||||
|
||||
Also, the author and contributors have put years of effort into performance optimisation already, so I can assume they haven’t missed anything obvious.
|
||||
|
||||
#### The setup
|
||||
|
||||
I made two copies of [the source code][2] and compiled one normally. For the other copy, I used this hacky preprocessor snippet to turn `const` into a no-op:
|
||||
|
||||
```
|
||||
#define const
|
||||
```
|
||||
|
||||
(GNU) `sed` can add that to the top of each file with something like `sed -i '1i#define const' *.c *.h`.
|
||||
|
||||
Sqlite makes things slightly more complicated by generating code using scripts at build time. Fortunately, compilers make a lot of noise when `const` and non-`const` code are mixed, so it was easy to detect when this happened, and tweak the scripts to include my anti-`const` snippet.
|
||||
|
||||
Directly diffing the compiled results is a bit pointless because a tiny change can affect the whole memory layout, which can change pointers and function calls throughout the code. Instead I took a fingerprint of the disassembly (`objdump -d libsqlite3.so.0.8.6`), using the binary size and mnemonic for each instruction. For example, this function:
|
||||
|
||||
```
|
||||
000000000005d570 <sqlite3_blob_read>:
|
||||
5d570: 4c 8d 05 59 a2 ff ff lea -0x5da7(%rip),%r8 # 577d0 <sqlite3BtreePayloadChecked>
|
||||
5d577: e9 04 fe ff ff jmpq 5d380 <blobReadWrite>
|
||||
5d57c: 0f 1f 40 00 nopl 0x0(%rax)
|
||||
```
|
||||
|
||||
would turn into something like this:
|
||||
|
||||
```
|
||||
sqlite3_blob_read 7lea 5jmpq 4nopl
|
||||
```
|
||||
|
||||
I left all the Sqlite build settings as-is when compiling anything.
|
||||
|
||||
#### Analysing the compiled code
|
||||
|
||||
The `const` version of libsqlite3.so was 4,740,704 bytes, about 0.1% larger than the 4,736,712 bytes of the non-`const` version. Both had 1374 exported functions (not including low-level helpers like stuff in the PLT), and a total of 13 had any difference in fingerprint.
|
||||
|
||||
A few of the changes were because of the dumb preprocessor hack. For example, here’s one of the changed functions (with some Sqlite-specific definitions edited out):
|
||||
|
||||
```
|
||||
#define LARGEST_INT64 (0xffffffff|(((int64_t)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((int64_t)-1) - LARGEST_INT64)
|
||||
|
||||
static int64_t doubleToInt64(double r){
|
||||
/*
|
||||
** Many compilers we encounter do not define constants for the
|
||||
** minimum and maximum 64-bit integers, or they define them
|
||||
** inconsistently. And many do not understand the "LL" notation.
|
||||
** So we define our own static constants here using nothing
|
||||
** larger than a 32-bit integer constant.
|
||||
*/
|
||||
static const int64_t maxInt = LARGEST_INT64;
|
||||
static const int64_t minInt = SMALLEST_INT64;
|
||||
|
||||
if( r<=(double)minInt ){
|
||||
return minInt;
|
||||
}else if( r>=(double)maxInt ){
|
||||
return maxInt;
|
||||
}else{
|
||||
return (int64_t)r;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Removing `const` makes those constants into `static` variables. I don’t see why anyone who didn’t care about `const` would make those variables `static`. Removing both `static` and `const` makes GCC recognise them as constants again, and we get the same output. Three of the 13 functions had spurious changes because of local `static const` variables like this, but I didn’t bother fixing any of them.
|
||||
|
||||
Sqlite uses a lot of global variables, and that’s where most of the real `const` optimisations came from. Typically they were things like a comparison with a variable being replaced with a constant comparison, or a loop being partially unrolled a step. (The [Radare toolkit][3] was handy for figuring out what the optimisations did.) A few changes were underwhelming. `sqlite3ParseUri()` is 487 instructions, but the only difference `const` made was taking this pair of comparisons:
|
||||
|
||||
```
|
||||
test %al, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
cmp $0x23, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
```
|
||||
|
||||
And swapping their order:
|
||||
|
||||
```
|
||||
cmp $0x23, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
test %al, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
```
|
||||
|
||||
#### Benchmarking
|
||||
|
||||
Sqlite comes with a performance regression test, so I tried running it a hundred times for each version of the code, still using the default Sqlite build settings. Here are the timing results in seconds:
|
||||
|
||||
| const | No const
|
||||
---|---|---
|
||||
Minimum | 10.658s | 10.803s
|
||||
Median | 11.571s | 11.519s
|
||||
Maximum | 11.832s | 11.658s
|
||||
Mean | 11.531s | 11.492s
|
||||
|
||||
Personally, I’m not seeing enough evidence of a difference worth caring about. I mean, I removed `const` from the entire program, so if it made a significant difference, I’d expect it to be easy to see. But maybe you care about any tiny difference because you’re doing something absolutely performance critical. Let’s try some statistical analysis.
|
||||
|
||||
I like using the Mann-Whitney U test for stuff like this. It’s similar to the more-famous t test for detecting differences in groups, but it’s more robust to the kind of complex random variation you get when timing things on computers (thanks to unpredictable context switches, page faults, etc). Here’s the result:
|
||||
|
||||
| const | No const
|
||||
---|---|---
|
||||
N | 100 | 100
|
||||
Mean rank | 121.38 | 79.62
|
||||
Mann-Whitney U | 2912
|
||||
---|---
|
||||
Z | -5.10
|
||||
2-sided p value | <10-6
|
||||
HL median difference | -.056s
|
||||
95% confidence interval | -.077s – -0.038s
|
||||
|
||||
The U test has detected a statistically significant difference in performance. But, surprise, it’s actually the non-`const` version that’s faster — by about 60ms, or 0.5%. It seems like the small number of “optimisations” that `const` enabled weren’t worth the cost of extra code. It’s not like `const` enabled any major optimisations like auto-vectorisation. Of course, your mileage may vary with different compiler flags, or compiler versions, or codebases, or whatever, but I think it’s fair to say that if `const` were effective at improving C performance, we’d have seen it by now.
|
||||
|
||||
### So, what’s `const` for?
|
||||
|
||||
For all its flaws, C/C++ `const` is still useful for type safety. In particular, combined with C++ move semantics and `std::unique_pointer`s, `const` can make pointer ownership explicit. Pointer ownership ambiguity was a huge pain in old C++ codebases over ~100KLOC, so personally I’m grateful for that alone.
|
||||
|
||||
However, I used to go beyond using `const` for meaningful type safety. I’d heard it was best practices to use `const` literally as much as possible for performance reasons. I’d heard that when performance really mattered, it was important to refactor code to add more `const`, even in ways that made it less readable. That made sense at the time, but I’ve since learned that it’s just not true.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html
|
||||
|
||||
作者:[Simon Arneaud][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[LazyWolfLin](https://github.com/LazyWolfLin)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://theartofmachinery.com
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://theartofmachinery.com/2019/04/05/d_as_c_replacement.html#const-and-immutable
|
||||
[2]: https://sqlite.org/src/doc/trunk/README.md
|
||||
[3]: https://rada.re/r/
|
@ -1,261 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hello-wn)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to Create and Use Swap File on Linux)
|
||||
[#]: via: (https://itsfoss.com/create-swap-file-linux/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
How to Create and Use Swap File on Linux
|
||||
======
|
||||
|
||||
This tutorial discusses the concept of swap file in Linux, why it is used and its advantages over the traditional swap partition. You’ll learn how to create swap file or resize it.
|
||||
|
||||
### What is a swap file in Linux?
|
||||
|
||||
A swap file allows Linux to simulate the disk space as RAM. When your system starts running out of RAM, it uses the swap space to and swaps some content of the RAM on to the disk space. This frees up the RAM to serve more important processes. When the RAM is free again, it swaps back the data from the disk. I recommend [reading this article to learn more about swap on Linux][1].
|
||||
|
||||
Traditionally, swap space is used as a separate partition on the disk. When you install Linux, you create a separate partition just for swap. But this trend has changed in the recent years.
|
||||
|
||||
With swap file, you don’t need a separate partition anymore. You create a file under root and tell your system to use it as the swap space.
|
||||
|
||||
With dedicated swap partition, resizing the swap space is a nightmare and an impossible task in many cases. But with swap files, you can resize them as you like.
|
||||
|
||||
Recent versions of Ubuntu and some other Linux distributions have started [using the swap file by default][2]. Even if you don’t create a swap partition, Ubuntu creates a swap file of around 1 GB on its own.
|
||||
|
||||
Let’s see some more on swap files.
|
||||
|
||||
![][3]
|
||||
|
||||
### Check swap space in Linux
|
||||
|
||||
Before you go and start adding swap space, it would be a good idea to check whether you have swap space already available in your system.
|
||||
|
||||
You can check it with the [free command in Linux][4]. In my case, my [Dell XPS][5] has 14GB of swap.
|
||||
|
||||
```
|
||||
free -h
|
||||
total used free shared buff/cache available
|
||||
Mem: 7.5G 4.1G 267M 971M 3.1G 2.2G
|
||||
Swap: 14G 0B 14G
|
||||
```
|
||||
|
||||
The free command gives you the size of the swap space but it doesn’t tell you if it’s a real swap partition or a swap file. The swapon command is better in this regard.
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/dev/nvme0n1p4 partition 14.9G 0B -2
|
||||
```
|
||||
|
||||
As you can see, I have 14.9 GB of swap space and it’s on a separate partition. If it was a swap file, the type would have been file instead of partition.
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/swapfile file 2G 0B -2
|
||||
```
|
||||
|
||||
If you don’ have a swap space on your system, it should show something like this:
|
||||
|
||||
```
|
||||
free -h
|
||||
total used free shared buff/cache available
|
||||
Mem: 7.5G 4.1G 267M 971M 3.1G 2.2G
|
||||
Swap: 0B 0B 0B
|
||||
```
|
||||
|
||||
The swapon command won’t show any output.
|
||||
|
||||
### Create swap file on Linux
|
||||
|
||||
If your system doesn’t have swap space or if you think the swap space is not adequate enough, you can create swap file on Linux. You can create multiple swap files as well.
|
||||
|
||||
[][6]
|
||||
|
||||
Suggested read Fix Missing System Settings In Ubuntu 14.04 [Quick Tip]
|
||||
|
||||
Let’s see how to create swap file on Linux. I am using Ubuntu 18.04 in this tutorial but it should work on other Linux distributions as well.
|
||||
|
||||
#### Step 1: Make a new swap file
|
||||
|
||||
First thing first, create a file with the size of swap space you want. Let’s say that I want to add 1 GB of swap space to my system. Use the fallocate command to create a file of size 1 GB.
|
||||
|
||||
```
|
||||
sudo fallocate -l 1G /swapfile
|
||||
```
|
||||
|
||||
It is recommended to allow only root to read and write to the swap file. You’ll even see warning like “insecure permissions 0644, 0600 suggested” when you try to use this file for swap area.
|
||||
|
||||
```
|
||||
sudo chmod 600 /swapfile
|
||||
```
|
||||
|
||||
Do note that the name of the swap file could be anything. If you need multiple swap spaces, you can give it any appropriate name like swap_file_1, swap_file_2 etc. It’s just a file with a predefined size.
|
||||
|
||||
#### Step 2: Mark the new file as swap space
|
||||
|
||||
Your need to tell the Linux system that this file will be used as swap space. You can do that with [mkswap][7] tool.
|
||||
|
||||
```
|
||||
sudo mkswap /swapfile
|
||||
```
|
||||
|
||||
You should see an output like this:
|
||||
|
||||
```
|
||||
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
|
||||
no label, UUID=7e1faacb-ea93-4c49-a53d-fb40f3ce016a
|
||||
```
|
||||
|
||||
#### Step 3: Enable the swap file
|
||||
|
||||
Now your system knows that the file swapfile can be used as swap space. But it is not done yet. You need to enable the swap file so that your system can start using this file as swap.
|
||||
|
||||
```
|
||||
sudo swapon /swapfile
|
||||
```
|
||||
|
||||
Now if you check the swap space, you should see that your Linux system recognizes and uses it as the swap area:
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/swapfile file 1024M 0B -2
|
||||
```
|
||||
|
||||
#### Step 4: Make the changes permanent
|
||||
|
||||
Whatever you have done so far is temporary. Reboot your system and all the changes will disappear.
|
||||
|
||||
You can make the changes permanent by adding the newly created swap file to /etc/fstab file.
|
||||
|
||||
It’s always a good idea to make a backup before you make any changes to the /etc/fstab file.
|
||||
|
||||
```
|
||||
sudo cp /etc/fstab /etc/fstab.back
|
||||
```
|
||||
|
||||
Now you can add the following line to the end of /etc/fstab file:
|
||||
|
||||
```
|
||||
/swapfile none swap sw 0 0
|
||||
```
|
||||
|
||||
You can do it manually using a [command line text editor][8] or you just use the following command:
|
||||
|
||||
```
|
||||
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
||||
Now you have everything in place. Your swap file will be used even after you reboot your Linux system.
|
||||
|
||||
### Adjust swappiness
|
||||
|
||||
The swappiness parameters determines how often the swap space should be used. The swappiness value ranges from 0 to 100. Higher value means the swap space will be used more frequently.
|
||||
|
||||
The default swappiness in Ubuntu desktop is 60 while in server it is 1. You can check the swappiness with the following command:
|
||||
|
||||
```
|
||||
cat /proc/sys/vm/swappiness
|
||||
```
|
||||
|
||||
Why servers should use a low swappiness? Because swap is slower than RAM and for a better performance, the RAM should be utilized as much as possible. On servers, the performance factor is crucial and hence the swappinness is as low as possible.
|
||||
|
||||
[][9]
|
||||
|
||||
Suggested read How to Replace One Linux Distribution With Another From Dual Boot [Keeping Home Partition]
|
||||
|
||||
You can change the swappiness on the fly using the following systemd command:
|
||||
|
||||
```
|
||||
sudo sysctl vm.swappiness=25
|
||||
```
|
||||
|
||||
This change it only temporary though. If you want to make it permanent, you can edit the /etc/sysctl.conf file and add the swappiness value in the end of the file:
|
||||
|
||||
```
|
||||
vm.swappiness=25
|
||||
```
|
||||
|
||||
### Resizing swap space on Linux
|
||||
|
||||
There are a couple of ways you can resize the swap space on Linux. But before you see that, you should learn a few things around it.
|
||||
|
||||
When you ask your system to stop using a swap file for swap area, it transfers all the data (pages to be precise) back to RAM. So you should have enough free RAM before you swap off.
|
||||
|
||||
This is why a good practice is to create and enable another temporary swap file. This way, when you swap off the original swap area, your system will use the temporary swap file. Now you can resize the original swap space. You can manually remove the temporary swap file or leave it as it is and it will be automatically deleted on the next boot.
|
||||
|
||||
If you have enough free RAM or if you created a temporary swap space, swapoff your original file.
|
||||
|
||||
```
|
||||
sudo swapoff /swapfile
|
||||
```
|
||||
|
||||
Now you can use fallocate command to change the size of the file. Let’s say, you change it to 2 GB in size:
|
||||
|
||||
```
|
||||
sudo fallocate -l 2G /swapfile
|
||||
```
|
||||
|
||||
Now mark the file as swap space again:
|
||||
|
||||
```
|
||||
sudo mkswap /swapfile
|
||||
```
|
||||
|
||||
And turn the swap on again:
|
||||
|
||||
```
|
||||
sudo swapon /swapfile
|
||||
```
|
||||
|
||||
You may also choose to have multiple swap files at the same time.
|
||||
|
||||
### Removing swap file in Linux
|
||||
|
||||
You may have your reasons for not using swap file on Linux. If you want to remove it, the process is similar to what you just saw in resizing the swap.
|
||||
|
||||
First, make sure that you have enough free RAM. Now swap off the file:
|
||||
|
||||
```
|
||||
sudo swapoff /swapfile
|
||||
```
|
||||
|
||||
The next step is to remove the respective entry from the /etc/fstab file.
|
||||
|
||||
And in the end, you can remove the file to free up the space:
|
||||
|
||||
```
|
||||
sudo rm /swapfile
|
||||
```
|
||||
|
||||
**Do you swap?**
|
||||
|
||||
I think you now have a good understanding of swap file concept in Linux. You can now easily create swap file or resize them as per your need.
|
||||
|
||||
If you have anything to add on this topic or if you have any doubts, please leave a comment below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/create-swap-file-linux/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/swap-size/
|
||||
[2]: https://help.ubuntu.com/community/SwapFaq
|
||||
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/08/swap-file-linux.png?resize=800%2C450&ssl=1
|
||||
[4]: https://linuxhandbook.com/free-command/
|
||||
[5]: https://itsfoss.com/dell-xps-13-ubuntu-review/
|
||||
[6]: https://itsfoss.com/fix-missing-system-settings-ubuntu-1404-quick-tip/
|
||||
[7]: http://man7.org/linux/man-pages/man8/mkswap.8.html
|
||||
[8]: https://itsfoss.com/command-line-text-editors-linux/
|
||||
[9]: https://itsfoss.com/replace-linux-from-dual-boot/
|
@ -0,0 +1,352 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to Find and Replace a String in File Using the sed Command in Linux)
|
||||
[#]: via: (https://www.2daygeek.com/linux-sed-to-find-and-replace-string-in-files/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
How to Find and Replace a String in File Using the sed Command in Linux
|
||||
======
|
||||
|
||||
When you are working on text files you may need to find and replace a string in the file.
|
||||
|
||||
Sed command is mostly used to replace the text in a file.
|
||||
|
||||
This can be done using the sed command and awk command in Linux.
|
||||
|
||||
In this tutorial, we will show you how to do this using the sed command and then show about the awk command.
|
||||
|
||||
### What is sed Command
|
||||
|
||||
Sed command stands for Stream Editor, It is used to perform basic text manipulation in Linux. It could perform various functions such as search, find, modify, insert or delete files.
|
||||
|
||||
Also, it’s performing complex regular expression pattern matching.
|
||||
|
||||
It can be used for the following purpose.
|
||||
|
||||
* To find and replace matches with a given format.
|
||||
* To find and replace specific lines that match a given format.
|
||||
* To find and replace the entire line that matches the given format.
|
||||
* To search and replace two different patterns simultaneously.
|
||||
|
||||
|
||||
|
||||
The fifteen examples listed in this article will help you to master in the sed command.
|
||||
|
||||
If you want to remove a line from a file using the Sed command, go to the following article.
|
||||
|
||||
**`Note:`** Since this is a demonstration article, we use the sed command without the `-i` option, which removes lines and prints the contents of the file in the Linux terminal.
|
||||
|
||||
But if you want to remove lines from the source file in the real environment, use the `-i` option with the sed command.
|
||||
|
||||
Common Syntax for sed to replace a string.
|
||||
|
||||
```
|
||||
sed -i 's/Search_String/Replacement_String/g' Input_File
|
||||
```
|
||||
|
||||
First we need to understand sed syntax to do this. See details about it.
|
||||
|
||||
* `sed:` It’s a Linux command.
|
||||
* `-i:` It’s one of the option for sed and what it does? By default sed print the results to the standard output. When you add this option with sed then it will edit files in place. A backup of the original file will be created when you add a suffix (For ex, -i.bak
|
||||
* `s:` The s is the substitute command.
|
||||
* `Search_String:` To search a given string or regular expression.
|
||||
* `Replacement_String:` The replacement string.
|
||||
* `g:` Global replacement flag. By default, the sed command replaces the first occurrence of the pattern in each line and it won’t replace the other occurrence in the line. But, all occurrences will be replaced when the replacement flag is provided
|
||||
* `/` Delimiter character.
|
||||
* `Input_File:` The filename that you want to perform the action.
|
||||
|
||||
|
||||
|
||||
Let us look at some examples of commonly used with sed command to search and convert text in files.
|
||||
|
||||
We have created the below file for demonstration purposes.
|
||||
|
||||
```
|
||||
# cat sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 1) How to Find and Replace the “first” Event of the Pattern on a Line
|
||||
|
||||
The below sed command replaces the word **unix** with **linux** in the file. This only changes the first instance of the pattern on each line.
|
||||
|
||||
```
|
||||
# sed 's/unix/linux/' sed-test.txt
|
||||
|
||||
1 Unix linux unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxlinux UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 2) How to Find and Replace the “Nth” Occurrence of the Pattern on a Line
|
||||
|
||||
Use the /1,/2,../n flags to replace the corresponding occurrence of a pattern in a line.
|
||||
|
||||
The below sed command replaces the second instance of the “unix” pattern with “linux” in a line.
|
||||
|
||||
```
|
||||
# sed 's/unix/linux/2' sed-test.txt
|
||||
|
||||
1 Unix unix linux 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 3) How to Search and Replace all Instances of the Pattern in a Line
|
||||
|
||||
The below sed command replaces all instances of the “unix” format with “Linux” on the line because “g” means a global replacement.
|
||||
|
||||
```
|
||||
# sed 's/unix/linux/g' sed-test.txt
|
||||
|
||||
1 Unix linux linux 23
|
||||
2 linux Linux 34
|
||||
3 linuxlinux UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 4) How to Find and Replace the Pattern for all Instances in a Line from the “Nth” Event
|
||||
|
||||
The below sed command replaces all the patterns from the “Nth” instance of a pattern in a line.
|
||||
|
||||
```
|
||||
# sed 's/unix/linux/2g' sed-test.txt
|
||||
|
||||
1 Unix unix linux 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 5) Search and Replace the pattern on a specific line number
|
||||
|
||||
You can able to replace the string on a specific line number. The below sed command replaces the pattern “unix” with “linux” only on the 3rd line.
|
||||
|
||||
```
|
||||
# sed '3 s/unix/linux/' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxlinux UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 6) How to Find and Replace Pattern in a Range of Lines
|
||||
|
||||
You can specify the range of line numbers to replace the string.
|
||||
|
||||
The below sed command replaces the “Unix” pattern with “Linux” with lines 1 through 3.
|
||||
|
||||
```
|
||||
# sed '1,3 s/unix/linux/' sed-test.txt
|
||||
|
||||
1 Unix linux unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxlinux UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 7) How to Find and Change the pattern in the Last Line
|
||||
|
||||
The below sed command allows you to replace the matching string only in the last line.
|
||||
|
||||
The below sed command replaces the “Linux” pattern with “Unix” only on the last line.
|
||||
|
||||
```
|
||||
# sed '$ s/Linux/Unix/' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Unix is free and opensource operating system
|
||||
```
|
||||
|
||||
### 8) How to Find and Replace the Pattern with only Right Word in a Line
|
||||
|
||||
As you might have noticed, the substring “linuxunix” is replaced with “linuxlinux” in the 6th example. If you want to replace only the right matching word, use the word-boundary expression “\b” on both ends of the search string.
|
||||
|
||||
```
|
||||
# sed '1,3 s/\bunix\b/linux/' sed-test.txt
|
||||
|
||||
1 Unix linux unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 9) How to Search and Replaces the pattern with case insensitive
|
||||
|
||||
Everyone knows that Linux is case sensitive. To make the pattern match with case insensitive, use the I flag.
|
||||
|
||||
```
|
||||
# sed 's/unix/linux/gI' sed-test.txt
|
||||
|
||||
1 linux linux linux 23
|
||||
2 linux Linux 34
|
||||
3 linuxlinux linuxLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 10) How to Find and Replace a String that Contains the Delimiter Character
|
||||
|
||||
When you search and replace for a string with the delimiter character, we need to use the backslash “\” to escape the slash.
|
||||
|
||||
In this example, we are going to replaces the “/bin/bash” with “/usr/bin/fish”.
|
||||
|
||||
```
|
||||
# sed 's/\/bin\/bash/\/usr\/bin\/fish/g' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /usr/bin/fish CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
The above sed command works as expected, but it looks bad. To simplify this, most of the people will use the vertical bar “|”. So, I advise you to go with it.
|
||||
|
||||
```
|
||||
# sed 's|/bin/bash|/usr/bin/fish/|g' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /usr/bin/fish/ CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 11) How to Find and Replaces Digits with a Given Pattern
|
||||
|
||||
Similarly, digits can be replaced with pattern. The below sed command replaces all digits with “[0-9]” “number” pattern.
|
||||
|
||||
```
|
||||
# sed 's/[0-9]/number/g' sed-test.txt
|
||||
|
||||
number Unix unix unix numbernumber
|
||||
number linux Linux numbernumber
|
||||
number linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 12) How to Find and Replace only two Digit Numbers with Pattern
|
||||
|
||||
If you want to replace the two digit numbers with the pattern, use the sed command below.
|
||||
|
||||
```
|
||||
# sed 's/\b[0-9]\{2\}\b/number/g' sed-test.txt
|
||||
|
||||
1 Unix unix unix number
|
||||
2 linux Linux number
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 13) How to Print only Replaced Lines with the sed Command
|
||||
|
||||
If you want to display only the changed lines, use the below sed command.
|
||||
|
||||
* p – It prints the replaced line twice on the terminal.
|
||||
* n – It suppresses the duplicate rows generated by the “p” flag.
|
||||
|
||||
|
||||
|
||||
```
|
||||
# sed -n 's/Unix/Linux/p' sed-test.txt
|
||||
|
||||
1 Linux unix unix 23
|
||||
3 linuxunix LinuxLinux
|
||||
```
|
||||
|
||||
### 14) How to Run Multiple sed Commands at Once
|
||||
|
||||
The following sed command detect and replaces two different patterns simultaneously.
|
||||
|
||||
The below sed command searches for “linuxunix” and “CentOS” pattern, replacing them with “LINUXUNIX” and “RHEL8” at a time.
|
||||
|
||||
```
|
||||
# sed -e 's/linuxunix/LINUXUNIX/g' -e 's/CentOS/RHEL8/g' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 LINUXUNIX UnixLinux
|
||||
linux /bin/bash RHEL8 Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
The following sed command search for two different patterns and replaces them with one string at a time.
|
||||
|
||||
The below sed command searches for “linuxunix” and “CentOS” pattern, replacing them with “Fedora30” at a time.
|
||||
|
||||
```
|
||||
# sed -e 's/\(linuxunix\|CentOS\)/Fedora30/g' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 Fedora30 UnixLinux
|
||||
linux /bin/bash Fedora30 Linux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 15) How to Find and Replace the Entire Line if the Given Pattern Matches
|
||||
|
||||
If the pattern matches, you can use the sed command to replace the entire line with the new line. This can be done using the “C” flag.
|
||||
|
||||
```
|
||||
# sed '/OS/ c New Line' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
New Line
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
|
||||
### 16) How to Search and Replace lines that Matches a Pattern
|
||||
|
||||
You can specify a pattern for the sed command to fit on a line. In the event of pattern matching, the sed command searches for the string to be replaced.
|
||||
|
||||
The below sed command first looks for lines that have the “OS” pattern, then replaces the word “Linux” with “ArchLinux”.
|
||||
|
||||
```
|
||||
# sed '/OS/ s/Linux/ArchLinux/' sed-test.txt
|
||||
|
||||
1 Unix unix unix 23
|
||||
2 linux Linux 34
|
||||
3 linuxunix UnixLinux
|
||||
linux /bin/bash CentOS ArchLinux OS
|
||||
Linux is free and opensource operating system
|
||||
```
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/linux-sed-to-find-and-replace-string-in-files/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
402
translated/tech/20190812 Why const Doesn-t Make C Code Faster.md
Normal file
402
translated/tech/20190812 Why const Doesn-t Make C Code Faster.md
Normal file
@ -0,0 +1,402 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (LazyWolfLin)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Why const Doesn't Make C Code Faster)
|
||||
[#]: via: (https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html)
|
||||
[#]: author: (Simon Arneaud https://theartofmachinery.com)
|
||||
|
||||
为什么 `const` 无法让 `C` 代码跑得更快?
|
||||
======
|
||||
|
||||
在几个月前的一篇文章里,我曾说过“[有个一个流行的传言,`const` 有助于编译器优化 `C` 和 `C++` 代码][1]”。我觉得我需要解释一下,尤其是曾经我自己也以为这是显然对的。我将会用一些理论并构造一些例子来论证,然后在一个真正的代码库,`Sqlite`,上做一些实验和基准测试。
|
||||
|
||||
### 一个简单的测试
|
||||
|
||||
让我们从一个最简单、最明显的例子开始,以前认为这是一个 `const` 让 `C` 代码跑得更快的例子。首先,假设我们有如下两个函数声明:
|
||||
|
||||
```
|
||||
void func(int *x);
|
||||
void constFunc(const int *x);
|
||||
```
|
||||
|
||||
然后假设我们如下两份代码:
|
||||
|
||||
```
|
||||
void byArg(int *x)
|
||||
{
|
||||
printf("%d\n", *x);
|
||||
func(x);
|
||||
printf("%d\n", *x);
|
||||
}
|
||||
|
||||
void constByArg(const int *x)
|
||||
{
|
||||
printf("%d\n", *x);
|
||||
constFunc(x);
|
||||
printf("%d\n", *x);
|
||||
}
|
||||
```
|
||||
|
||||
调用 `printf()` 时,CPU 会通过指针从 RAM 中取得 `*x` 的值。很显然,`constByArg()` 会稍微快一点,因为编译器知道 `*x` 是常量,因此不需要在调用 `constFunc()` 之后再次获取它的值。它仅是打印相同的东西。没问题吧?让我们来看下 `GCC` 在如下编译选项下生成的汇编代码:
|
||||
|
||||
```
|
||||
$ gcc -S -Wall -O3 test.c
|
||||
$ view test.s
|
||||
```
|
||||
|
||||
以下是函数 `byArg()` 的完整汇编代码:
|
||||
|
||||
```
|
||||
byArg:
|
||||
.LFB23:
|
||||
.cfi_startproc
|
||||
pushq %rbx
|
||||
.cfi_def_cfa_offset 16
|
||||
.cfi_offset 3, -16
|
||||
movl (%rdi), %edx
|
||||
movq %rdi, %rbx
|
||||
leaq .LC0(%rip), %rsi
|
||||
movl $1, %edi
|
||||
xorl %eax, %eax
|
||||
call __printf_chk@PLT
|
||||
movq %rbx, %rdi
|
||||
call func@PLT # The only instruction that's different in constFoo
|
||||
movl (%rbx), %edx
|
||||
leaq .LC0(%rip), %rsi
|
||||
xorl %eax, %eax
|
||||
movl $1, %edi
|
||||
popq %rbx
|
||||
.cfi_def_cfa_offset 8
|
||||
jmp __printf_chk@PLT
|
||||
.cfi_endproc
|
||||
```
|
||||
|
||||
函数 `byArg()` 和函数 `constByArg()` 生成的汇编代码中唯一的不同之处是 `constByArg()` 有一句汇编代码 `call constFunc@PLT`,这正是源码中的调用。关键字 `const` 本身并没有造成任何字面上的不同。
|
||||
|
||||
好了,这是 `GCC` 的结果。或许我们需要一个更聪明的编译器。`Clang` 会有更好的表现吗?
|
||||
|
||||
```
|
||||
$ clang -S -Wall -O3 -emit-llvm test.c
|
||||
$ view test.ll
|
||||
```
|
||||
|
||||
这是 `IR` 代码(`LLVM` 的中间语言)。它比汇编代码更加紧凑,所以我可以把两个函数都导出来,让你可以看清楚我所说的“除了调用外,没有任何字面上的不同”是什么意思:
|
||||
|
||||
```
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @byArg(i32*) local_unnamed_addr #0 {
|
||||
%2 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
|
||||
tail call void @func(i32* %0) #4
|
||||
%4 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @constByArg(i32*) local_unnamed_addr #0 {
|
||||
%2 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
|
||||
tail call void @constFunc(i32* %0) #4
|
||||
%4 = load i32, i32* %0, align 4, !tbaa !2
|
||||
%5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
### 某些有作用的东西
|
||||
|
||||
接下来是一组 `const` 能够真正产生作用的代码:
|
||||
|
||||
```
|
||||
void localVar()
|
||||
{
|
||||
int x = 42;
|
||||
printf("%d\n", x);
|
||||
constFunc(&x);
|
||||
printf("%d\n", x);
|
||||
}
|
||||
|
||||
void constLocalVar()
|
||||
{
|
||||
const int x = 42; // const on the local variable
|
||||
printf("%d\n", x);
|
||||
constFunc(&x);
|
||||
printf("%d\n", x);
|
||||
}
|
||||
```
|
||||
|
||||
下面是 `localVar()` 的汇编代码,其中有两条指令在 `constLocalVar()` 中会被优化:
|
||||
|
||||
```
|
||||
localVar:
|
||||
.LFB25:
|
||||
.cfi_startproc
|
||||
subq $24, %rsp
|
||||
.cfi_def_cfa_offset 32
|
||||
movl $42, %edx
|
||||
movl $1, %edi
|
||||
movq %fs:40, %rax
|
||||
movq %rax, 8(%rsp)
|
||||
xorl %eax, %eax
|
||||
leaq .LC0(%rip), %rsi
|
||||
movl $42, 4(%rsp)
|
||||
call __printf_chk@PLT
|
||||
leaq 4(%rsp), %rdi
|
||||
call constFunc@PLT
|
||||
movl 4(%rsp), %edx # not in constLocalVar()
|
||||
xorl %eax, %eax
|
||||
movl $1, %edi
|
||||
leaq .LC0(%rip), %rsi # not in constLocalVar()
|
||||
call __printf_chk@PLT
|
||||
movq 8(%rsp), %rax
|
||||
xorq %fs:40, %rax
|
||||
jne .L9
|
||||
addq $24, %rsp
|
||||
.cfi_remember_state
|
||||
.cfi_def_cfa_offset 8
|
||||
ret
|
||||
.L9:
|
||||
.cfi_restore_state
|
||||
call __stack_chk_fail@PLT
|
||||
.cfi_endproc
|
||||
```
|
||||
|
||||
`LLVM` 生成的 `IR` 代码中更明显。在 `constLocalVar()` 中,第二次调用 `printf()` 之前的 `load` 会被优化掉:
|
||||
|
||||
```
|
||||
; Function Attrs: nounwind uwtable
|
||||
define dso_local void @localVar() local_unnamed_addr #0 {
|
||||
%1 = alloca i32, align 4
|
||||
%2 = bitcast i32* %1 to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %2) #4
|
||||
store i32 42, i32* %1, align 4, !tbaa !2
|
||||
%3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 42)
|
||||
call void @constFunc(i32* nonnull %1) #4
|
||||
%4 = load i32, i32* %1, align 4, !tbaa !2
|
||||
%5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %2) #4
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
好吧,现在,`constLocalVar()` 成功的优化了 `*x` 的重新读取,但是可能你已经注意到一些问题:`localVar()` 和 `constLocalVar()` 在函数体中做了同样的 `constFunc()` 调用。如果编译器能够推断出 `constFunc()` 没有修改 `constLocalVar()` 中的 `*x`,那为什么不能推断出完全一样的函数调用也没有修改 `localVar()` 中的 `*x`?
|
||||
|
||||
这个解释更贴近于为什么 `C` 语言的 `const` 不能作为优化手段的核心。`C` 语言的 `const` 有两个有效的含义:它可以表示这个变量是某个可能是常数也可能不是常数的数据的一个只读别名,或者它可以表示这变量真正的常量。如果你移除了一个指向常量的指针的 `const` 属性并写入数据,那结果将是一个未定义行为。另一方面,如果是一个指向非常量值的 `const` 指针,将就没问题。
|
||||
|
||||
这份 `constFunc()` 的可能实现揭示了这意味着什么:
|
||||
|
||||
```
|
||||
// x 是一个指向某个可能是常数也可能不是常数的数据的只读指针
|
||||
void constFunc(const int *x)
|
||||
{
|
||||
// local_var 是一个真正的常数
|
||||
const int local_var = 42;
|
||||
|
||||
// C 语言规定的未定义行为
|
||||
doubleIt((int*)&local_var);
|
||||
// 谁知道这是不是一个未定义行为呢?
|
||||
doubleIt((int*)x);
|
||||
}
|
||||
|
||||
void doubleIt(int *x)
|
||||
{
|
||||
*x *= 2;
|
||||
}
|
||||
```
|
||||
|
||||
`localVar()` 传递给 `constFunc()` 一个指向非 `const` 变量的 `const` 指针。因为这个变量并非常量,`constFunc()` 可以撒个谎并强行修改它而不触发而不触发未定义行为。所以,编译器不能断定变量在调用 `constFunc()` 后仍是同样的值。在 `constLocalVar()` 中的变量是真正的常量,因此,编译器可以断定它不会改变——因为在 `constFunc()` 去除变量的 `const` 属性并写入它*将*会是一个未定义行为。
|
||||
|
||||
第一个例子中的函数 `byArg()` 和 `constByArg()` 是没有可能优化的,因为编译器没有任何方法能够知道 `*x` 是否真的是 `const` 常量。
|
||||
|
||||
但是为什么不一致呢?如果编译器能够推断出 `constLocalVar()` 中调用的 `constFunc()` 不会修改它的参数,那么肯定也能继续在其他 `constFunc()` 的调用上实施相同的优化,是吗?并不。编译器不能假设 `constLocalVar()` 根本没有运行。 如果不是这样(例如,它只是代码生成器或者宏的一些未使用的额外输出),`constFunc()` 就能偷偷地修改数据而不触发未定义行为。
|
||||
|
||||
你可能需要重复阅读上述说明和示例,但不要担心它听起来很荒谬,它确实是正确的。不幸的是,对 `const` 变量进行写入是最糟糕的未定义行为:大多数情况下,编译器不知道它是否将会是未定义行为。所以,大多数情况下,编译器看见 `const` 时必须假设它未来可能会被移除掉,这意味着编译器不能使用它进行优化。这在实践中是正确的,因为真实的 `C` 代码会在“深思熟虑”后移除 `const`。
|
||||
|
||||
简而言之,很多事情都可以阻止编译器使用 `const` 进行优化,包括使用指针从另一内存空间接受数据,或者在堆空间上分配数据。更糟糕的是,在大部分编译器能够使用 `const` 进行优化的情况,它都不是必须的。例如,任何像样的编译器都能推断出下面代码中的 `x` 是一个常量,甚至都不需要 `const`:
|
||||
|
||||
```
|
||||
int x = 42, y = 0;
|
||||
printf("%d %d\n", x, y);
|
||||
y += x;
|
||||
printf("%d %d\n", x, y);
|
||||
```
|
||||
|
||||
TL;DR,`const` 对优化而言几乎无用,因为:
|
||||
|
||||
1. 除了特殊情况,编译器需要忽略它,因为其他代码可能合法地移除它
|
||||
2. 在 #1 以外地大多数例外中,编译器无论如何都能推断出该变量是常量
|
||||
|
||||
### C++
|
||||
|
||||
如果你在使用 `C++` 那么有另外一个方法让 `const` 能够影响到代码的生成。你可以用 `const` 和非 `const` 的参数重载同一个函数,而非 `const` 版本的代码可能可以优化(由程序员优化而不是编译器)掉某些拷贝或者其他事情。
|
||||
|
||||
```
|
||||
void foo(int *p)
|
||||
{
|
||||
// 需要坐更多的数据拷贝
|
||||
}
|
||||
|
||||
void foo(const int *p)
|
||||
{
|
||||
// 不需要保护性的拷贝副本
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int x = 42;
|
||||
// const 影响被调用的是哪一个版本的重载函数
|
||||
foo(&x);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
一方面,我不认为这会在实际的 `C++` 代码中大量使用。另一方面,为了导致差异,程序员需要做出编译器无法做出的假设,因为它们不受语言保护。
|
||||
|
||||
### 用 `Sqlite3` 进行实验
|
||||
|
||||
有了足够的理论和例子。那么 `const` 在一个真正的代码库中有多大的影响呢?我将会在代码库 `Sqlite`(版本:3.30.0)上做一个测试,因为:
|
||||
|
||||
* 它真正地使用了 `const`
|
||||
* 它不是一个简单的代码库(超过 20 万行代码)
|
||||
* 作为一个代码库,它包括了字符串处理、数学计算、日期处理等一系列内容
|
||||
* 它能够在绑定 CPU 下进行负载测试
|
||||
|
||||
|
||||
|
||||
此外,作者和贡献者们已经进行了多年的性能优化工作,因此我能确定他们没有错过任何有显著效果的优化。
|
||||
|
||||
#### 配置
|
||||
|
||||
我做了两份[源码]][2]拷贝,并且正常编译其中一份。而对于另一份拷贝,我插入了这个预处理代码段,将 `const` 变成一个空操作:
|
||||
|
||||
```
|
||||
#define const
|
||||
```
|
||||
|
||||
(GNU) `sed` 可以将一些东西添加到每个文件的顶端,比如 `sed -i '1i#define const' *.c *.h`。
|
||||
|
||||
在编译期间使用脚本生成 `Sqlite` 代码稍微有点复杂。幸运的是当 `const` 代码和非 `const` 代码混合时,编译器会产生了大量的提醒,因此很容易发现它并调整脚本来包含我的反 `const` 代码段。
|
||||
|
||||
直接比较编译结果毫无意义,因为任意微小的改变就会影响整个内存布局,这可能会改变整个代码中的指针和函数调用。因此,我用每个指令的二进制大小和汇编代码作为识别码(`objdump -d libsqlite3.so.0.8.6`)。举个例子,这个函数:
|
||||
|
||||
```
|
||||
000000000005d570 <sqlite3_blob_read>:
|
||||
5d570: 4c 8d 05 59 a2 ff ff lea -0x5da7(%rip),%r8 # 577d0 <sqlite3BtreePayloadChecked>
|
||||
5d577: e9 04 fe ff ff jmpq 5d380 <blobReadWrite>
|
||||
5d57c: 0f 1f 40 00 nopl 0x0(%rax)
|
||||
```
|
||||
|
||||
将会变成这样:
|
||||
|
||||
```
|
||||
sqlite3_blob_read 7lea 5jmpq 4nopl
|
||||
```
|
||||
|
||||
在编译时,我保留了所有 `Sqlite` 的编译设置。
|
||||
|
||||
#### 分析编译结果
|
||||
|
||||
`const` 版本的 `libsqlite3.so` 的大小是 4,740,704 byte,大约比 4,736,712 byte 的非 `const` 版本大了 0.1% 。在全部 1374 个导出函数(不包括类似 PLT 里的底层辅助函数)中,一共有 13 个函数的识别码不一致。
|
||||
|
||||
其中的一些改变是由于插入的预处理代码。举个例子,这里有一个发生了更改的函数(已经删去一些 `Sqlite` 特有的定义):
|
||||
|
||||
```
|
||||
#define LARGEST_INT64 (0xffffffff|(((int64_t)0x7fffffff)<<32))
|
||||
#define SMALLEST_INT64 (((int64_t)-1) - LARGEST_INT64)
|
||||
|
||||
static int64_t doubleToInt64(double r){
|
||||
/*
|
||||
** Many compilers we encounter do not define constants for the
|
||||
** minimum and maximum 64-bit integers, or they define them
|
||||
** inconsistently. And many do not understand the "LL" notation.
|
||||
** So we define our own static constants here using nothing
|
||||
** larger than a 32-bit integer constant.
|
||||
*/
|
||||
static const int64_t maxInt = LARGEST_INT64;
|
||||
static const int64_t minInt = SMALLEST_INT64;
|
||||
|
||||
if( r<=(double)minInt ){
|
||||
return minInt;
|
||||
}else if( r>=(double)maxInt ){
|
||||
return maxInt;
|
||||
}else{
|
||||
return (int64_t)r;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
删去 `const` 使得这些常量变成了 `static` 变量。我不明白为什么会有不了解 `const` 的人让这些变量加上 `static`。同时删去 `static` 和 `const` 会让 GCC 再次认为它们是常量,而我们将得到同样的编译输出。由于类似这样的局部的 `static const` 变量,使得 13 个函数中有 3 个函数产生假的变化,但我一个都不打算修复它们。
|
||||
|
||||
`Sqlite` 使用了很多全局变量,而这正是大多数真正的 `const` 优化产生的地方。通常情况下,它们类似于将一个变量比较代替成一个常量比较,或者一个循环在部分展开的一步。([Radare toolkit][3] 可以很方便的找出这些优化措施。)一些变化则令人失望。`sqlite3ParseUri()` 有 487 指令,但 `const` 产生的唯一区别是进行了这个比较:
|
||||
|
||||
```
|
||||
test %al, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
cmp $0x23, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
```
|
||||
|
||||
并交换了它们的顺序:
|
||||
|
||||
```
|
||||
cmp $0x23, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
test %al, %al
|
||||
je <sqlite3ParseUri+0x717>
|
||||
```
|
||||
|
||||
#### 基准测试
|
||||
|
||||
`Sqlite` 自带了一个性能回归测试,因此我尝试每个版本的代码执行一百次,仍然使用默认的 `Sqlite` 编译设置。以秒为单位的测试结果如下:
|
||||
|
||||
| const | No const
|
||||
---|---|---
|
||||
Minimum | 10.658s | 10.803s
|
||||
Median | 11.571s | 11.519s
|
||||
Maximum | 11.832s | 11.658s
|
||||
Mean | 11.531s | 11.492s
|
||||
|
||||
就我个人看来,我没有发现足够的证据说明这个差异值得关注。我是说,我从整个程序中删去 `const`,所以如果它有明显的差别,那么我希望它是显而易见的。但也许你关心任何微小的差异,因为你正在做一些绝对性能非常重要的事。那让我们试一下统计分析。
|
||||
|
||||
我喜欢使用类似 Mann-Whitney U 检验这样的东西。它类似于更著名的 T 检验,但对你在机器上计时时产生的复杂随机变量(由于不可预测的上下文切换,页错误等)更加鲁棒。以下是结果:
|
||||
|
||||
|| const | No const|
|
||||
---|---|---
|
||||
N | 100 | 100
|
||||
Mean rank | 121.38 | 79.62
|
||||
|
||||
|||
|
||||
---|---
|
||||
Mann-Whitney U | 2912
|
||||
Z | -5.10
|
||||
2-sided p value | <10-6
|
||||
HL median difference | -0.056s
|
||||
95% confidence interval | -0.077s – -0.038s
|
||||
|
||||
U 检验已经发现统计意义上具有显著的性能差异。但是,令人惊讶的是,实际上是非 `const` 版本更快——大约 60ms,0.5%。似乎 `const` 启用的少量“优化”不值得额外代码的开销。这不像是 `const` 启用了任何类似于自动矢量化的重要的优化。当然,你的结果可能因为编译器配置、编译器版本或者代码库等等而有所不同,但是我觉得这已经说明了 `const` 是否能够有效地提高 `C` 的性能,我们现在已经看到答案了。
|
||||
|
||||
### 那么,`const` 有什么用呢?
|
||||
|
||||
尽管存在缺陷,`C/C++` 的 `const` 仍有助于类型安全。特别是,结合 `C++` 的移动语义和 `std::unique_pointer`,`const` 可以使指针所有权显式化。在超过十万行代码的 `C++` 旧代码库里,指针所有权模糊是一个大难题,我对此深有感触。
|
||||
|
||||
但是,我以前常常使用 `const` 来实现有意义的类型安全。我曾听说过基于性能上的原因,最好是尽可能多地使用 `const`。我曾听说过当性能很重要时,重构代码并添加更多的 `const` 非常重要,即使以降低代码可读性的方式。当时觉得这没问题,但后来我才知道这并不对。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html
|
||||
|
||||
作者:[Simon Arneaud][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[LazyWolfLin](https://github.com/LazyWolfLin)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://theartofmachinery.com
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://theartofmachinery.com/2019/04/05/d_as_c_replacement.html#const-and-immutable
|
||||
[2]: https://sqlite.org/src/doc/trunk/README.md
|
||||
[3]: https://rada.re/r/
|
@ -0,0 +1,263 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hello-wn)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: (heguangzhi)
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to Create and Use Swap File on Linux)
|
||||
[#]: via: (https://itsfoss.com/create-swap-file-linux/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
如何在 Linux 上创建和使用交换文件
|
||||
======
|
||||
|
||||
本教程讨论了 Linux 中交换文件的概念,为什么使用它以及它相对于传统交换分区的优势。您将学习如何创建交换文件和调整其大小。
|
||||
|
||||
### 什么是 Linux 的交换文件?
|
||||
|
||||
交换文件允许 Linux 将磁盘空间模拟为内存。当您的系统开始耗尽内存时,它会使用交换空间将内存的一些内容交换到磁盘空间上。这样释放了内存,为更重要的进程服务。当内存再次空闲时,它会从磁盘交换回数据。我建议[阅读这篇文章,了解更多关于交换在 Linux ][1]。
|
||||
|
||||
传统上,交换空间被用作磁盘上的一个独立分区。安装 Linux 时,只需创建一个单独的分区进行交换。但是这种趋势在最近几年发生了变化。
|
||||
|
||||
使用交换文件,您不再需要单独的分区。您在 root 下创建一个文件,并告诉您的系统将其用作交换空间就行了。
|
||||
|
||||
使用专用的交换分区,在许多情况下,调整交换空间的大小是一个噩梦,也是一项不可能完成的任务。但是有了交换文件,你可以随意调整它们的大小。
|
||||
|
||||
最新版本的 Ubuntu 和其他一些 Linux 发行版已经开始 [默认使用交换文件][2]。即使您没有创建交换分区,Ubuntu 也会自己创建一个 1GB 左右的交换文件。
|
||||
|
||||
让我们看看交换文件的更多信息。
|
||||
|
||||
![][3]
|
||||
|
||||
### 检查 Linux 的交换空间
|
||||
|
||||
在您开始添加交换空间之前,最好检查一下您的系统中是否已经有了交换空间。
|
||||
|
||||
你可以用[ free 命令在Linux][4]检查它。就我而言,我的[戴尔XPS][5]有 14GB 的交换容量。
|
||||
|
||||
```
|
||||
free -h
|
||||
total used free shared buff/cache available
|
||||
Mem: 7.5G 4.1G 267M 971M 3.1G 2.2G
|
||||
Swap: 14G 0B 14G
|
||||
```
|
||||
free 命令给出了交换空间的大小,但它并没有告诉你它是真正的交换分区还是交换文件。swapon 命令在这方面会更好。
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/dev/nvme0n1p4 partition 14.9G 0B -2
|
||||
```
|
||||
|
||||
如您所见,我有 14.9GB 的交换空间,它在一个单独的分区上。如果是交换文件,类型应该是文件而不是分区。
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/swapfile file 2G 0B -2
|
||||
```
|
||||
|
||||
如果您的系统上没有交换空间,它应该显示如下内容:
|
||||
|
||||
```
|
||||
free -h
|
||||
total used free shared buff/cache available
|
||||
Mem: 7.5G 4.1G 267M 971M 3.1G 2.2G
|
||||
Swap: 0B 0B 0B
|
||||
```
|
||||
|
||||
swapon 命令不会显示任何输出。
|
||||
|
||||
|
||||
### 在 Linux 上创建交换文件
|
||||
|
||||
如果您的系统没有交换空间,或者您认为交换空间不足,您可以在 Linux 上创建交换文件。您也可以创建多个交换文件。
|
||||
|
||||
[][6]
|
||||
|
||||
建议阅读 Ubuntu 14.04 的修复缺失系统设置[快速提示]
|
||||
|
||||
让我们看看如何在 Linux 上创建交换文件。我在本教程中使用 Ubuntu 18.04,但它也应该适用于其他 Linux 发行版本。
|
||||
|
||||
#### 步骤1:创建一个新的交换文件
|
||||
|
||||
首先,创建一个具有所需交换空间大小的文件。假设我想给我的系统增加 1GB 的交换空间。使用fallocate 命令创建大小为 1GB 的文件。
|
||||
|
||||
```
|
||||
sudo fallocate -l 1G /swapfile
|
||||
```
|
||||
|
||||
建议只允许 root 用户读写交换文件。当您尝试将此文件用于交换区域时,您甚至会看到类似“建议的不安全权限0644,0600”的警告。
|
||||
|
||||
```
|
||||
sudo chmod 600 /swapfile
|
||||
```
|
||||
|
||||
请注意,交换文件的名称可以是任意的。如果您需要多个交换空间,您可以给它任何合适的名称,如swap_file_1、swap_file_2等。它只是一个预定义大小的文件。
|
||||
|
||||
#### 步骤2:将新文件标记为交换空间
|
||||
|
||||
您需要告诉 Linux 系统该文件将被用作交换空间。你可以用 [mkswap][7] 工具做到这一点。
|
||||
|
||||
```
|
||||
sudo mkswap /swapfile
|
||||
```
|
||||
|
||||
您应该会看到这样的输出:
|
||||
|
||||
```
|
||||
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
|
||||
no label, UUID=7e1faacb-ea93-4c49-a53d-fb40f3ce016a
|
||||
```
|
||||
|
||||
#### 步骤3:启用交换文件
|
||||
|
||||
现在,您的系统知道文件交换文件可以用作交换空间。但是还没有完成。您需要启用交换文件,以便系统可以开始使用该文件作为交换。
|
||||
|
||||
```
|
||||
sudo swapon /swapfile
|
||||
```
|
||||
|
||||
现在,如果您检查交换空间,您应该会看到您的Linux系统识别并使用它作为交换区域:
|
||||
|
||||
```
|
||||
swapon --show
|
||||
NAME TYPE SIZE USED PRIO
|
||||
/swapfile file 1024M 0B -2
|
||||
```
|
||||
|
||||
#### 第四步:让改变持久化
|
||||
|
||||
迄今为止您所做的一切都是暂时的。重新启动系统,所有更改都将消失。
|
||||
|
||||
您可以通过将新创建的交换文件添加到 /etc/fstab 文件来使更改持久化。
|
||||
|
||||
对 /etc/fstab 文件进行任何更改之前,最好先进行备份。
|
||||
|
||||
```
|
||||
sudo cp /etc/fstab /etc/fstab.back
|
||||
```
|
||||
|
||||
如何将以下行添加到 /etc/fstab 文件的末尾:
|
||||
|
||||
```
|
||||
/swapfile none swap sw 0 0
|
||||
```
|
||||
|
||||
|
||||
您可以使用[命令行文本编辑器][8]手动执行,或者只使用以下命令:
|
||||
|
||||
```
|
||||
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
||||
现在一切都准备好了。即使在重新启动您的 Linux 系统后,您的交换文件也会被使用。
|
||||
|
||||
### 调整交换
|
||||
|
||||
交换参数决定了交换空间的使用频率。交换值的范围从0到100。较高的值意味着交换空间将被更频繁地使用。
|
||||
|
||||
Ubuntu 桌面的默认交 换度是 60,而服务器的默认交换度是 1。您可以使用以下命令检查swappiness:
|
||||
|
||||
```
|
||||
cat /proc/sys/vm/swappiness
|
||||
```
|
||||
|
||||
为什么服务器应该使用低交换率?因为交换比内存慢,为了获得更好的性能,应该尽可能多地使用内存。在服务器上,性能因素至关重要,因此交换性尽可能低。
|
||||
|
||||
[][9]
|
||||
|
||||
建议阅读如何在双引导区用另一个替换一个 Linux 发行版[保留主分区]
|
||||
|
||||
|
||||
您可以使用以下系统命令动态更改变:
|
||||
|
||||
```
|
||||
sudo sysctl vm.swappiness=25
|
||||
```
|
||||
|
||||
这种改变只是暂时的。如果要使其永久化,可以编辑 /etc/sysctl.conf 文件,并在文件末尾添加swappiness 值:
|
||||
|
||||
|
||||
```
|
||||
vm.swappiness=25
|
||||
```
|
||||
|
||||
### 在 Linux 上调整交换空间的大小
|
||||
|
||||
在 Linux 上有几种方法可以调整交换空间的大小。但是在您看到这一点之前,您应该了解一些关于它的事情。
|
||||
|
||||
当您要求系统停止将交换文件用于交换区域时,它会将所有数据(确切地说是页面)传输回内存。所以你应该有足够的空闲内存,然后再停止交换。
|
||||
|
||||
这就是为什么创建和启用另一个临时交换文件是一个好的做法原因。这样,当您交换原始交换区域时,您的系统将使用临时交换文件。现在您可以调整原始交换空间的大小。您可以手动删除临时交换文件或保持原样,下次启动时会自动删除。
|
||||
|
||||
如果您有足够的可用内存或者创建了临时交换空间,那就使您的原始交换文件下线。
|
||||
|
||||
```
|
||||
sudo swapoff /swapfile
|
||||
```
|
||||
|
||||
现在您可以使用 fallocate 命令来更改文件的大小。比方说,您将其大小更改为 2GB:
|
||||
|
||||
```
|
||||
sudo fallocate -l 2G /swapfile
|
||||
```
|
||||
|
||||
现在再次将文件标记为交换空间:
|
||||
|
||||
```
|
||||
sudo mkswap /swapfile
|
||||
```
|
||||
|
||||
并再次使交换文件上线:
|
||||
|
||||
```
|
||||
sudo swapon /swapfile
|
||||
```
|
||||
您也可以选择同时拥有多个交换文件。
|
||||
|
||||
### 删除 Linux 中的交换文件
|
||||
|
||||
您可能有不在 Linux 上使用交换文件的原因。如果您想删除它,该过程类似于您刚才看到的调整交换大小的过程。
|
||||
|
||||
首先,确保你有足够的空闲内存。现在使交换文件离线:
|
||||
|
||||
```
|
||||
sudo swapoff /swapfile
|
||||
```
|
||||
|
||||
下一步是从 /etc/fstab 文件中删除相应的条目。
|
||||
|
||||
最后,您可以删除文件来释放空间:
|
||||
|
||||
```
|
||||
sudo rm /swapfile
|
||||
```
|
||||
|
||||
**你交换吗?**
|
||||
|
||||
我想您现在已经很好地理解了 Linux 中的交换文件概念。现在,您可以根据需要轻松创建交换文件或调整它们的大小。
|
||||
|
||||
如果你对这个话题有什么要补充的或者有任何疑问,请在下面留下评论。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/create-swap-file-linux/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/heguangzhi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/swap-size/
|
||||
[2]: https://help.ubuntu.com/community/SwapFaq
|
||||
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/08/swap-file-linux.png?resize=800%2C450&ssl=1
|
||||
[4]: https://linuxhandbook.com/free-command/
|
||||
[5]: https://itsfoss.com/dell-xps-13-ubuntu-review/
|
||||
[6]: https://itsfoss.com/fix-missing-system-settings-ubuntu-1404-quick-tip/
|
||||
[7]: http://man7.org/linux/man-pages/man8/mkswap.8.html
|
||||
[8]: https://itsfoss.com/command-line-text-editors-linux/
|
||||
[9]: https://itsfoss.com/replace-linux-from-dual-boot/
|
Loading…
Reference in New Issue
Block a user