My C++11 async practice

I’d like to rewrite concurrent.futures.ProcessPoolExecutor.map may be slow in some cases by using C++11 async and future

It takes some time to remember C++ stuff, but it’s still fun to write C++ code 🙂

Sample Code

#include <iostream>
#include <cmath>
#include <future>
#include <queue>
#include <vector>
#include <tuple>
#include <chrono>
#include <iterator>
#include <memory>
#include <functional>

typedef uint32_t NUM_TYPE;

const size_t REPEAT_TIMES = 100;
const NUM_TYPE TEST_NUM = 200000;

const size_t MAX_WORKER = 4;

typedef std::tuple<NUM_TYPE, bool> RESULT_TYPE;
typedef std::priority_queue<NUM_TYPE, std::vector<NUM_TYPE>, std::greater<NUM_TYPE>> STORE_DATA_TYPE;

RESULT_TYPE is_prime(NUM_TYPE num) {
  if (num % 2 == 0) {
    return RESULT_TYPE(num, false);
  }

  NUM_TYPE sqrt_num = static_cast<NUM_TYPE>(std::floor(std::sqrt(num)));
  for (NUM_TYPE i = 3; i < sqrt_num + 1; i+=2) {
    if (num % i == 0) {
      return RESULT_TYPE(num, false);
    }
  }
  return RESULT_TYPE(num, true);
}


std::queue<NUM_TYPE> is_prime_wrapper(std::vector<NUM_TYPE> nums) {
  std::queue<NUM_TYPE> result;
  for (auto & num : nums) {
    RESULT_TYPE prime = is_prime(num);
    if (std::get<1>(prime)) {
      result.push(std::get<0>(prime));
    }
  }
  return result;
}

STORE_DATA_TYPE async_worker(NUM_TYPE num) {
  STORE_DATA_TYPE result;
  std::vector<NUM_TYPE> nums;

  for (NUM_TYPE i = 0; i < num; ++i) {
    nums.push_back(i);
  }

  const size_t NUM_SIZE = num / MAX_WORKER;
  std::vector< std::future<std::queue<NUM_TYPE>> > futures;
  for (size_t i = 0 ; i < MAX_WORKER; ++i) {
    std::vector<NUM_TYPE> split_nums;
    if (i == MAX_WORKER - 1)  {
      split_nums = std::vector<NUM_TYPE>(std::begin(nums) + NUM_SIZE * i, std::end(nums));
    } else {
      split_nums = std::vector<NUM_TYPE>(std::begin(nums) + NUM_SIZE * i,
                                         std::begin(nums) + NUM_SIZE * (i+1));
    }
    futures.push_back(std::async(std::launch::async,
                                 is_prime_wrapper,
                                 std::move(split_nums)));
  }

  for (auto& worker : futures) {
    auto partial = worker.get();
    while (!partial.empty()) {
      result.push(partial.front());
      partial.pop();
    }
  }
  return result;
}

int main() {
  using std::chrono::high_resolution_clock;
  using std::chrono::milliseconds;

  milliseconds total_ms(0);
  for (size_t i = 0; i < REPEAT_TIMES; ++i) {
    auto t0 = high_resolution_clock::now();
    STORE_DATA_TYPE result = async_worker(TEST_NUM);

    auto t1 = high_resolution_clock::now();
    total_ms += std::chrono::duration_cast<milliseconds>(t1 - t0);
  }
  std::cout << "takes " << total_ms.count() / REPEAT_TIMES << " ms" << std::endl;
  return 0;
}

Result

takes 49 ms

一個 boost::exception 的例子

因為覺得 dprintf 出現的次數實在太多了,我會希望在 exception 就可以包好 __FILE__ 跟 __LINE__,又可以把 std::wstring 包在 exception 一起丟出去,在 catch 的時候再印出來就好。

所以試著寫了這樣一個例子,結果寫的四不像,跟原本想的東西完全部不同了,而且我對於那個 BUFSIZ 相當不滿啊。其實原本希望可以用 ##__VA_ARGS__ 這種 macro 直接吃進來變成一個 boost::any[],然後再想辦法轉成 boost::format 可以用的格式,中間還可以透過 BOOST_FOREACH 來玩的,不過我徹底失敗了,所以最後的成果大家笑笑就好了。 Continue reading “一個 boost::exception 的例子”

從 inet_ntoa 看 thread safe 的 API

這周 refactor 一段 code 之後,被同事說:「為什麼 log 的來源 IP 跟目的地 IP 總是一樣呢?」

