Valgrind(メモリリークチェックツール)

Rustのドキュメントの中に出てきたキーワードですが、Valgrindなるツールがあるらしいので触ってみた。

M1 Macにはインストールできないので、久々にVMのUbuntuにインストールしてみた。

ついでにRustもインストール。

<Rust on Ubuntu>

参考サイト、

https://qiita.com/yoshiyasu1111/items/0c3d08658560d4b91431

$ curl –proto ‘=https’ –tlsv1.2 -sSf https://sh.rustup.rs | sh

ただしcurlがcurl: (23) Failure writing output to destinationのエラーを吐くので、

$ sudo snap remove curl

$ sudo apt install curl

でcurl再インストールしたらできました、この理屈は以下のようなものかと、

https://qiita.com/ynott/items/c7362ab3fdcee3fb0972

 

<Valgrindのインストール>

https://valgrind.org/info/

wikipediaによるとvalgrind自身はvmになっていて、バイナリを中間言語に変換して実行するらしいから、実行速度はかなり低下する(数分の1)けれども、メモリリークテストにとってはそれほど問題とならないという理屈のようです。バイナリを中間言語に変換するので、コンパイラが何であってもバイナリさえあれば実行可能です。

Linux(VM上のUbuntu)にソースからインストールしてみた、

————————————————

ダウンロードした最新のtar.bz2ファイルを

tar -jxvf valgrind-3.23.0.tar.bz2

展開されたソースフィイルに移動して、

$ ./configure

$ make 

$ sudo make install

インストールできたら動作確認、

main.rsというシンプルなRustのファイルを作って、

fn main() {

  println!("Hello, world!");

}

これをコンパイルしてmainファイル作成して以下を実行すると、

$ valgrind ls -l
==10251== Memcheck, a memory error detector
==10251== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==10251== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==10251== Command: ls -l
==10251== 
合計 3700
-rwxrwxr-x 1 hoge hoge 3780776  5月 12 12:37 main
-rw-rw-r-- 1 hoge hoge      44  5月 12 12:36 main.rs
==10251== 
==10251== HEAP SUMMARY:
==10251==     in use at exit: 20,365 bytes in 9 blocks
==10251==   total heap usage: 140 allocs, 131 frees, 109,770 bytes allocated
==10251== 
==10251== LEAK SUMMARY:
==10251==    definitely lost: 0 bytes in 0 blocks
==10251==    indirectly lost: 0 bytes in 0 blocks
==10251==      possibly lost: 0 bytes in 0 blocks
==10251==    still reachable: 20,365 bytes in 9 blocks
==10251==         suppressed: 0 bytes in 0 blocks
==10251== Rerun with --leak-check=full to see details of leaked memory
==10251== 
==10251== For lists of detected and suppressed errors, rerun with: -s
==10251== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Rustなので問題は出ませんが、次にC++で意図的にリークが発生するような以下のソースでは、

#include <stdlib.h>
int main()
{
 char *x = new char[100];
 return 0;
}

以下のようにエラー(LEAK SUMMARY:)が発生します、この実行は実はintel MacOSにValgrindインストールして行ってますが、

