Category: 读书记录

  • git merge,rebase,reset

    老分不清怎么用git merge 与 rebase 区别.
    撤回的时候使用git reset 又忘了具体参数.

    git log

    显示提交的详细信息

    commit 2ec3438b5c53932e928ec4284c5121acb86f2abb (HEAD -> mdfs-ng, origin/mdfs-ng)
    Author: #小辉辉 <xxxx.com>
    Date:   Tue Apr 17 17:30:22 2018 +0800
    
        clear : del unused code
    

    git log –pretty=oneline

    只显示commit id和提交的描述信息

    2ec3438b5c53932e928ec4284c5121acb86f2abb (HEAD -> mdfs-ng, origin/mdfs-ng) clear : del unused code
    8a78c5779328d2e48826b9203a50d596d508392f update : clear code by sonar
    

    git reflog

    可以查看所有分支的所有操作记录.包括已经删除commit记录

    2a84922 HEAD@{158}: checkout: moving from hui-comment to devel
    0d0b108 (master, hui-comment) HEAD@{159}: checkout: moving from master to hui-comment
    

    HEAD

    HEAD 意义
    HEAD 提交的最新版本
    HEAD^ 上一个版本
    HEAD^^ 上上个版本
    HEAD~100 往上100个版本
    HEAD@{index} 提交索引

    git reset

    git reset --hard HEAD@{2}
    

    git commit –amend

    修改已经提交的信息

    git rebase

    合并

    1. 合并1~3条提交
      git rebase -i HEAD~3
      
      pick dc44d6b 根据sonar修改FixedLenChunkFingerIndexDB
      pick 8a78c57 update : clear code by sonar
      pick 2ec3438 clear : del unused code
      
      #将pick改为squash或者s,之后保存并关闭文本编辑窗口即可
      pick dc44d6b 根据sonar修改FixedLenChunkFingerIndexDB
      s 8a78c57 update : clear code by sonar
      s 2ec3438 clear : del unused code
      
    2. 指名要合并的版本之前的版本号
      git rebase -i 0d0b108
      
    3. 遇到冲突需要使用
      git add .  
      git rebase --continue  
      
    4. 取消
      git rebase --abort
      

    Merging vs. Rebasing

    The first thing to understand about git rebase is that it solves the same problem as git merge.

    The Merge Option

    git checkout feature
    git merge master
    #Or, you can condense this to a one-liner:
    git merge master feature
    

    The Rebase Option

    git checkout feature
    git rebase master
    

    参考

    (Git)合并多个commit

    7. 用rebase合并
    Merging vs. Rebasing

  • float_and_double

    浮点数是指用符号,尾数,基数和指数这四部分来表示的小数。因为计算机内部使用的是二进制数,所以基数自然是2。

    来源

    CPU中的二进制数据(小数篇)

    计算机浮点数规格化表示

    单精度与双精度是什么意思,有什么区别?

    浮点数

    • 双进度 64 位,8byte

    • 单精度 32 位,4byte

    浮点数构成`

    浮点数构成_双精度

    浮点数构成_单精度

    1. IEEE 754標準規定:非正規形式的浮點數的指數偏移值比正規形式的浮點數的指數偏移值小1。例如,最小的正規形式的單精度浮點數的指數部分編碼值為1,指數的實際值為-126;而非規約的單精度浮點數的指數域編碼值為0,對應的指數實際值也是-126而不是-127。實際上非正規形式的浮點數仍然是有效可以使用的,只是它們的絕對值已經小於所有的規約浮點數的絕對值;即所有的非規約浮點數比規約浮點數更接近0。規約浮點數的尾數大於等於1且小於2,而非規約浮點數的尾數小於1且大於0

    2. 小数部分是原十进制数值变为二进制后再经过规格化,规格化后省去唯一的整数1之后剩下的0.0100000 00000000 00000000写进去的。

    3. 指数部分是由指数实际值-3再加上固定值(这里取IEEE754标准的固定值2**(e-1)-1,e为储存指数的比特长度,在这里是8 bit)(单精度时指数偏移度为127,目的是为了同时可以表示正和负的指数,即00000000-11111111分别对应-127 — 128)。

      # 十进制运算
      0.15625-(1.0/8)-0-(1.0/32) = 0
      # 因式分解,用二的倍数表示
      0.15625=(1.0/8)+0+(1.0/32)
      # 转为二进制
      0.15625=(1)*(0+0.00101)=(1)*(1+0.01)x2^(-3)
      # 二进制下小数位为0.01
      # 尾数为0100 0000 0000 0000 0000 000
      # 指数 -3
      (-3)-(-127)=134 => 0b0111_1100
      
      

    一个32位单精度浮点数-3.75表示的例子:

    #首先转化为2进制表示
    −3.75=−(2+1+1/2+1/4)=−1.111×2^1
    #整理符号位并进行规格化表示
    −1.111×21=(−1)(1)×(1+0.1110 0000 0000 0000 0000 000)×2^1
    # 尾数为1110 0000 0000 0000 0000 000
    # 指数为1-127=128=0b10000000
    

    精度

    浮点数的尾数是二进制的,转化之后,有一些数无法表示,这也是精度丢失的原因.

  • go 内存对齐

    文章来源

    内存对齐是指首地址对齐,而不是说每个变量大小对齐

    为减少内存对齐带来的 padding 浪费. 构建结构体时,先写大的成员

    操作系统并非一个字节一个字节访问内存,而是按2, 4, 8这样的字长来k访问。
    因此,当 CPU 从存储器读数据到寄存器,或者从寄存器写数据到存储器,IO 的数据长度通常是字长。 如:
    * 32 位系统访问粒度是 4 字节(bytes),
    * 64 位系统的是 8 字节。

    当被访问的数据长度为 n 字节且该数据地址为n字节对齐,那么操作系统就可以高效地一次定位到数据,无需多次读取、处理对齐运算等额外操作。
    数据结构应该尽可能地在自然边界上对齐。如果访问未对齐的内存,CPU 需要做两次内存访问。

    没有内存对齐,cpu读取两次,然后在寄存器中合并.

    平台对齐系数

    计算机在加载和保存数据时,如果内存地址合理地对齐的将会更有效率。
    * 2字节大小的int16类型的变量地址应该是偶数,
    * 一个4字节大小的rune类型变量的地址应该是4的倍数,
    * 一个8字节大小的float64、uint64或64-bit指针类型变量的地址应该是8字节对齐的。
    * 但是对于再大的地址对齐倍数则是不需要的,即使是complex128等较大的数据类型最多也只是8字节对齐。

    unsafe.Sizeof

    unsafe.Sizeof函数返回操作数在内存中的字节大小
    * 参数可以是任意类型的表达式
    * 并且它并不会对表达式进行求值。

    unsafe.Alignof

    unsafe.Alignof 函数返回对应参数的类型需要对齐的倍数.
    和 Sizeof 类似, Alignof 也是返回一个常量表达式, 对应一个常量.
    通常情况下布尔和数字类型需要对齐到它们本身的大小(最多8个字节), 其它的类型对齐到机器字大小.

    unsafe.Offsetof

    unsafe.Offsetof 函数的参数必须是一个字段 x.f, 然后返回 f 字段相对于 x 起始地址的偏移量, 包括可能的空洞

    go string 类型 大小 16.

    • go语言的string是一种数据类型,这个数据类型占用16字节空间,
    • 前8字节是一个指针,指向字符串值的地址,后八个字节是一个整数,标识字符串的长度;
    • 注意go语言的字符串内部并不以’\0’作为结尾,而是通过一个长度域来表示字符串的长度。

    go 数组类型, 测试过 []int8 和 []int 都是 24

    • 在Go语言中数组是一个值类型value type
    • 是真真实实的数组,而不是一个指向数组内存起始位置的指针,也不能和同类型的指针进行转化,这一点严重不同于C语言
    • 所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作
      (这让我想起, channel 传递对象的时候,也是值拷贝. 需要避免拷贝巨大对象)
    • 如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所传入数组的一个副本。

    demo

    package main
    
    import (
        "fmt"
        "reflect"
        "unsafe"
    )
    
    type empty_struct struct {
    }
    
    type empty_struct_with_name1 struct {
        flag    bool
        name    string
        empty_s empty_struct
    }
    
    type empty_struct_with_name2 struct {
        a bool
        b empty_struct
        c string
        d empty_struct
    }
    
    func main() {
        s_val := "test"
        int_arr_val := []int{0, 1, 2, 3, 4, 5}
        var empty_s empty_struct
    
        fmt.Println("type:\t", reflect.TypeOf(s_val), "\tsize of:\t", unsafe.Sizeof(s_val), "\talign of:\t", unsafe.Alignof(s_val), "\tval:\t", s_val)
        fmt.Println("type:\t", reflect.TypeOf(int_arr_val), "\tsize of:\t", unsafe.Sizeof(int_arr_val), "\talign of:\t", unsafe.Alignof(int_arr_val), "\tval:\t", int_arr_val)
    
      //空 struct 正常size of 是 0
        fmt.Println("type:\t", reflect.TypeOf(empty_s), "\tsize of:\t", unsafe.Sizeof(empty_s), "\talign of:\t", unsafe.Alignof(empty_s), "\tval:\t", empty_s)
    
      // 空 struct 由于需要内存对齐,占了 8 字节空间
        var empty_s_name1 empty_struct_with_name1
        fmt.Println("type:\t", reflect.TypeOf(empty_s_name1), "\tsize of:\t", unsafe.Sizeof(empty_s_name1), "\talign of:\t", unsafe.Alignof(empty_s_name1), "\tval:\t", empty_s_name1)
    
      //第一个空 struct 由于需要内存对齐和 bool a 一起共同占了 8 字节空间
      //第二个空 struct 由于需要内存对齐和 独自占了 8 字节空间
        var empty_s_name2 empty_struct_with_name2
        fmt.Println("type:\t", reflect.TypeOf(empty_s_name2), "\tsize of:\t", unsafe.Sizeof(empty_s_name2), "\talign of:\t", unsafe.Alignof(empty_s_name2), "\tval:\t", empty_s_name2)
        fmt.Println("struce empty_s_name2 size of:\t", unsafe.Sizeof(empty_s_name2),
            "\ta offset of:\t", unsafe.Offsetof(empty_s_name2.a), "\tb offset of:\t", unsafe.Offsetof(empty_s_name2.b),
            "\tc offset of:\t", unsafe.Offsetof(empty_s_name2.c), "\td offset of:\t", unsafe.Offsetof(empty_s_name2.d))
    
        /*
            output :
            type:    string     size of:     16     align of:    8  val:     test
            type:    []int  size of:     24     align of:    8  val:     [0 1 2 3 4 5]
            type:    main.empty_struct  size of:     0  align of:    1  val:     {}
            type:    main.empty_struct_with_name1   size of:     32     align of:    8  val:     {false  {}}
            type:    main.empty_struct_with_name2   size of:     32     align of:    8  val:     {false {}  {}}
            struce empty_s_name2 size of:    32     a offset:    0  b offset of:     1  c offset of:     8  d offset of:     24
        */
    }
    
    
  • go loop,defer 和 闭包

    golang 中的陷阱. 这是一个有趣的例子,用到了 loop, defer 和闭包.

    例子 1,输出全是 5, 例子 2 和例子 3 会输出 5 4 3 2 1 0.

    // 1
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            defer func() {
                fmt.Println(i)
            }()
        }
        /*
            为什么全是 5,为什么不是 0, 1, 2, 3, 4, 5 这样的输出结果呢?
    
            其根本原因是闭包所导致的,有两点原因:
    
            1. 在 for 循环结束后,局部变量 i 的值已经是 5 了,并且 defer 的闭包是直接引用变量的 i。
            2. 结合defer 关键字的特性,可得知会在 main 方法主体结束后再执行。
            结合上述,最终输出的结果是已经自增完毕的 5。
    
        */
    }
    
    //2
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            y := i
            defer func() {
                fmt.Println(y)
            }()
        }
    }
    
    //3
    package main
    
    import "fmt"
    
    func main() {
        var whatever [6]struct{}
        for i := range whatever {
            defer func(i int) {
                fmt.Println(i)
            }(i)
        }
    }
    

    defer 执行顺序是先进后出的倒序执行.

    例子 2 中用局部变量声明的方式把的值拷克了.因此输出结果和例子 1 不同.

    例子 3 中,defer 执行的函数的参数是由声明时确定的.因此输出结果和例子 1 不同.

    来源: https://eddycjy.com/posts/go/go-tips-defer/

  • bloomfilter 库比较

    当前问题

    • 存储服务部署时会规划的内存,但是运行一段时候后,会发生oom
    • 很奇怪,存储oom时,dump的堆大小比规划的内存常常小几个GB
    • 存储服务运行一段时候后,容易发生full gc

    背景

    存储是全局重删的. 每一个节点负责自身数据的重删索引管理. 目前直接把重删索引存储在ssd的文件上,并且使用开放寻址法处理hash冲突问题.

    文件是否写满通过hash冲突次数和在这个文件已经保存的hash值总数. 一个索引文件保存的最大数量hash限制为:

    ((Integer.MAX_VALUE / ENTRY_SIZE) – 100 )的下个质数

    ((2**31-1)/37.0-100)的下个质数 为 58039999

    算法如何来的,我只能说,第一次见它就是这样算的.可能是考虑到java 对文件内存映射不能 Integer.MAX_VALUE

    因此一个keys文件长度最大为 hash_count_limit * ENTRY_SIZE 约等于 2GB左右.

    (more…)