Skip to content

Commit 817c899

Browse files
committed
Save Sun Nov 13 12:20:09 AM CST 2022
1 parent 123af77 commit 817c899

6 files changed

+32
-29
lines changed

读书笔记/CMU 15-445:645 Database Systems (Fall 2021)/Lecture #11: Query Execution I.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ An access method is how the DBMS accesses the data stored in a table. In general
8282

8383
A sequential table scan is almost always the least efficient method by which a DBMS may execute a query. There are a number of optimizations available to help make sequential scans faster:
8484

85-
* **Prefetching**: Fetch the next few pages in advance so that the DBMS does not have to block on storage
86-
I/O when accessing each page.
85+
* **Prefetching**: Fetch the next few pages in advance so that the DBMS does not have to block on storage I/O when accessing each page.
8786
* **Buffer Pool Bypass**: The scan operator stores pages that it fetches from disk in its local memory instead of the buffer pool in order to avoid sequential flooding.
8887
* **Parallelization**: Execute the scan using multiple threads/processes in parallel.
8988
* **Zone Map**: ==Pre-compute aggregations== for each tuple attribute in a page. The DBMS can then decide whether it needs to access a page by checking its Zone Map first. The Zone Maps for each page are stored in separate pages and there are typically multiple entries in each Zone Map page. Thus, it is

读书笔记/CMU 15-445:645 Database Systems (Fall 2021)/Lecture #12: Query Execution II.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ To get around this, DBMSs use I/O parallelism to *split installation across mult
158158

159159
In multi-disk parallelism, the OS/hardware is configured to store the DBMS’s files across multiple storage devices. This can be done through storage appliances or RAID configuration. All of the storage setup is transparent to the DBMS so workers cannot operate on different devices because the DBMS is unaware of the underlying parallelism.
160160

161-
<img src="https://littleneko.oss-cn-beijing.aliyuncs.com/img/image-20220306225046510.png" alt="image-20220306225046510" style="zoom:25%;" /><img src="https://littleneko.oss-cn-beijing.aliyuncs.com/img/image-20220306225106274.png" alt="image-20220306225106274" style="zoom:25%;" />
161+
<img src="https://littleneko.oss-cn-beijing.aliyuncs.com/img/image-20220306225046510.png" alt="image-20220306225046510" style="zoom:25%;" />
162+
163+
<img src="https://littleneko.oss-cn-beijing.aliyuncs.com/img/image-20220306225106274.png" alt="image-20220306225106274" style="zoom:25%;" />
162164

163165
## Database Partitioning
164166

读书笔记/Effective Modern C++/Rvalue References, Move Semantics, and Perfect Forwarding Part.1.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void f(T&& param); // 不是右值引用
3636
template<typename T>
3737
void f(T&& param); // param 是一个通用引用
3838
```
39-
第二种情况是== auto 声明符(auto&&)==,它是从以上示例中拿出的:
39+
第二种情况是 ==auto 声明符(auto&&)==,它是从以上示例中拿出的:
4040
```cpp
4141
auto&& var2 = var1; // var2 是一个通用引用
4242
```
@@ -59,7 +59,7 @@ f(w); // 传递给函数 f 一个左值;param 的类
5959
f(std::move(w)); // 传递给 f 一个右值;param 的类型会是
6060
// Widget&&,即右值引用
6161
```
62-
对一个通用引用而言,类型推导是必要的,但是它还不够。==引用声明的形式必须正确,并且该形式是被限制的,它必须恰好为 ”T&&"==,因此 "std::vector<T>&&" 的声明并不是通用引用。
62+
对一个通用引用而言,类型推导是必要的,但是它还不够。==引用声明的形式必须正确,并且该形式是被限制的,它必须恰好为 ”T&&"==,因此 "std::vector\<T\>&&" 的声明并不是通用引用。
6363

6464
==即使一个简单的 const 修饰符的出现,也足以使一个引用失去成为通用引用的资格==:
6565

@@ -108,7 +108,7 @@ public:
108108

109109
---
110110