% valgrind --tool=memcheck ./memory
==23180== Memcheck, a memory error detector
==23180== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==23180== Using Valgrind-3.23.0.GIT-lbmacos and LibVEX; rerun with -h for copyright info
==23180== Command: ./memory
==23180== 
--23180-- run: /usr/bin/dsymutil "./memory"
warning: no debug symbols in executable (-arch x86_64)
==23180== Syscall param map_with_linking_np(link_info) points to uninitialised byte(s)
==23180==    at 0x100067D2E: __map_with_linking_np (in /usr/lib/dyld)
==23180==    by 0x10001C88A: dyld4::setUpPageInLinkingRegions(dyld4::RuntimeState&, dyld4::Loader const*, unsigned long, unsigned short, unsigned short, bool, dyld3::Array const&, dyld3::Array const&) (in /usr/lib/dyld)
==23180==    by 0x10001C221: invocation function for block in dyld4::Loader::setUpPageInLinking(Diagnostics&, dyld4::RuntimeState&, unsigned long, unsigned long long, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x10001BF94: dyld4::Loader::setUpPageInLinking(Diagnostics&, dyld4::RuntimeState&, unsigned long, unsigned long long, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x10001CA08: dyld4::Loader::applyFixupsGeneric(Diagnostics&, dyld4::RuntimeState&, unsigned long long, dyld3::Array const&, dyld3::Array const&, bool, dyld3::Array const&) const (in /usr/lib/dyld)
==23180==    by 0x100022212: dyld4::JustInTimeLoader::applyFixups(Diagnostics&, dyld4::RuntimeState&, dyld4::DyldCacheDataConstLazyScopedWriter&, bool) const (in /usr/lib/dyld)
==23180==    by 0x100009DBC: dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) (in /usr/lib/dyld)
==23180==    by 0x1000092FE: (below main) (in /usr/lib/dyld)
==23180==  Address 0x1048e7d00 is on thread 1's stack
==23180==  in frame #1, created by dyld4::setUpPageInLinkingRegions(dyld4::RuntimeState&, dyld4::Loader const*, unsigned long, unsigned short, unsigned short, bool, dyld3::Array const&, dyld3::Array const&) (???:)
==23180== 
==23180== 
==23180== HEAP SUMMARY:
==23180==     in use at exit: 9,441 bytes in 176 blocks
==23180==   total heap usage: 186 allocs, 10 frees, 10,173 bytes allocated
==23180== 
==23180== LEAK SUMMARY:
==23180==    definitely lost: 4,388 bytes in 135 blocks
==23180==    indirectly lost: 48 bytes in 1 blocks
==23180==      possibly lost: 600 bytes in 3 blocks
==23180==    still reachable: 4,405 bytes in 37 blocks
==23180==         suppressed: 0 bytes in 0 blocks
==23180== Rerun with --leak-check=full to see details of leaked memory
==23180== 
==23180== Use --track-origins=yes to see where uninitialised values come from
==23180== For lists of detected and suppressed errors, rerun with: -s
==23180== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 124 from 39)

Rustではあまり出番がなさそうですが、c++では使えるツールのようです。

 

admin

c/c++におけるerrno値の定義

CtelloのGitHubのソースをビルドして実行すると以下のようなエラーが出ました。

最初は何気なくTello sdk 2.0用だからと思ってましたが、考えてみれば引数のエラーだから2.0は無関係。

cello.cppで、

#include <errno.h>

されているので、errno.hの中身を追跡するとエラー番号一覧がわかるはず。

コードの中では、

strerror(errno)

を使ってデコードしてますが、

[11/09/22 12:28:51] [ctello] [info] Finding Tello ...
[11/09/22 12:28:51] [ctello] [error] sendto: 22 (Invalid argument)
[11/09/22 12:28:52] [ctello] [error] sendto: 22 (Invalid argument)
[11/09/22 12:28:53] [ctello] [error] sendto: 22 (Invalid argument)

 

基本のエラー番号をまとめてるサイトがあって、

https://qiita.com/h2suzuki/items/0cc924cdd9d5c6d47448

こちらは有用だと思います。

肝心の引数エラーの原因調査はこれから。

 

admin

c++でtello制御(コマンドファイル実行とビデオストリーム収録を並行実行)

Telloからビデオストリーム受信して、デコードして表示とローカルファイルに格納、同時にコマンドファイルからコマンド読み出して並行に実行させるバージョンです。

Telloにコマンド送って時間待ち5秒は冗長だろうしスマートでもないから改善余地ありです。

コードはこちら、

https://github.com/chateight/c_plus_video

ctello_stream.cppが今回のソースです。

 

admin

 

Spdlog(c++ログツール)

Ctelloでspdlogが必須だったので、触ってみました。

spdlogはc++用のログツール、c++に標準で存在しないのは不思議な気もしますが。

本家

https://github.com/gabime/spdlog

使用例も豊富です。

 

<インストール>

① 以下のヘッダーファイルをコピー

https://github.com/gabime/spdlog/tree/v1.x/include/spdlog

② おすすめは、

“Compiled version (recommended – much faster compile times)”

と言ってますが、macだとbrewが一番簡単でしょう。

% brew install spdlog

% brew list --version|grep spdlog

spdlog 1.10.0_1

 

<サンプルコード>

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"

int main()
{
    spdlog::info("infromaion!");
    spdlog::warn("warning!");
    spdlog::error("error!");

    std::string test_message = "OK";
    spdlog::info("Test result is {}", test_message);

    int number_int = 10;
    float number_float = 12.345;
    spdlog::info("number_int is {:03d}", number_int);

    // set log level which also affects to the file log
    spdlog::set_level(spdlog::level::warn);
    spdlog::info("number_float is {:03.2f}", number_float);

    // logging to the log file
    auto log_file = spdlog::basic_logger_mt("logger", "log/basic.txt");
    log_file -> info("hello!");
    spdlog::set_default_logger(log_file);
    spdlog::warn("warning! write to the log file");
}

 

