<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【線上問題】P1級公司故障,年終獎不保

          共 2932字,需瀏覽 6分鐘

           ·

          2022-01-05 00:10


          ?

          對本文有疑問的,可以公眾號留言、私信,也可以加筆者微信直接交流(文末留有微信二維碼);另外還有批量免費計算機電子書,后臺回復[pdf]免費獲取

          ?

          大家好,我是雨樂!

          前段時間,某個同事找我傾訴,說是因為strict weak ordering導致程序coredump,給公司造成數(shù)百萬損失,最終評級故障為P0級,年終獎都有點不保了,聽完不禁一陣唏噓。

          之前的文章中,我們分析了std::sort的源碼實現(xiàn),在數(shù)據(jù)量大時候,采用快排,分段遞歸排序。一旦分段后的數(shù)據(jù)量小于某個閾值,為了避免快排的遞歸調(diào)用引起的額外開銷,此時就采用插入排序。如果遞歸層次過深,還會采用堆排序。

          今天,借助本文,我們分析下這次故障的原因,避免后面的開發(fā)過程中出現(xiàn)類似的問題。

          背景

          流量經(jīng)過召回、過濾等一系列操作后,得到最終的廣告候選集,需要根據(jù)相應的策略,進行排序,最終返回首位最優(yōu)廣告。

          struct?AdItem?{
          ??std::string?ad_id;
          ??int?priority;
          ??int?score;
          };

          現(xiàn)在有一個AdItem類型的verctor,要求對其排序,排序規(guī)則如下:

          • 按照priority升序排列

          • 如果priority一樣大,則按照score降序排列

          需求還是比較簡單吧,當時線上代碼如下:

          void?AdSort(std::vector?&ad_items)?{
          ?std::sort(ad_items.begin(),?ad_items.end(),?[](const?AdItem?&item1,?const?AdItem?&item2)?{
          ???if?(item1.priority???????return?true;
          ????}?else?if?(item1.priority?>?item2.priority)?{
          ??????return?false;
          ????}

          ????return?item1.score?>=?item2.score;
          ?}?);
          }

          測試環(huán)境構(gòu)造測試case,符合預期,上線。

          恐怖的事情來了,上線不久后,程序直接coredump,然后自動重啟,接著有coredump,當時心情是這樣的。

          定位

          第一件事,登錄線上服務器,通過gdb查看堆棧信息

          由于線上是release版的,看不了堆棧信息,將其編譯成debug版,在某臺線上進行灰度,不出意料,仍然崩潰,查看堆棧信息。

          通過堆棧信息,這塊的崩潰恰好是在AdSort函數(shù)執(zhí)行完,析構(gòu)std::vector的時候發(fā)生,看來就是因為此次上線導致,于是代碼回滾,重新分析原因。

          原因

          為了盡快定位原因,將這塊代碼和線上的vector值獲取出來,在本地構(gòu)建一個小范圍測試,基本代碼如下:

          void?AdSort(std::vector?&ad_items)?{
          std::sort(ad_items.begin(),?ad_items.end(),?[](const?AdItem?&item1,?const?AdItem?&item2)?{
          ??if?(item1.priority???????return?true;
          ????}?else?if?(item1.priority?>?item2.priority)?{
          ??????return?false;
          ????}

          ????return?item1.score?>=?item2.score;
          }?);
          }

          int?main()?{
          ??std::vector?v;
          ??/*
          ??給v進行賦值操作
          ??*/


          ??AdSort(v);

          ??return?0;
          }

          執(zhí)行下面命令進行編譯,并運行:

          g++?-g?test.cc?-o?test
          ./test

          運行報錯,如下:

          通過gdb查看堆棧信息

          線上問題復現(xiàn),基本能夠確認coredump原因就是因為AdSort導致,但是在AdSort中,就一個簡單的排序,sort不可能出現(xiàn)崩潰,唯一的原因,就是lambda函數(shù)實現(xiàn)有問題。

          利用逐步定位排除法,重新修改lambda函數(shù),執(zhí)行,運行正常。

          void?AdSort(std::vector?&ad_items)?{
          ?std::sort(ad_items.begin(),?ad_items.end(),?[](const?AdItem?&item1,?const?AdItem?&item2)?{
          ???if?(item1.priority???????return?true;
          ????}?else?if?(item1.priority?>?item2.priority)?{
          ??????return?false;
          ????}
          ????
          ????if?(item1.score?>?item2.score)?{
          ??????return?true;
          ????}

          ????return?false;
          ?}?);
          }

          運行正常,那么就是因為lambda比較函數(shù)有問題,那么為什么這樣就沒問題了呢?

          想起之前在>中看到一句話第21條:總是讓比較函數(shù)在等值情況下返回false。應該就是沒有遵循這個原則,才導致的coredump。

          那么為什么要遵循這個原則呢?打開Google,輸入std::sort coredump,看到了一句話

          ?

          Having a non-circular relationship is called non-transitivity for the < operator. It’s not too hard to realise that if your relationships are circular then you won’t be getting reasonable results. In fact there is a very strict set of rules that a data type and its comparators must abide by in order to get correct results from C++ STL algorithms, that is 「strict weak ordering」.

          ?

          從上面的意思看,在STL中,對于sort函數(shù)中的排序算法,需要遵循嚴格弱序(strict weak ordering)的原則。

          嚴格弱序

          什么是嚴格弱序呢?摘抄下來自wikipedia的定義:

          ?

          A 「strict weak ordering」 is a binary relation < on a set S that is a strict partial order (a transitive relation that is irreflexive, or equivalently,[5] that is asymmetric) in which the relation "neither a < b nor b < a" is transitive.[1] Therefore, a strict weak ordering has the following properties:

          • For all x in S, it is not the case that x < x (irreflexivity).
          • For all x, y in S, if x < y then it is not the case that y < x (asymmetry).
          • For all x, y, z in S, if x < y and y < z then x < z (transitivity).
          • For all x, y, z in S, if x is incomparable with y (neither x < y nor y < x hold), and y is incomparable with z, then x is incomparable with z (transitivity of incomparability).
          ?

          上面概念,總結(jié)下就是,存在兩個變量x和y:

          • x > y 等同于 ?y < x
          • x == y 等同于 !(x < y) && !(x > y)

          要想嚴格弱序,就需要遵循如下規(guī)則:

          • 對于所有的x:x < x永遠不能為true,每個變量值必須等于其本身
          • 如果x < y,那么y < x就不能為true
          • 如果x < y 并且y < z,那么x < z,也就是說有序性必須可傳遞性
          • 如果x == y并且y == z,那么x == z,也就是說值相同也必須具有可傳遞性

          那么,為什么不遵循嚴格弱序的規(guī)則,就會導致coredump呢?

          ?

          對于std::sort(),當容器里面元素的個數(shù)大于_S_threshold的枚舉常量值時,會使用快速排序,在STL中這個值的默認值是16

          ?

          我們先看下sort的函數(shù)調(diào)用鏈(去掉了不會導致coredump的部分):

          sort
          ->?__introsort_loop
          -->?__unguarded_partition

          我們看下__unguarded_partition函數(shù)的定義:

          template
          ?????_RandomAccessIterator
          ?????__unguarded_partition(_RandomAccessIterator?__first,
          ???????????????_RandomAccessIterator?__last,
          ???????????????_Tp?__pivot,?_Compare?__comp)
          ?????{
          ???????while?(true)
          ?????{
          ???????while?(__comp(*__first,?__pivot))
          ?????????++__first;
          ???????--__last;
          ???????while?(__comp(__pivot,?*__last))
          ?????????--__last;
          ???????if?(!(__first??????????return?__first;
          ???????std::iter_swap(__first,?__last);
          ???????++__first;
          ?????}
          ?????}

          在上面代碼中,有下面一段:

          while?(__comp(*__first,?__pivot))
          ?????????++__first;

          其中,__first為迭代器,__pivot為中間值,__comp為傳入的比較函數(shù)。

          如果傳入的vector中,后面的元素完全相等,那么__comp比較函數(shù)一直是true,那么后面++__first,最終就會使得迭代器失效,從而導致coredump。

          好了,截止到此,此次線上故障原因分析完畢。

          此次故障,由于牽扯到算法、工程等部門,由于一開始的時候,不確定問題出在哪(一方面線上是release版本,一方面涉及到多個模塊的改動),幾個部門聯(lián)合分析,最終才定位到bug原因,期間曲折過程略去不表。

          結(jié)語

          這個故障,說真的,無話可說,只能怪自己學藝不精,心服口服,也算是給自己一個教訓,后面test case盡可能跟線上一致,把問題盡早暴露在測試階段。

          這次把這個故障原因分享出來,希望大家在后面的開發(fā)過程中,能夠避免遇到同樣的問題。

          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久久久国产精品频道 | 三级片在线免费直播成人电影 | 国产又粗又猛视频 | 国产操操操| 爱搞国产|