c++ 复杂对象

说明

这是一个小实验。

定义一个复杂类

1
2
3
4
5
6
struct Complex {
int64_t a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,
a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1, q1, r1, s1, t1, u1, v1, w1, x1, y1, z1,
a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2, q2, r2, s2, t2, u2, v2, w2, x2, y2, z2,
a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3, q3, r3, s3, t3, u3, v3, w3, x3, y3, z3;
};

性能对比

作为实验,我们把负载类的对象插入一个 Map。在开始性能对比之前,定义计算时间的工具函数。

1
2
3
4
5
6
7
8
std::chrono::time_point<std::chrono::system_clock> now() {
return std::chrono::system_clock::now();
}

long elapsed(std::chrono::time_point<std::chrono::system_clock> &start) {
auto e = std::chrono::system_clock::now() - start;
return std::chrono::duration_cast<std::chrono::milliseconds>(e).count();
}

std::map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename K, typename V>
using Map = std::map<K, V>;

int main() {
typedef Complex MapV;
int loop = 500 * 10000;
MapV complex;
Map<int, MapV> m;
auto start = now();
for (int i = 0; i < loop; ++i) {
m[i] = complex;
}
std::cout << "elapsed: " << elapsed(start) << std::endl;
return 0;
}

输出

1
elapsed: 7750

std::unordered_map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename K, typename V>
using Map = std::unordered_map<K, V>;

int main() {
typedef Complex MapV;
int loop = 500 * 10000;
MapV complex;
Map<int, MapV> m;
auto start = now();
for (int i = 0; i < loop; ++i) {
m[i] = complex;
}
std::cout << "elapsed: " << elapsed(start) << std::endl;
return 0;
}

输出

1
elapsed: 4474

std::shared_ptr

如果我们改为使用共享指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename K, typename V>
using Map = std::unordered_map<K, V>;

int main() {
typedef std::shared_ptr<Complex> MapV;
int loop = 1000 * 10000;
MapV complex = std::make_shared<Complex>();
Map<int, MapV> m;
auto start = now();
for (int i = 0; i < loop; ++i) {
m[i] = complex;
}
std::cout << "elapsed: " << elapsed(start) << std::endl;
return 0;
}

输出

1
elapsed: 2002

简单对象

再来对比下指针和简单对象。

智能指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename K, typename V>
using Map = std::unordered_map<K, V>;

int main() {
typedef int64_t BaseType;
typedef std::shared_ptr<BaseType> MapV;
int loop = 500 * 10000;
MapV complex = std::make_shared<BaseType>();
Map<int, MapV> m;
auto start = now();
for (int i = 0; i < loop; ++i) {
m[i] = complex;
}
std::cout << "elapsed: " << elapsed(start) << std::endl;
return 0;
}

输出

1
elapsed: 2012

简单对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename K, typename V>
using Map = std::unordered_map<K, V>;

int main() {
typedef int64_t BaseType;
typedef BaseType MapV;
int loop = 500 * 10000;
MapV complex{};
Map<int, MapV> m;
auto start = now();
for (int i = 0; i < loop; ++i) {
m[i] = complex;
}
std::cout << "elapsed: " << elapsed(start) << std::endl;
return 0;
}

输出

1
elapsed: 1763

std::move

std::move 实际会调用 move constructor(A(A &&another)),所以,对于基础类型和没有实现 move constructor 的类不起效果。

定义 A。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A {
public:
A() {
std::cout << "construct A" << std::endl;
}

A(const A& a) {
std::cout << "copy construct A" << std::endl;
}

~A() {
std::cout << "destruct A" << std::endl;
}
};

看下插入 map。

1
2
3
4
5
6
7
8
9
10
11
std::map<int, A> ma;
{
A a;
ma.emplace(1, std::move(a));
}

// 输出
construct A
copy construct A
destruct A
destruct A

用如下方式。

1
2
3
4
5
6
7
8
std::map<int, A> ma;
{
ma[1];
}

// 输出
construct A
destruct A

实现移动构造。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A {
public:
A() {
std::cout << "construct A" << std::endl;
}

A(const A& a) {
std::cout << "copy construct A" << std::endl;
}

A(A &&a) {
std::cout << "move construct A" << std::endl;
}

~A() {
std::cout << "destruct A" << std::endl;
}
};

申请内存

先来看下空转。

1
2
3
4
5
6
7
8
int loop = 1000 * 10000;
auto start = now();
for (int i = 0; i < loop; ++i) {
}
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 24