<使い方(cmakeファイル)>

https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt

から以下のmakefileで、

cmake_minimum_required(VERSION 3.14)
project(SpdProject)

set(CMAKE_CXX_STANDARD 14)

if(NOT TARGET spdlog)
    # Stand-alone build
    find_package(spdlog REQUIRED)
endif()

# ---------------------------------------------------------------------------------------
# Example of using pre-compiled library
# ---------------------------------------------------------------------------------------

add_executable(spdlogTest src/spdlog_t.cpp)
target_link_libraries(spdlogTest PRIVATE spdlog::spdlog)

 

<実行結果>

・コンソール

% ./build/spdlogTest 
[2022-11-06 18:49:01.191] [info] infromaion!
[2022-11-06 18:49:01.192] [warning] warning!
[2022-11-06 18:49:01.192] [error] error!
[2022-11-06 18:49:01.192] [info] Test result is OK
[2022-11-06 18:49:01.192] [info] number_int is 010

・ログファイル(basic.txt)

実行の都度追記されます。

[2022-11-06 18:34:16.437] [logger] [warning] warning! write to the log file
[2022-11-06 18:49:01.192] [logger] [warning] warning! write to the log file

 

<ソース他>

ソースコード他は、以下に置いてあります。

https://github.com/chateight/spdlog_ex

 

admin

c++でtelloを制御する

Pythonからc++のライブラリ呼ぶのは、ライブラリのビルド条件が複雑なので、いっそ全てをc++で作りあげるのが良さそうです。opencvもc++用が存在してるわけだから。

SDK 1.3に記述ある通り、UDPの送信とステータスの受信ポートが違うことは要注意です。

https://dl-cdn.ryzerobotics.com/downloads/tello/20180910/Tello%20SDK%20Documentation%20EN_1.3.pdf

やっていることはPython版と同じく、外部のテキストファイルに記述されたコマンドを順次telloに送ってステータスを受信しているだけです。

実は、コマンドの実行結果を受け取る前に次々にコマンド送ってるから、スプールされてしまっているようだから、作りが変だとは思う。

コードはこちら、

https://github.com/chateight/c_plus_video

ソケット通信の基本の解説は、

https://qiita.com/Michinosuke/items/0778a5344bdf81488114

通信サンプルは、

https://qiita.com/srs/items/c9286b5cff99e741b993

 

c++での制御は、すでに先駆者がいるから、この先はこれを使った方がいいでしょう。

https://github.com/carlospzlz/ctello

P.S. 2022/11/5

これはTello EDU用でした、違いを修正してもいいかもしれないけど。

 

動作は他の手段で動かすのと同じ。

 

admin

h264decoder共有ライブラリはBoost.Python使っている?

以下の記事中で、

https://isehara-3lv.sakura.ne.jp/blog/2022/10/28/telloで画像転送python-h-264共用ライブラリ/

c++のライブラリをPythonから呼び出すためにpybind11のインストールが指定されていますが、c++ライブラリのラッパーファイル(h264decoder_python.cpp)を見てみると、pybind11ではなくてBOOST_PYTHONが使われているからpybind11は不要だよねと思いました。

BOOST_PYTHON_MODULE(libh264decoder)

試しに、仮想環境(v_python)からpybind11をpip uninstall pybind11しても動作はするから、やはり不要らしい。

Boost.Pythonをpybind11と比較するとサイズが巨大らしい。pybind11の方が後発だから、恐らく機能的には優っているだろうしユーザーも多いのではないかと思います。

P.S. 2022/10/31

H.264 decoderのビルドのためのCMakeLists.txtを見てみるとpybind11を探して、もし存在しなければ持ってきてるからビルドには必要とされているようです、何故だろう?

find_package(pybind11)
if(pybind11_FOUND)
  message("Using existing pybind11 v${pybind11_VERSION}")
else()
  message("Fetching pybind11")
  include(FetchContent)
  FetchContent_Declare(
    pybind11
    GIT_REPOSITORY https://github.com/pybind/pybind11
    GIT_TAG v2.5.0)
  FetchContent_MakeAvailable(pybind11)
endif()

P.S. 2022/11/7

今更ながらですが、改めて見るとpybind11使ってました。じゃ最初のソースは何見たんだろう?とりあえずすっきりはしましたが、