char *szDestIp = inet_ntoa(destAddr);
char *szSrcIp = inet_ntoa(srcAddr);

後來仔細想了一下 inet_ntoa 的 prototype,我就明白了!如果這個 function 不必特意去 free 回傳的 pointer,那可能在內部有一個 static buffer 去保存這個值。果不期然,FreeBSD 的原始碼是這樣實作的

/*const*/ char *
inet_ntoa(struct in_addr in) {
        static char ret[18];

        strcpy(ret, "[inet_ntoa error]");
        (void) inet_ntop(AF_INET, &in, ret, sizeof ret);
        return (ret);
}

char *
inet_ntoa_r(struct in_addr in, char *buf, socklen_t size)
{
        (void) inet_ntop(AF_INET, &in, buf, size);
        return (buf);
}

到這邊我們大概就知道,inet_ntoa 並不是一個 thread safe 的 function,你可以用下面這段程式測一下會發生什麼事情。

void* thread_func (void *param)
{
    char * addr= (char *)param;
    struct in_addr val;
    inet_aton(addr, &val);

    while (1)
        printf("%s => %s\n", addr, inet_ntoa(val));

    pthread_exit(NULL);
}

int main()
{
    pthread_t thr1, thr2;
    pthread_create(&thr1, NULL, thread_func, (void *)"127.0.0.1");
    pthread_create(&thr2, NULL, thread_func, (void *)"127.0.0.2");

    while(1) {
        sleep (1);
    }

    return 0;
}

輸出大概會像

127.0.0.2 => 127.0.0.1
127.0.0.1 => 127.0.0.2
127.0.0.1 => [inet_ntoa error]
127.0.0.2 => 127.0.0.2

所以,直接使用 inet_ntoa 在 reentrant 上可能會發生問題,如果你必須面對這些問題,最好的方式就是使用 inet_ntoa_r 這個 reentrant 的版本,或者直接使用 inet_ntop。

而這難道非得要踩到地雷,或者從 API 的細節才能窺知這一切嗎?其實並不完全是,當你發現回傳的是一個 char *,又沒有要求你特別 free 他,你是不是就該猜測他是回傳 function 內部的一個 static buffer 了呢?所以,舉凡 ctime, asctime 都不保證是 thread safe 的,這是我們 programmer 該留意的細節。

所以,也許我們以後需要更小心的去寫程式,在選擇 API 時也必須留意更多細節,以免未來踩到更多地雷……。

以 boost::posix_time 處理時間字串

其實標題並不是那麼準確,這點我們之後再談。

其實起源很簡單,我們常常需要從各式各樣的時間字串轉成 time_t 或者 struct tm,要不然還可能是 FILETIME 跟 SYSTEMTIME 之類的結構。個人覺得 parsing 時間是一件難搞的事情,尤其是當你還要考慮 timezone 的影響,一切都變得不是那麼簡單了。

首先我必須要說,在 Windows 中沒有 strptime(3) 這樣還算好用的東西可以用,所以我又把腦袋動到 boost 上面去了。

我的目標是正確的處理「Sat, 28 Mar 2009 20:18:32 +0800」這樣的字串,然後把他轉成 time_t。我們會分兩個部份處理,首先我們先處理「Sat, 28 Mar 2009 20:18:32」這個部份,因為目前的 %q 只能處理輸出部份,請參考 Date Time Formatter/Parser Objects

    using namespace boost::posix_time;
    using namespace boost;
    using namespace std;

    std::string strDateTime = "Sat, 28 Mar 2009 20:18:32 +0800";
    std::string format = "%a, %d %b %Y %H:%M:%S";

    ptime pt(not_a_date_time);

    time_input_facet * input_facet = new time_input_facet(format);

    stringstream ss(strDateTime);
    ss.imbue(locale(ss.getloc(), input_facet ));

    ss >> pt;

這樣的確很簡單吧,只要按照文件寫的把 format 寫好,boost 就能幫你從 stringstream 的字串轉成 posix_time。

而 timezone 的部份就需要噁心一點的作法了。

    std::string strHour, strMinute;
    ss >> setw(3) >> strHour >> setw(2) >> strMinute;

    time_duration td(lexical_cast< int >(strHour), lexical_cast < int > (strMinute), 0);
    pt -= td;

我利用 setw 分別把 timezone 的小時與分抓出來,並轉成 time_duration,讓 posix_time 能直接減掉 timezone 的 offset 成為 GMT time,之後再做點手腳就可以變成 time_t 了。