再来看下申请内存。

1
2
3
4
5
6
7
8
9
int loop = 1000 * 10000;
auto start = now();
for (int i = 0; i < loop; ++i) {
Complex complex{};
}
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 144

再来看下插入 vector。

1
2
3
4
5
6
7
8
9
10
11
int loop = 1000 * 10000;
std::vector<Complex> complexes;
auto start = now();
for (int i = 0; i < loop; ++i) {
Complex complex{};
complexes.emplace_back(complex);
}
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 9663

再看下 reserve size。

1
2
3
4
5
6
7
8
9
10
11
12
int loop = 1000 * 10000;
std::vector<Complex> complexes;
complexes.reserve(loop);
auto start = now();
for (int i = 0; i < loop; ++i) {
Complex complex{};
complexes.emplace_back(complex);
}
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 2525

再看下通过构造函数初始化列表。

1
2
3
4
5
6
7
int loop = 1000 * 10000;
auto start = now();
std::vector<Complex> complexes(loop, Complex{});
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 2069

再来看下 resize。

1
2
3
4
5
6
7
8
int loop = 1000 * 10000;
std::vector<Complex> complexes;
auto start = now();
complexes.resize(loop);
std::cout << "elapsed: " << elapsed(start) << std::endl;

// output
elapsed: 1996

小结

在深度理解程序的过程中,很多操作的实际成本和我们想象中的不太一样,用一句话直白的说明就是,“这个操作竟然这么耗时?”,或者,“这个操作这么快?”。

操作

  • 计算
  • 内存申请
  • 容器
    • map,插入、查找

protobuf - go

Tips

optional 字段生成基本变量指针

对于 proto2 语法,如果基础字段添加了 optional 修饰,那么生成的 go 文件对应的字段是指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// proto 定义
syntax = "proto2";

message Foo {
optional int32 id = 1;
optional string bar = 2;
}

// go 文件
type Foo struct {
...

Id *int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
Bar *string `protobuf:"bytes,2,opt,name=bar" json:"bar,omitempty"`
}

go 语法不能对常量、右值取地址,无法直接赋值给对应变量。proto 提供了对应的 wrapper 工具,传入右值,返回指针。

1
2
foo := pb.Foo{}
foo.Id = proto.Int32(23)

s3

递归删除

1
2
3
4
aws s3 rm --recursive s3://bucket/ --exclude="*" --include="/folder_path/*" 

# 递归删除 s3://bucket/offline/data/2022
aws s3 rm --recursive s3://bucket/offline/data --include="2022"

查看空间占用

1
2
3
$ aws s3 ls s3://bucket/patth --recursive --summarize --human-readable | grep Total
Total Objects: 8382
Total Size: 984.4 GiB

brew - ubuntu

安装

1
2
3
4
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 配置
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/ubuntu/.profile

服务

consul

1
2
3
4
5
6
7
8
9
10
# 安装
brew install consul

# 配置
vim /home/linuxbrew/.linuxbrew/Cellar/consul/1.12.0/homebrew.consul.service
## 修改 ExecStart
ExecStart=/home/linuxbrew/.linuxbrew/opt/consul/bin/consul agent -dev -bind 0.0.0.0 -advertise 127.0.0.1 -client 0.0.0.0

# 手动启动
/home/linuxbrew/.linuxbrew/opt/consul/bin/consul agent -dev -bind 0.0.0.0 -advertise 127.0.0.1 -client 0.0.0.0

scala basic

[toc]

组织

文件

数据类型

类型

数字

字符串

布尔

限定

常量

变量

数据结构

数组

列表

集合

映射

语法

程序结构

注释

运算符

条件控制

循环

1
2
// 遍历次数
(0 until 10).map(...)

判断

函数

1
2
3
4
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
}

特性

语法糖

空指针处理

函数式编程

泛型编程

flatbuffer

定义

1
2
3
4
table Weapon {
name:string;
damage:short;
}

转换

1
2
3
4
flatbuffers::FlatBufferBuilder fbb;
...

const Weapon * weapon = GetWeapon(fbb.GetBufferPointer());

打印

1
2
3
4
5
6
7
// 打印结构
#include "flatbuffers/minireflect.h"

RequestT req;
flatbuffers::FlatBufferBuilder fbb;
fbb.Finish(Request::Pack(fbb, &req));
std::string debug_info = flatbuffers::FlatBufferToString(fbb.GetBufferPointer(), RequestTypeTable());

protobuf - c++

Descriptor & Reflection