PYBIND11_MODULE(h264decoder, m)
{
  PyEval_InitThreads(); // need for release of the GIL (http://stackoverflow.com/questions/8009613/boost-python-not-supporting-parallelism)
  py::class_(m, "H264Decoder")
                            .def(py::init<>())
                            .def("decode_frame", &PyH264Decoder::decode_frame)
                            .def("decode", &PyH264Decoder::decode);
  m.def("disable_logging", disable_logging);
}

 

admin

includePathが見つからない(Intel Mac)他

久々にIntelMacとM1MacでVScode環境でc++コンパイルしようとしたらいくつかのエラー発生。IntelMacは問題が二箇所で、M1Macは一箇所だけでしたが

— Intel Mac —

<問題1:includePathが見つからない>

以下を参考にパスを追加

https://jpdebug.com/p/151056

% g++ -v -E -x c++ –

で検索されたパスをc_cpp_properties.jsonに追記してコンパイルはできるようになったけど波線(つまり見つからないエラー)は消えないので、以下を追加

c_cpp_properties.jsonの、

“compilerPath”: “/usr/bin/clang”,

“compilerPath”: “/usr/bin/g++”,

に変えたら解決したようだ、これは即ち

% where g++ 

/usr/bin/g++

で示されるパスですが

<問題2:無限にXcodeコマンドラインツールをインストールしろメッセージ>

Xcodeコマンドラインツールをインストールしろと怒られて、何回インストールしても直らない

https://qiita.com/arks22/items/bb1a70a4803881c4e4e1

が該当していて、エラーメッセージの中に確かに解決案が提示されてます

~~~~~

Please ensure Xcode packages are up-to-date — try running ‘xcodebuild -runFirstLaunch’.

~~~~~

で以下の実行で解決したようです

% xcodebuild -runFirstLaunch

どこかのOSのアップデート、恐らく至近、で問題が出るようになったんでしょう

 

— M1Mac —

MacBook Air(M1)ではg++の版数を再指定(古い版数でコンパイルされたので)しなければいけなかったし、これもアップデート影響なのか

感覚的にはかりそめの解決のような気もするけれども、元々開発環境とはそういうものだろう

 

admin

new int[]とstd::vectorクラスでの実行速度差

<動作環境>

Intel MacBook Pro 16

 

<メモリ管理>

new int[]方式はメモリの領域管理、特にリリースを自分で管理しなければいけないのでバグの入り込み余地が多いのですが、コンテナクラスを使うことでその可能性を少なくしてくれます。しかし、想像するにパーフォーマンスは落ちるでしょう。ということで比べてみました。

#include <iostream>
#include <utility>
#include <vector>

const int a_size = 1000000*100;

class home
{
    int* m_land;
    int* cp_land;

public:
    explicit home(int size)
        : m_land{new int[size] ()}{}
    
    ~home() { delete [] m_land;}

    home(home&& other);

    int* land() const { return m_land; }

    int* land_cp() const { return cp_land; }

    void copy()
    {
        cp_land = new int[a_size];
        for (int i = 0; i <= a_size; i++)
        {
            cp_land[i] = m_land[i];
        }
    }

    void v_copy()
    {
        std::vector v_int(a_size, 0);
        //std::vector vc_int(a_size);
        //std::copy(v_int.begin(), v_int.end(), vc_int.begin());
        std::vector vc_int = v_int;
    }

    void del()
    {
        delete [] cp_land;
    }
};

home::home(home&& other)
    : m_land{other.m_land}
{
    other.m_land = nullptr;
}


int main()
{
    std::chrono::system_clock::time_point  start, end;
    start = std::chrono::system_clock::now();

    home A{a_size};
    std::cout << "A\'s land address : " << A.land() << std::endl;

    A.copy();
    std::cout << "A\'s cp_land address : " << A.land_cp() << std::endl;
    A.del();

    home B{std::move(A)};
    std::cout << "B\'s land address : " << B.land() << std::endl;

    B.v_copy();

    std::cout << std::endl;
    end = std::chrono::system_clock::now();
    double elapsed = std::chrono::duration_cast(end-start).count();
    std::cout << "elapsed time : " << elapsed << " ms" << std::endl;
}

追加したのはv_copyメソッドとその中でパターンの変更とcopyメソッド使う使わないはソース(35~38行目、69行目の該当行コメントアウト)で対応。

 

<実行結果>

① std::vectorで領域確保だけで2秒近く要している