111-
类型为 `auto` 的变量可以是通用引用,更准确地说,类型声明为 `auto&&` 的变量是通用引用,因为会发生类型推导,并且它们具有正确形式(`T&&`)。`auto` 类型的通用引用不如函数模板形参中的通用引用常见,但是它们在C++11 中常常出现,而它们在 C++14 中出现得更多,因为 C++14 的 lambda 表达式可以声明 `auto&&` 类型的形参。举个例子,如果你想写一个 C++14 标准的 lambda 表达式,来记录任意函数调用的时间开销,你可以这样写:
111+
类型为 `auto` 的变量可以是通用引用,更准确地说,类型声明为 `auto&&` 的变量是通用引用,因为会发生类型推导,并且它们具有正确形式(`T&&`)。`auto` 类型的通用引用不如函数模板形参中的通用引用常见,但是它们在 C++11 中常常出现,而它们在 C++14 中出现得更多,因为 C++14 的 lambda 表达式可以声明 `auto&&` 类型的形参。举个例子,如果你想写一个 C++14 标准的 lambda 表达式,来记录任意函数调用的时间开销,你可以这样写:
112112
```cpp
113113
auto timeFuncInvocation =
114114
[](auto&& func, auto&&... params) // C++14
@@ -120,7 +120,7 @@ auto timeFuncInvocation =
120120
stop timer and record elapsed time;
121121
};
122122
```
123-
func 是一个通用引用,可以被绑定到任何可调用对象,无论左值还是右值。args 是 0 个或者多个通用引用(即它是个通用引用 parameter pack),它可以绑定到任意数目、任意类型的对象上。多亏了 auto 类型的通用引用,函数timeFuncInvocation 可以对近乎任意(pretty much any)函数进行计时(之所以不是全部是因为完美转发有时候并不会成功)。
123+
func 是一个通用引用,可以被绑定到任何可调用对象,无论左值还是右值。args 是 0 个或者多个通用引用(即它是个通用引用 parameter pack),它可以绑定到任意数目、任意类型的对象上。多亏了 auto 类型的通用引用,函数 timeFuncInvocation 可以对近乎任意(pretty much any)函数进行计时(之所以不是全部是因为完美转发有时候并不会成功)。
124124
125125
---
126126
@@ -146,10 +146,10 @@ move(T&& param)
146146
return static_cast<ReturnType>(param);
147147
}
148148
```
149-
`std::move` 接受一个对象的引用(准确的说,一个通用引用(_universal reference_)),返回一个指向同对象的引用。
149+
`std::move` 接受一个对象的引用(准确的说,一个通用引用 (_universal reference_)),返回一个指向同对象的引用。
150150

151151

152-
该函数返回类型的 `&&` 部分表明 `std::move` 函数返回的是一个右值引用,但是,==如果类型 `T` 恰好是一个左值引用,那么 `T&&` 将会成为一个左值引用。为了避免如此,type trait `std::remove_reference` 应用到了类型 `T`上,因此确保了 `&&` 被正确的应用到了一个不是引用的类型上,这保证了 `std::move` 返回的真的是右值引用==。因此,`std::move` 将它的实参转换为一个右值,这就是它的全部作用=
152+
该函数返回类型的 `&&` 部分表明 `std::move` 函数返回的是一个右值引用,但是,==如果类型 `T` 恰好是一个左值引用,那么 `T&&` 将会成为一个左值引用。为了避免如此,type trait `std::remove_reference` 应用到了类型 `T`上,因此确保了 `&&` 被正确的应用到了一个不是引用的类型上,这保证了 `std::move` 返回的真的是右值引用==。因此,`std::move` 将它的实参转换为一个右值,这就是它的全部作用。
153153

154154

155155
另外,`std::move` 在 C++14 中可以被更简单地实现。多亏了函数返回值类型推导和标准库的模板别名 `std::remove_reference_t``std::move` 可以这样写:
@@ -167,7 +167,7 @@ decltype(auto) move(T&& param) // C++14,仍然在 std 命名空间
167167
当然,右值是移动操作的候选者,把 `std::move` 应用到一个对象上就是告诉编译器这个对象可以被移动,所以这就是为什么 `std::move` 叫现在的名字:更容易指定可以被移动的对象。
168168
169169
170-
事实上,右值只不过 _通常 _是移动操作的候选者。假设你有一个类,它用来表示一段注解,这个类的构造函数接受一个包含有注解的 `std::string` 作为形参,然后它复制该形参到数据成员,你声明一个值传递的形参:
170+
事实上,右值只不过 _通常_ 是移动操作的候选者。假设你有一个类,它用来表示一段注解,这个类的构造函数接受一个包含有注解的 `std::string` 作为形参,然后它复制该形参到数据成员,你声明一个值传递的形参:
171171
```cpp
172172
class Annotation {
173173
public:
@@ -181,10 +181,10 @@ private:
181181
std::string value;
182182
};
183183
```
184-
这段代码可以编译,可以链接,可以运行。这段代码将数据成员 value 设置为 text 的值。这段代码与你期望中的完美实现的唯一区别是 text 并不是被移动到 value,而是被拷贝。诚然,text 通过 `std::move` 被转换到右值,但是text 被声明为 `const std::string`,所以在转换之前,text 是一个==左值的 `const std::string`==,而转换的结果是一个==右值的 `const std::string`== ,但是纵观全程,==const 属性一直保留==。
184+
这段代码可以编译,可以链接,可以运行。这段代码将数据成员 value 设置为 text 的值。这段代码与你期望中的完美实现的唯一区别是 text 并不是被移动到 value,而是被拷贝。诚然,text 通过 `std::move` 被转换到右值,但是 text 被声明为 `const std::string`,所以在转换之前,text 是一个==左值的 `const std::string`==,而转换的结果是一个==右值的 `const std::string`== ,但是纵观全程,==const 属性一直保留==。
185185

186186

187-
当编译器决定哪一个std::string的构造函数被调用时,考虑它的作用,将会有两种可能性:
187+
当编译器决定哪一个 `std::string` 的构造函数被调用时,考虑它的作用,将会有两种可能性:
188188
```cpp
189189
class string { // std::string 事实上是
190190
public: // std::basic_string<char> 的类型别名
@@ -204,6 +204,7 @@ public: // std::basic_string<char> 的类型别名
204204
205205
# std::forward
206206
`std::forward` 与 `std::move` 是相似的,但是与 `std::move` 总是无条件的将它的实参转为右值不同,`std::forward` 只有在满足一定条件的情况下才执行转换。`std::forward` 是有条件的转换,要明白什么时候它执行转换,什么时候不,想想 `std::forward` 的典型用法,最常见的情景是一个模板函数,接收一个通用引用形参,并将它传递给另外的函数:
207+
207208
```cpp
208209
void process(const Widget& lvalArg); // 处理左值
209210
void process(Widget&& rvalArg); // 处理右值
@@ -313,7 +314,7 @@ w.setName("Adela Novak");
313314

314315
---
315316

316-
但是,关于对左值和右值的重载函数最重要的问题不是源代码的数量,也不是代码的运行时性能。而是设计的可扩展性差。`Widget::setName` 有一个形参,因此需要两种重载实现,但是对于有更多形参的函数,每个都可能是左值或右值,重载函数的数量几何式增长:n 个参数的话,就要实现 2n 种重载。这还不是最坏的。有的函数——实际上是函数模板——接受无限制个数的参数,每个参数都可以是左值或者右值。此类函数的典型代表是`std::make_shared`,还有对于 C++14 的 `std::make_unique`。查看他们的的重载声明:
317+
但是,关于对左值和右值的重载函数最重要的问题不是源代码的数量,也不是代码的运行时性能。而是设计的可扩展性差。`Widget::setName` 有一个形参,因此需要两种重载实现,但是对于有更多形参的函数,每个都可能是左值或右值,重载函数的数量几何式增长:n 个参数的话,就要实现 2n 种重载。这还不是最坏的。有的函数——实际上是函数模板——接受无限制个数的参数,每个参数都可以是左值或者右值。此类函数的典型代表是 `std::make_shared`,还有对于 C++14 的 `std::make_unique`。查看他们的的重载声明:
317318
```cpp
318319
template<class T, class... Args> //来自C++11标准
319320
shared_ptr<T> make_shared(Args&&... args);

读书笔记/Effective Modern C++/Rvalue References, Move Semantics, and Perfect Forwarding Part.2.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ void logAndAdd(const std::string& name)
1414
```cpp
1515
std::string petName("Darla");
1616
logAndAdd(petName); // 传递左值 std::string
17-
logAndAdd(std::string("Persephone")); // 传递右值 std::string
17+
logAndAdd(std::string("Persephone")); // 传递右值 std::string
1818
logAndAdd("Patty Dog"); // 传递字符串字面值
1919
```
2020

@@ -34,7 +34,7 @@ void logAndAdd(T&& name)
3434

3535
std::string petName("Darla"); // 跟之前一样
3636
logAndAdd(petName); // 跟之前一样,拷贝右值到 multiset
37-
logAndAdd(std::string("Persephone")); // 移动右值而不是拷贝它
37+
logAndAdd(std::string("Persephone")); // 移动右值而不是拷贝它
3838
logAndAdd("Patty Dog"); // 在 multiset 直接创建 std::string
3939
// 而不是拷贝一个临时 std::string
4040
```
@@ -259,7 +259,7 @@ void logAndAdd(T&& name)
259259
处理完之后,我们可以将注意力转移到名为 `logAndAddImpl` 的函数上了。有两个重载函数,第一个仅用于非整型类型(即 `std::is_instegral<typename std::remove_reference<T>::type>` 是 `false`):
260260
```cpp
261261
template<typename T> // 非整型实参:添加到全局数据结构中
262-
void logAndAddImpl(T&& name, std::false_type) // 译者注:高亮 std::false_type
262+
void logAndAddImpl(T&& name, std::false_type) // 译者注:高亮 std::false_type
263263
{
264264
auto now = std::chrono::system_clock::now();
265265
log(now, "logAndAdd");
@@ -369,7 +369,7 @@ public:
369369
370370
};
371371
```
372-
当我们拷贝或者移动一个 `SpecialPerson` 对象时,我们希望调用基类对应的拷贝和移动构造函数,来拷贝或者移动基类部分,但是这里,我们将 `SpecialPerson` 传递给基类的构造函数,因为 `SpecialPerson``Person` 类型不同(在应用 `std::decay` 后也不同),所以完美转发构造函数是启用的,会实例化为精确匹配`SpecialPerson` 实参的构造函数。相比于派生类到基类的转化——这个转化对于在 `Person` 拷贝和移动构造函数中把 `SpecialPerson` 对象绑定到 `Person` 形参非常重要,生成的精确匹配是更优的,所以这里的代码,拷贝或者移动 `SpecialPerson` 对象就会调用 `Person` 类的完美转发构造函数来执行基类的部分。
372+
当我们拷贝或者移动一个 `SpecialPerson` 对象时,我们希望调用基类对应的拷贝和移动构造函数,来拷贝或者移动基类部分,但是这里,我们将 `SpecialPerson` 传递给基类的构造函数,因为 `SpecialPerson``Person` 类型不同(在应用 `std::decay` 后也不同),所以完美转发构造函数是启用的,会实例化为精确匹配 `SpecialPerson` 实参的构造函数。相比于派生类到基类的转化——这个转化对于在 `Person` 拷贝和移动构造函数中把 `SpecialPerson` 对象绑定到 `Person` 形参非常重要,生成的精确匹配是更优的,所以这里的代码,拷贝或者移动 `SpecialPerson` 对象就会调用 `Person` 类的完美转发构造函数来执行基类的部分。
373373

374374

375375
派生类仅仅是按照常规的规则生成了自己的移动和拷贝构造函数,所以这个问题的解决还要落实在基类,尤其是控制是否使用 `Person` 通用引用构造函数启用的条件。现在我们意识到不只是禁止 `Person` 类型启用模板构造函数,而是禁止 `Person` 以及任何派生自 `Person` 的类型启用模板构造函数。讨厌的继承!

0 commit comments

Comments
 (0)