message

1
2
3
4
5
6
7
8
9
10
syntax = "proto3";

message Sea {
string name = 1;
}

message World {
int64 age = 1;
repeated string tag = 2;
}

Descriptor & Reflection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
World wd{};

// protobuf 3
auto age = World::GetDescriptor()->FindFieldByName("age");
// protobuf 2
auto age = wd.GetDescriptor()->FindFieldByName("age");

// 设置单值
World::GetReflection->SetInt64(&wd, age, 10);
// 设置 repeated 字段
World::GetReflection()->AddString(&wd, tag, "a");
World::GetReflection()->AddString(&wd, tag, "b");
World::GetReflection()->SetRepeatedString(&wd, tag, 1, "c");

// 设置 submessage
wd.mutable_sea()->set_name("pacific");

Json

1
2
3
4
5
6
7
8
9
10
#include <google/protobuf/util/json_util.h>

// message -> json
std::string output;
google::protobuf::util::MessageToJsonString(message, &output);

// json -> message
SomeMessage msg;
std::string input;
google::protobuf::util::MessageToJsonString(input, &msg);

cmake

广告推荐

广告商可以在广告交易平台,选择适合需求的计费模型和指标来优化广告投放。

买量方式

不同的广告系统,会根据业务的不同选择不同的买量方式(出价方式),程序化广告交易平台支持的常用买量方式有 CPI、CPC、CPM、深度转化出价、Target-CPE、Target-ROAS 等。对于视频平台广告系统可能会有 CPV 等买量方式。

  • CPI,Cost Per Install,每次安装成本
  • CPC,Cost Per Click,每次点击成本
  • CPM,Cost Per Mille,每千次展示成本
  • 双出价 / 深度转化出价
  • Target-CPE,Target Cost Per Engagement,每次参与成本,按参与收费
  • Target-ROAS,Target Return on Advertising Spend,按回报率收费
  • CPA,Cost Per Acquisition,每次获客成本
  • CPV,Cost Per View,每次观看成本,主要用于视频广告

指标

  • eCPM,期望价值,用于评估当前请求的预估价值

    • 基于 eCPM 来控制利润率
  • 填充率,FillRate,有效请求量(有广告填充) / 总请求量

  • ROI

ROI ,投资回报率(Return on Investment),利润 / 花费

  • CTR,点击率(点击 / 展示)
  • CVR,点击转化率(转化 / 点击)
  • IVR,曝光转化率(转化 / 展示)

eCPM = ivr * price * 1000

  • LTV,Lifetime Value,生命周期总价值,指一个用户在其与企业之间的整个生命周期内产生的总价值或收益

术语

  • Offer,在广告行业,广告主投的广告为 Offer,开发者接受 Offer,广告交易平台的作用是撮合开发者拿到广告主的 Offer
  • Campaign,广告活动,一个 Offer 可以拆分多个 Campaign
  • Supply,供给方,媒体
  • Demand,需求方,广告主
  • Advertiser,广告主
  • PMP,Private Marketplace,优质媒体私有化购买
  • IAA,In-App Advertising,应用内广告,通过展示广告来获得收入
  • IAP,In-App Purchases,应用内购买,帮助开发者实现收入来源
  • EGR,Engagement Rate,参与率,广告或营销活动中用户互动的比率
  • SKAN,基于苹果官方归因解决方案 SKAdNetwork 的回传数据做出的一个帮助广告主衡量 iOS 端投放效果的榜单,属于确定性归因

服务

DMP

数据管理平台(Data Management Platform,DMP),用于存储设备用户的兴趣、安装列表、以及人物画像等数据。

其他

  • PCOC,Predict Click Over Click,校准之后的点击率与后验点击率(近似真实概率)的比值

参考

logrotate

配置

示例

rotate.conf

1
2
3
4
5
6
7
8
9
/home/wii/www/logs/*.log {
daily
rotate 30
missingok
nocompress
copytruncate
notifempty
dateext
}

Rotate

触发 rotate

1
2
3
4
5
6
logrotate [options] [config-file]

logrotate -v -s /home/wii/www/logs/rotate-status rotate.conf

-v: 打印具体信息
-s: 指定 status 保存路径; 默认在 /var/lib/logrotate/ 下,可能没有权限

注意

  • logrotate 根据 status 文件判断是否需要 rotate,第一次执行可能只创建 status 文件,并不会创建 rotate 日志文件。如果想要触发,修改 status 文件记录的最后一次 rotate 时间,以触发 rotate