② 領域のパディングは自動と明示である程度の差がある、new int[]は0でしかできないけど、vectorは任意の値で初期化できるのはアドバンテージ

③ コピーは単純コピーが相対的std::copyより早いけど、それでもマニュアルのforループコピーよりもおよそ2倍(std::copyはおよそ4倍)の時間が掛かる

④ std::copyは実行時間最遅

領域サイズ:100MB

<初期状態:マニュアル(new int[])で領域確保とコピー>
A's land address : 0x7fad25700000
A's cp_land address : 0x7fad05700000
B's land address : 0x7fad25700000

elapsed time : 497 ms


<領域確保:おそらく0埋めされるはず>
A's land address : 0x7f8996700000
A's cp_land address : 0x7f8976700000
B's land address : 0x7f8996700000

elapsed time : 2292 ms


<領域初期化値を明示的に追加>
A's land address : 0x7f8549700000
A's cp_land address : 0x7f8529700000
B's land address : 0x7f8549700000

elapsed time : 2518 ms


<別のstd::vectorにコピー>
A's land address : 0x7fc174f00000
A's cp_land address : 0x7fc154f00000
B's land address : 0x7fc174f00000

elapsed time : 3510 ms


<コピーにstd::copyを使用>
A's land address : 0x7f8b68700000
A's cp_land address : 0x7f8b48700000
B's land address : 0x7f8b68700000

elapsed time : 4396 ms


便利・セキュアさと処理時間は相反するというのは、まあそんなものでしょうか。

ソースは、

https://isehara-3lv.sakura.ne.jp/blog/2022/07/13/右辺値参照の代表的使い方である移譲処理でのパ/

の記事に追加して以下のurlになります。

https://github.com/chateight/c-plusplus/blob/master/move_perform.cpp

 

P.S. @2022/7/21

M1 MacBook Airで同じ処理実行させると処理速度が全く違う。いずれのケースでも1秒以下(700ms程度)で実行されるから、その差はなんだろう?

そのままのソースではコンパイルできないので、#include <chrono>は必要でしたが、それは本質ではない。

可能性としては、コンパイラーのバージョンぐらい(c++11 vs c++20)なのか?MacBook Pro 16のコンパイルオプションを-std=c++20にしても変わらないからそうじゃないよね。だとすると処理系の差?

 

admin

右辺値参照の代表的使い方である移譲処理でのパーフォーマンス効果

<実行環境>

Intel MacBook pro 16

 

<右辺値参照で移譲処理>

コピーは性能に大きな影響があるのは常ですが、じゃどれぐらい影響あるのか試してみました。

やってること、

① メモリ領域確保(初期化 or 未初期化:13行目の()有無)、この場合には100MB確保と領域の移譲処理

② 領域のコピー(有り or 無し:53/54/55行目のコメントアウト有無)

の4パターンの実行速度比較

#include <iostream>
#include <utility>

const long a_size = 1000000*100;

class home
{
    int* m_land;
    int* cp_land;

public:
    explicit home(long size)
        : m_land{new int[size] ()}{}
    
    ~home() { delete [] m_land;}

    home(home&& other);

    int* land() const { return m_land; }

    int* land_cp() const { return cp_land; }

    void copy()
    {
        cp_land = new int[a_size];
        for (int i = 0; i <= a_size; i++)
        {
            cp_land[i] = m_land[i];
        }
    }

    void del()
    {
        delete [] cp_land;
    }
};

home::home(home&& other)
    : m_land{other.m_land}
{
    other.m_land = nullptr;
}


int main()
{
    std::chrono::system_clock::time_point  start, end;
    start = std::chrono::system_clock::now();

    home A{a_size};
    std::cout << "A\'s land address : " << A.land() << std::endl;

    A.copy();
    std::cout << "A\'s cp_land address : " << A.land_cp() << std::endl;
    A.del();

    home B{std::move(A)};
    std::cout << "B\'s land address : " << B.land() << std::endl;

    std::cout << std::endl;
    end = std::chrono::system_clock::now();
    double elapsed = std::chrono::duration_cast(end-start).count();
    std::cout << "elapsed time : " << elapsed << " ms" << std::endl;
}

<結果>

・領域初期化有りモードではおおよそコピー時間差(領域初期化の有無で、領域コピー有りの時間にそれほど差はないのはなぜかと思うけど)、これを見ると領域の初期化も当然重い処理。

・領域初期化無では領域コピー実行の比重がほぼ100%、コピーしなければ1ms以下で処理完了してます。

