std::vector<ModelObject> mos{mo1, mo2};
auto mos = std::vector<ModelObject>{mo1, mo2};
// Don't do this
std::vector<ModelObject> mos;
mos.push_back(mo1);
mos.push_back(mo2);
初始化列表能帮助减少对象拷贝和容器的大小的扩容。
// Instead of
auto mo1 = getSomeModelObject();
auto mo2 = getAnotherModelObject();
doSomething(mo1, mo2);
// consider:
doSomething(getSomeModelObject(), getAnotherModelObject());
这能减少编译器的move
操作。
// Bad Idea
std::string somevalue;
if (caseA) {
somevalue = "Value A";
} else {
somevalue = "Value B";
}
// Better Idea
const std::string somevalue = caseA ? "Value A" : "Value B";
// Bad Idea
std::string somevalue;
if (caseA) {
somevalue = "Value A";
} else if(caseB) {
somevalue = "Value B";
} else {
somevalue = "Value C";
}
// Better Idea
const std::string somevalue = [&](){
if (caseA) {
return "Value A";
} else if (caseB) {
return "Value B";
} else {
return "Value C";
}
}();
new
操作// require two heap allocation
std::shared_ptr<ModelObject_Impl>(new ModelObject_Impl());
// should become
std::make_shared<ModelObject_Impl>(); // (it's also more readable and concise)
unique_ptr
std::unique_ptr<ModelObject_Impl> factory();
auto shared = std::shared_ptr<ModelObject_Impl>(factory());
最佳实践是通过工厂函数返回unique_ptr
,在需要的时候再转化为shared_ptr
。
std::endl
包含flush
的操作。
// Good Idea
for (int i = 0; i < 15; ++i)
{
MyObject obj(i);
// do something with obj
}
// Bad Idea
MyObject obj; // meaningless object initialization
for (int i = 0; i < 15; ++i)
{
obj = MyObject(i); // unnecessary assignment operation
// do something with obj
}
// obj is still taking up memory for no reason
init-statement
if (MyObject obj(index); obj.good()) {
// do something if obj is good
} else {
// do something if obj is not good
}
// Bad Idea
std::cout << someThing() << "\n";
// Good Idea
std::cout << someThing() << '\n';
"\n"
会被解析为const char *
并为其做范围检查。单个\n
能避免多余的CPU指令执行。
std::bind
// Bad Idea
auto f = std::bind(&my_function, "hello", std::placeholders::_1);
f("world");
// Good Idea
auto f = [](const std::string &s) { return my_function("hello", s); };
f("world");
推荐使用lambda
,而不是std::bind
。
vector
在使用容器的时候多问问是否用一个vector
就够?
对于任何一个有虚函数的类添加虚拟析构函数,即时它什么都没做。
class Transaction {
public:
Transaction();
virtual void log_transaction() const = 0;
};
Transaction:: Transaction() {
...
// log the tranaction
log_transaction();
}
class BuyTransaction: public Transaction {
public:
virtual void log_tranction() const;
...
}
class SellTransaction: public Transaction {
public:
virtual void log_transaction() const;
}
BuyTransaction b;
https://doris.apache.org/zh-CN/docs/data-table/data-model/
Rollup must be based on Aggregate Table.
ALTER TABLE ads ADD ROLLUP `PublisherRollup` (`Date`, PublisherId, Clicks, `Cost`)
Doris MV can be created based on Duplicate Table.
CREATE MATERIALIZED VIEW `PublisherMView` AS
SELECT `Date`, PublisherId, SUM(Clicks), SUM(`Cost`) FROM ads GROUP BY `Date`, PublisherId
Glue
in LatinSIGMOD 2022
: A Fast Query Engine for Lakehouse Systems[1]volex
/clickhouse
/arrow
我们都提到的一点是一家人出去玩,印象最深的还是年初的那次去海南自驾游,那一周是一种比较轻松和自由的状态。
于我而言,过往一年比较重要的是更多的往内心看,从更多角度去思考自己内心的想法,更注重自己内心的感受。包括什么对自己是最重要的,什么是不需要花费太多精力去顾及和考虑的。
家里的方方面面我自己觉得都还不错,在2023年这样的大背景下也弥足珍贵了。
对于2024
也没有具体的计划和清单,更多地还是从内心出发去行事,也希望能有一些收获和成长。
这个是我在 DataFunConf2023-深圳站
上的分享整理的文字内容。
上一篇讲到了代码生成,这一篇我们会讲一下由代码生成引申出来的如下内容:
JIT(Just-in-time Compilation),即时编译
,也称为运行时编译
,是一种执行计算机程序的方法。程序是在执行过程中,而不是执行之前进行编译。
以Java
为例,下图的流程描述了Java
程序从源代码到机器字节码的流程以及JIT
在这个流程中的位置。
在数据库领域,JIT技术应用在表达式代码生成,查询代码生成等多个方面。延续上一篇我们讲到的表达式求值,我们来看JIT技术在表达式求值过程中的作用。
Apache Gandiva是一个运行时表达式编译器,利用LLVM生成用于在Arrow Record Batch上进行计算的高效本机代码。Gandiva只用来处理在投影(Projection)和过滤(Filtering)阶段的表达式。
Gandiva充分利用Arrow内存格式和现代硬件。基于Arrow内存模型,由于Arrow数组为值(Data)和有效位图(Validity)分别提供缓冲区,因此值及其空值状态通常可以独立处理,从而实现更好的指令流水线。在现代硬件上,使用LLVM编译表达式使执行得以优化,以适应本地运行时环境和硬件,包括可用的SIMD指令。为了减少优化开销,许多Gandiva函数被预先编译成LLVM IR(中间表示)。
因为采用Arrow内存格式,Gandiva对向量化和SIMD的支持也是天然的。
此外Gandiva在提供异步线程控制,多语言支持,已经性能提升上都有着不错的表现。
这个是我在 DataFunConf2023-深圳站
上的分享整理的文字内容。
代码生成(Code Generation)在数据库中有着广泛的应用。代码生成能将表达式,查询,计算过程等在程序执行的时候编译成机器码再执行,从而提高程序运行的效率。特别是随着硬件的发展,IO不再成为查询的瓶颈,对于计算密集型的查询,代码生成能极大提高查询性能。
我们以一个简单的查询来看一下数据库的处理流程:
SELECT
price * 0.8 + distance / 1000 AS credit
FROM
trips
WHERE
arrival_city = 'Shenzhen’ AND duration < 120
这里主要看Filter
阶段,也就是WHERE
后面的表达式处理流程。通常情况下该表达式会被翻译成一个抽象语法书(AST
)。
我们通过一段简单的Python
代码来看下解释执行的过程是处理的。
首先定义一个简单的类来表示AST
对应的节点:
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
对应上图中的AST
,我们得到一个表达式树:
expr_tree = Node(
'AND',
Node('=', Node('arrival_city'), Node('"Shenzhen"')),
Node('<', Node('duration'), Node('120'))
)
采用深度优先(DFS)的方式遍历表达式树,对表达式求值。这里只处理了上述表达式中存在的几种情况。
AND
的visit
函数,再分别调用左右子节点的visit
函数,返回AND
操作结果=
的visit
函数,比较字段arrival_ciy
和字符串常量Shenzhen
的值<
的visit
函数,比较字段duration
和整型常量120
的值def visit(node, record):
if node is None:
return None
if node.value == 'AND':
left_result = visit(node.left, record)
right_result = visit(node.right, record)
return left_result and right_result if left_result is not None and right_result is not None else None
elif node.value == '<':
left_value = get_value(node.left, record)
right_value = get_value(node.right, record)
return left_value < right_value if left_value is not None and right_value is not None else None
elif node.value == '=':
left_value = get_value(node.left, record)
right_value = get_value(node.right, record)
return left_value == right_value
# 处理其他操作符和操作数的情况
else:
return None
def get_value(node, record):
if node.value.isdigit():
return int(node.value)
elif node.value.startswith('"') and node.value.endswith('"'):
return node.value[1:-1]
elif node.value in record:
return record[node.value]
else:
return None
这种解释执行的方式简单易理解,但是存在一些问题:
代码生成就是生成需要执行的代码。这里我们还是使用Python
来模拟采用代码生成的方式来处理上面的表达式:
def generate_code(node):
if node is None:
return ""
if node.value in ('AND', 'OR'):
left_code = generate_code(node.left)
right_code = generate_code(node.right)
return f"({left_code}) {node.value.lower()} ({right_code})"
elif node.value in ('>', '<'):
return f"record['{node.left.value}'] {node.value} {node.right.value}"
elif node.value == '=':
return f"record['{node.left.value}'] == {node.right.value}"
else:
raise ValueError(f"Unsupported operation: {node.value}")
def jit_evaluator(expr_code, record):
try:
local_vars = {}
exec(f"result = {expr_code}", {'record': record}, local_vars)
return local_vars.get('result', None)
except Exception as e:
print(f"Error evaluating expression: {e}")
return False
# 生成代码并执行
expr_code = generate_code(expr_tree)
print(f"Generated expression code: {expr_code}")
for record in database:
result = jit_evaluator(expr_code, record)
if result:
print(f"Record {record} matches the filter.")
通常情况下我们会采用LLVM的中间语言(IR)作为生成的代码语言。整个表达式求值的执行过程就分为两步:
其中生成中间代码通常会先进行类型推导,然后再生成代码。进行类型推导的过程也是检查表达式是否合法的过程。
编译器对生成的代码编译,得到机器可以执行的机器码,这里面就包含我们需要执行的类和函数。在执行过程中调用具体的逻辑就可以得到表达式的值。
]]>它预置了一些设备参数可以选择:
❯ dmc list
0: Faulty 5400 HDD
1: 5400 HDD
2: 7200 HDD
3: Slow SSD
4: SATA II SSD
5: SATA III SSD
6: PCIe 2 SSD
7: PCIe 3 SSD
假设我们要限制文件夹~/data
下的读写速度,那么可以运行如下命令:
sudo dmc start ~/data/ "Slow SSD"
可以查看Slow SSD
对应的具体读写速度限制:
> dmc show "Slow SSD"
Profile: Slow SSD
Type: SSD
Access time: 100 us
Read throughput: 250 MB/s
Write throughput: 125 MB/s
I/O Queue Depth: 32
Max Read Bytes: 33554432
Max Write Bytes: 33554432
Max Read Segments: 64
Max Write Segments: 64
测试完了之后可以使用dmc stop
来清除之前的设置,使对应文件夹下的读写速度恢复正常。
年初刚过完年那会儿,桃桃外婆过来换奶奶,打算在这边待一个多月再回去。后续外公因为家里有事情提前回去了,然后就是上海封城,直到6月。这两天早上6点起来买叮咚买菜还抢不到运力,那段让人难受的封控日子又浮现在眼前。这样一折腾半年就过去了。
解封后我们把桃桃送回去,到开学的时候又接回上海。这期间趁着周末的时间把装修的事情定得差不多。也是因为要上幼儿园的原因,8月底不得不搬到闵行这边来。又担心装修甲醛之类的影响,继续在桃桃新的幼儿园旁边租了一个小房间过度了一个多月。这段时间过着候鸟一样的生活,晚上过去睡觉,白天我们去上班,桃桃上学。大概在国庆节的时候我们算正式搬入新家了。
这个学期我们给桃桃找了一个晚托班,尝试我们俩人自己带娃。上班通勤的时间变得更长了一点,家里还有一些需要慢慢处理的事情,加上给她报了几个兴趣班,当所有这样一堆事情都夹杂在一起的时候,我们俩每天就像是在和时间赛跑一样,没有多少个人的时间。这快到年底了,随着疫情防控的放开,还是把桃桃奶奶叫过来帮忙,不然幼儿园这放假了待在家里我们俩真吃不消。
工作上只是把该做的事情做好了,并没有太多亮点。年初自己定了几个工作之外的小目标大部分也没有实现,从这个方面来讲也是挺失败的。不过这一年有过一些不太深入的职业上的思考,有一些焦虑。这方面希望来年有更多的尝试和总结。
总的来说这过去的一年还是充实饱满的。我和我的队友相互配合,推着整个小家稳稳前行,也算是在动荡的大环境之下苟住了。队友总比我给力,她的韧性总能爆发出更大的力量。中年人没有那么多不切实际的幻想,把身体整好,照顾好家庭,有老婆热炕头,再踏实地做点事情,满足了。
]]>认识他是在去参加一个技术分享会,会后晚饭是他组局买单的,大家一起加了联系方式。他当时是想做在线办公的软件的互联互通,比如微软的office三件套和WPS的文档能够互操作,场景大概是一个厂商的用户能无缝和另外一个厂商的用户在线沟通和协作。姑且不说这个想法是否合理。他当时的情况是没有IT行业的技术背景,找了一个同样没有技术背景的人来做这个事情,所以可想而知在和其他人交流和沟通的时候别人是怎样一个反应。
后来找我去给他帮帮忙,看他为人的确不错,所以帮着他维护一些云服务器,帮着做一些他技术上不懂的事情,大概是以兼职的形式进行的。 他之所以有资本做这个事情也是因为之前卖医疗器材有一些积累, 所以还能拉扯个小团队。这个事情我记不太清楚坚持了多久,的确是没有什么产出,到处参加了不少研讨会和技术分享会,包括Google I/O大会的门票他也有能力搞到并且跑到那边去还能和那边的高层领导聊上几句。 这点我的确是佩服的。
后来发现这个好像的确做不通,他转去做食品安全相关的一些解决方案, 类似想做一个点评一样的平台去监督每个餐厅的食品安全。做了一些简单的尝试,包括人工地推。我参与了一些服务器维护和建站的简单工作,其实没有帮上太大的忙。
这之后我毕业了就没有继续做技术支持了, 他也邀请我去他那里和他一起做,待遇什么的和我找的最好的工作待遇齐平没问题之类的,我婉谢了。后来请他吃了一次饭,随便聊了一些。中间我爸因为车祸住院,他给了一个非常大的红包说是看我爸的,我谢绝了他还是执意要给我。他一直给我说的一个事情是,不要对这个有什么心理负担,你之后把这种善意拿去帮助别人就是对我的一个反馈。这句话对我影响挺大,之后我也是以这样的理念去践行的。
人生的经历因为这样的一些人和事变得丰富, 感谢他曾经的帮助和友善,祝福他能在大洋彼岸生活得幸福开心。
]]>