Home avatar

This is my blog

Potobuf 的编码

编码的作用

其实当前的数值类型 (int, short, char, …)可以理解为定长编码, 但是现实中, 我们 80% 的情况下, 只使用一些小的正整数, 还有些情况下, 使用小的负数。 这样一来我们就可以压缩信息。比如: 0000 0000 0001, 我们可以压缩为 11 个 0, 一个 1。因为信息(11 个 0, 1 个 1)是连在一起的 (还有多个信息也是连在一起的), 我们需要一种方案来区分开 11 个 0一个 1, 还有区分其他信息。可以理解为需要信息边界 (自定义的概念), 而 variant 的消息边界是"每个字节的最高位(第8位)用来表示是否有后续字节", 信息内的信息边界是, 全是 0 则不记录。信息之间的消息边界是, 最高位 (第 8 位) 为 0 时, 信息的右边界。

Test Blog

Chroma Style

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
	// hi
	fmt.Println("Hello, World!")
}

No type:

1
2
3
4
5
6
7
8
package main

import "fmt"

func main() {
	// hi
	fmt.Println("Hello, World!")
}

mathjax

行内数学公式:$a^2 + b^2 = c^2$。

QT时间转换接口的加锁问题

遇到问题的场景

  • 负责旧司的后台工具, 要转换大量的 K 线文件 (一共百 GB 级别)。用到多线程转换。但是在转换过程中, CPU 占用和磁盘速度没有拉满。
  • 因为工具是在 Windows 平台运行的, 所以使用了 进程资源管理器 查看进程的运行情况。发现内核时间占用比较多。所以猜测是加锁导致的。

解决问题的思路

  • 直接使用控制变量法是最快的。因为线程池的 worker 使用最频繁的代码块是转换 K 线的各个字段的 for 代码。
  • 注释该 for 的代码。然后运行, 发现 CPU 占用和磁盘直接拉满。
  • 基本确定是该块代码加锁了。
  • 这时发现时间转换使用了 QT 的 QDateTime::fromString。我不知内部实现, 所以注释日期转换代码。发现 CPU 占用和磁盘速度也是拉满的。
  • 所以基本确定是 QDateTime::fromString 代码加锁了。
  • 最后自己根据时间字符串的分隔符取出各个时间字段 (年、月、日、时、分、秒)。

查看 QDateTime::fromString 的实现

为了满足自己的好奇心去查看了 QT 的 QDateTime::fromString 的实现:

Enable_if 的用法

函数返回值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
foo(T t) {
    std::cout << "Integer: " << t << std::endl;
    return t;
}

template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
foo(T t) {
    std::cout << "Floating point: " << t << std::endl;
    return t;
}

int main() {
    foo(10);
    foo(3.14);
    return 0;
}

模板的默认类型实参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <iostream>
#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_integral<T>::value, bool>>
void foo(T t) {     // 只有需要一个版本的 foo 的情况下。因为模板的默认实参不同的两个模板是相同的东西。
                    // 如果想要两个版本以上, 则将 enable_if 做成模板类型, 请继续向下看。
  std::cout << "Integer: " << t << std::endl;
}

int main() {
  foo(10);
  return 0;
}

模板类型形参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <type_traits>

/* 如果需要两个以上版本的 foo 的情况下。*/

template <typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
void foo(T t) {     
  std::cout << "Integer: " << t << std::endl;
}

template <typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
void foo(T t) {     
  std::cout << "Float: " << t << std::endl;
}

int main() {
  foo(10);
  foo(10.1);
  return 0;
}

模板的特化形参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <type_traits>

template<typename T, typename U>
class Foo {};

template<typename T>
class Foo<T, std::enable_if_t<std::is_same_v<T, int>, T>> {
  public:
    Foo() {
      std::cout << "int" << std::endl;
    }
};

template<typename T>
class Foo<T, std::enable_if_t<std::is_same_v<T, float>, T>> {
  public:
    Foo() {
      std::cout << "float" << std::endl;
    }
};

template<typename T>
using MyFoo = Foo<T, T>;

int main() {
  MyFoo<int> foo1;
  MyFoo<float> foo2;
  return 0;
}

扩展 enable_if

IsIntegral:

Cpp 初始化风格

cpp 的初始化风格

兼容 C 的初始化风格:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int i = 10;
int a[] = {1, 2, 3};

struct Foo {
  char c;
  int i;
};

union Bar {
  char c;
  int i;
};

Foo foo = {.c = 'A', .i = 42};
Bar bar = {.c = 'A'};

但是 Cpp 引入了类, 这样会导致一个问题:

0%