・いずれにしろ、これらの処理が繰り返し実行される場合には大きなパーフォーマンスの差になるから、移譲は使える時には使ったほうが良い。

領域サイズ:100MB

<領域初期化有り>

・std::move
A's land address : 0x7ff049f00000
B's land address : 0x7ff049f00000

elapsed time : 166 ms

・+ 領域copy
A's land address : 0x7fb615700000
A's cp_land address : 0x7fb5f5700000
B's land address : 0x7fb615700000

elapsed time : 546 ms


<領域初期化無し>

・std::move
A's land address : 0x7fb23ff00000
B's land address : 0x7fb23ff00000

elapsed time : 0 ms

・+ 領域copy
A's land address : 0x7fb03df00000
A's cp_land address : 0x7fb01df00000
B's land address : 0x7fb03df00000

elapsed time : 514 ms

ソースは以下のリンクから、

https://github.com/chateight/c-plusplus/blob/master/move_perform.cpp

メモリ管理をマニュアルでやっていますが、vectorクラスのようなコンテナを使ったときのパーフォーマンスにも興味があります。

 

<疑問一点>

デストラクターでcp_landの解放すると、

*** error for object 0x7ff7b9aae490: pointer being freed was not allocated

のエラーが出ますが、なぜ?取り敢えずはdel()メソッド追加で対応してますが。

 

admin

 

c++ threadインスタンスを無名にしてvectorに格納

マルチスレッドで、スレッドに名称をつけてスレッド数を管理するのは面倒です。

スレッド名を無名にして、vectorに格納しても同じ処理でのスレッド名に意味はないからその方が便利です。

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <cmath>

std::mutex mtx_;                // mutex for exclusive control
std::vector vec{};         // prime numbers array
int cal_int = 1;                // calc target integer
int max_int = 10000000;         // calc target max number
std::vector threads;       // array of threads
int num_thread = 12;                    // number of threads



void add_prime(int i)           // store the prime numberns in the vector
{
    std::lock_guard lock(mtx_);
    vec.push_back(i);
}

int get_int()                   // get the integer to be tested
{
    std::lock_guard lock(mtx_);
    ++cal_int;
    if (cal_int > 2 && cal_int%2 == 0)      // do not return an even number larger than four
    {
        ++cal_int;
    }
    return cal_int;
}

void ThreadA()
{
    while (true)
    {
        bool flag = false;
        int i = get_int();
        if (i > max_int)
        {
            break;
        }
        int sqt = sqrt(i);
        for (int j = 2; j <= sqt; ++j)
        {
            if (i%j == 0)
            {
                flag = true;
                break;
            }
        }
        if (flag != true)
        {
            add_prime(i);
        }
    }
}

int main()
{
    std::chrono::system_clock::time_point  start, end;
    start = std::chrono::system_clock::now();

    std::cout << std::endl;
    std::cout << "Hardware concurrency = " << std::thread::hardware_concurrency() << std::endl;

    for(size_t i=0; i < num_thread; ++i){
        threads.emplace_back(std::thread(ThreadA));
    }

    for(auto& thread : threads){
        thread.join();
    }

    std::cout << std::endl;
    std::cout << "Number of threads = " << num_thread << std::endl;

    std::cout << std::endl;
    end = std::chrono::system_clock::now();
    double elapsed = std::chrono::duration_cast(end-start).count();
    std::cout << "elapsed time : " << elapsed << " ms" << std::endl;

    std::cout << std::endl;
    std::cout << "number of prime numbers : " << vec.size() << std::endl;

    std::cout << std::endl;
    std::cout << "last five prime numbers" << std::endl;
    std::sort(vec.begin(), vec.end() );
    
    for (auto itr = (vec.end() - 5); itr != vec.end(); ++itr)
    {
        std::cout << *itr << std::endl;
    }

}

こうすれば、変数を一箇所変えるだけでスレッド数が自由に変更できます。4以上の偶数は素数判定スレッドに渡さないで、処理時間短縮も図っています。

 

<実行結果>

Hardware concurrency = 12

Number of threads = 12

elapsed time : 1358 ms

number of prime numbers : 664579

last five prime numbers
9999937
9999943
9999971
9999973
9999991

https://isehara-3lv.sakura.ne.jp/blog/2022/05/01/c-マルチスレッドの効果/

で、およそ1.8秒でしたが1.3秒程度まで高速化。所詮、多数桁の素数計算には誤差とも言える範囲ですが。

 

admin