嚴格來說,我想我是透過 time_input_facet 來處理字串的,希望這點小技巧對於需要 parsing 時間字串的人能有所幫助。

p.s: 其實這篇三個禮拜前就準備好了,只是一直拖到現在才把他丟出來 XD

初探 TR1 function object

最近的專案常常需要更新 INI 設定檔,而最麻煩的事情莫過於太多零散的設定,你必須要記住 INI 檔設定的 key,再把在某個 object 的 member function 把值抓出來,最後再丟給 WritePrivateProfileString 之類的 function 把值更新進檔案。

所以常常就會寫出一堆噁心的 code 像是。

    
    obj.iniKey = "INI_SERVER";
    obj.iniValue = info.GetServer();
    UpdateByObj(obj);

這樣的話,有幾個設定就要寫幾次,所有的 Key 跟 Value 的相關性都顯得零散,未來要加上新的設定,也非得要再加上類似的 code 才行……。

忽然我就靈機一動,也許可以用 tr1::function 跟 tr1::bind 來做這樣的事情。

    struct proxyConfig
    {
        std::string iniKey;
        std::tr1::function<std::string()> func;
    };

    const proxyConfig configArray [] =
    {
        { "INI_SERVER", std::tr1::bind(&proxyInfo::GetServer, &info) },
        { "INI_PORT",   std::tr1::bind(&proxyInfo::GetPort, &info) },
        { "INI_USER",   std::tr1::bind(&proxyInfo::GetUser, &info) },
        { "INI_PASS",   std::tr1::bind(&proxyInfo::GetPass, &info) },
    };

    for (size_t i = 0; i < sizeof(configArray)/sizeof(configArray[0]); ++i)
    {
        obj.iniKey = configArray[i].iniKey;
        obj.iniValue = configArray[i].func();
        UpdateByObj(obj);
    }

靠著把 INI 的 key 跟 member function 建表就顯得簡單一點,並把更新的程式碼簡化成一份,未來如果要新增不同的設定,也只要更動表中的 INI key 跟所綁定的 function 就好。

不過後來仔細想想,其實只要在建表的時候寫成 info.GetServer() 就可以滿足我的需求了。

而 tr1::function 與 tr1::bind 絕對不是只有這點小技倆而已,真正使用的巧妙應該是 C++ Function Objects in TR1 所提及那種彈性而優雅地將 function 包裹成物件,能有改變的彈性,也能應時應地做出適合的調整。

如果你的編譯器還不支援 tr1,除了換一個之外,你還可以考慮一下 boost,也許更瞭解一點,會讓你的生活變得更好歐。

C++ Format String using boost::format

如果你只想寫純粹的 C,那你可以忽略這篇的資訊了,因為 sprintf 之類的 function 大概就可以滿足你了。那為什麼還要提到 C++?我想 C++ 的 string 對於程式設計者是美好的,因為我們不用擔心 buffer 到底需要多長,我們可能是無腦的利用 operator += 去操作這個字串,並利用提供的 substring 等 function 快速的開發軟體。

但在我甚少的 C++ 開發經驗中,我總覺得透過 operator += 以及 stringstream 等東西,並沒有辦法像 sprintf 那樣直覺而優雅的將變數置換至字串當中。

就以 Windows 常見的 INI 格式來說,我們如果要輸出一個 AppName,像是 [MyApp],可能的作法是

    output += '[';
    output += strSection;
    output += ']';

這似乎比傳統的 sprintf 要顯得複雜的多,也顯得不好閱讀……

    sprintf(output, "[%s]", strSection);

現在你可以考慮 boost::format

    std::string output = boost::str(boost::format("[%s]") % strSection);

讓你寫的 C++ 能有 sprintf 的優雅性,又不必太過於擔心 buffer 的操作。

至於一些 manipulators,與詳細的 formatting 的用法,就請你自己去看一下官方的說明吧。此外,我覺得 python 的 formatting string 能吃 dictionary 也實在是很棒,而且很優雅的寫法。

    print "%(key)s %(key)s" % {'key': 'yoyo'}

在 boost::format 雖然只能這樣寫,但也聊勝於無了。

std::cout < < boost::format("%1% %1%") % "yoyo"; [/sourcecode] 某些情況下,你會考慮用 boost::lexical_cast,而捨棄 stringstream 等,不過也容許我提醒你一下,boost 會讓 object 變得肥上不少,至於怎麼取捨,就端看你的選擇了。