結局C++とRustってどっちが良いの? 2traits
■ このスレッドは過去ログ倉庫に格納されています
「C++の色々配慮してめんどくさい感じは好きだけど、実務になったらメモリ安全性とか考えて今後Rustに変わっていくんかな」
「うだうだ言ってないで仕事で必要なのをやればいいんだよ、趣味なら好きなのやればいい」
っていうスレ。
前スレ: 結局C++とRustってどっちが良いの?
https://mevius.5ch.net/test/read.cgi/tech/1677286186/ >>658
C++に不慣れなRust使いさんですか?
fooもgooもmemcpyされてる
https://godbolt.org/z/xMxesqcss
>aの要素をcopyもmoveもしないので
これのソースは? >>659
あれ!? すまん
今の今までメンバーの配列は
デフォルトでcopyもmoveもしないのかと思ってた >>543
>>545
コンパイルしてみないとどれだけ容量食うか判らないのは可笑しいな
コンパイル前に今のcrates全部で3GB食うからそれ以上空けてないと
コンパイル開始しないで警告するようなオプション無いかな ポインタをちゃんと理解出来て無い人が、誤った使い方でLinkedListの性能
が悪いと思い込んでいるに一票。 その件は、リニアでもランダムでも個別要素に総なめアクセスするだけの問題→vector+reserveサイズで悩むだけだな
それ以外の状況は千差万別だけど、データ構造を再発明してもしょうがない、知識をつけないとね
>>599みたいな状況→LRUCache→>>592のLinkedHashMap(listと配列のハイブリッド)みたいに >個別要素に総なめアクセスするだけの問題
なら、Arena方式がOKの場合としてstd::pmr::monotonic_buffer_resourceを使ってみた
vector::push_back: 53
256
vector::push_back: 52
reserve, vector::push_back: 15
reserve, vector::emplace_back: 15
list::push_back: 96
list::emplace_back: 94
list<foo,Arena>::push_back: 22
list<foo,Arena>::emplace_back: 22 >vector+reserveサイズで悩む
見積を間違うとかえって遅くなるんだ(なんで?)
list+Arenaは使いどころがあるかもね
vector::push_back: 58
reserve, vector::push_back: 17 *
reserve, vector::emplace_back: 15 *
80% reserve, vector::push_back: 43 x
80% reserve, vector::emplace_back: 44 x
50% reserve, vector::push_back: 32
50% reserve, vector::emplace_back: 34
20% reserve, vector::push_back: 66 xx
20% reserve, vector::emplace_back: 66 xx
vector<foo,Arena>::push_back: 58
vector<foo,Arena>::emplace_back: 60
list::push_back: 94
list::emplace_back: 95
list<foo,Arena>::push_back: 22 *
list<foo,Arena>::emplace_back: 22 * 個人的には思ってたより面白い展開だけど
Rustで手を動かして晒す人が出てくるまで
C++側は控えても良いんじゃないかな?
でないと>>604みたいなのが増えるだけや >>662
逆やろw
ポインタも知らず実測したこともない子が
listに対して過剰に期待寄せてるだけで
実際C++書いてるやつならもっと冷静よ データ構造の性能比べはスレ違いだからそろそろやめねえか データ追加の計測をしてるようだけど
実際にはその後にそれらデータを使うため
listは各ポインタをたどるところで不利になってしまう
vectorはreserve無しだと再配置コストがかかるけど
最大個数Mのときに再配置コスト累計はM~2Mで固定
使えば使うほど誤差になっていくので
パフォーマンスを気になるような使い方なら無視してよいかと ここの人達が馬鹿なのは、LinkedListだけ本来の使い方が出来てないこと。
ポインタの使い方が根本的に理解できてないことが分かる。 vectorは、要素のラベルとしてindexを使うが、indexは初心者でも理解し易い。
ところが、LinkedListは、要素のラベルとしてポインタを使う。
ポインタを理解できない人は、いつまでもindexを使おうとする。
それでLinkedListの本来の性能が出ないので、LinkedListはvectorより遅い
と思い込むが、実際はその人の使い方が未熟だからに過ぎない。
多くはキャッシュのせいではなく、その人がまともに使いこなせて無い事が原因。 どうして馬鹿な人に限って「キャッシュのせいで」というかといえば、
キャッシュは、ケースバイケースなので、優秀な人でも断定的に語ることが
できないから、馬鹿な人でも優秀な人と似たような状況になって、能力の差異を
ごまかすことが出来そうに思えてしまうから。
キャッシュは、事を行なう順序によって、本当に千差万別の状況が生まれるし、
CPUの世代によっても変わってきてしまうから、語るのが難しいものであるが、
それ故に、理解力が無いから語れない人も、「ごまかすチャンス」を与える
ことが可能となる誤魔化しのユートピアとなる。 >>671
>vectorは、要素のラベルとしてindexを使うが、indexは初心者でも理解し易い。
STLのalgorithmの関数群に渡すときはiteratorのみ >>673
なんでそんな面倒なことを俺がやらなきゃならないんだよ。 率直に申し上げると
CとRustの相性は割と良いが
C++とRustの相性は良いとは
(特にテンプレ)言い切れない
即ちCからのリプレースに於いて
Rustが最適解に成り得るのに対し
C++はそのままC++で使い続けるが良かろう >>672
ケースバイケースじゃないですよ
cache hierarchyとcache localityという常識を学んでください
大昔ならいざ知らず今は大学1年生レベルでみんな習う常識ですよ >>677
キャッシュは、そういう問題じゃなく、前に何をやったかによって、
キャッシュに乗るかどうかが変わってくると言うことが起きることを
「ケースバイケース」と言っている。
だから、LinkedListにおいて、要素を交換するとしても、ソートの様に
ランダムに近い2つが交換した後のソート後の集合を先頭から最後まで巡る場合には
キャッシュミスが多くなるが、必ず隣り合う前後を交換することを2個おきにやった
場合の集合を最初から最後まで巡る場合は、キャッシュミスはほとんど起きない
という事情がある。
だから、LinkedListで要素を交換すると必ず遅い、というような法則は存在
しない。 間違った使い方をしていると主張するならば
他の人たちのように実際にコードを示して
そして比較計測することで実証しよう >>678
>キャッシュは、そういう問題じゃなく、前に何をやったかによって、
>キャッシュに乗るかどうかが変わってくると言うことが起きることを
>「ケースバイケース」と言っている。
君がそう思って書いてるのはわかってるよ
それが的外れだから勉強したほうがいいよと言ってるんだよ
> 必ず隣り合う前後を交換することを2個おきにやった場合の集合を最初から最後まで巡る場合
この処理がvectorより速くなるとでも?
遅い速いは相対的なものなんだから何と比べてるかくらいはわかるでしょ? Rustはグラフ的な構造が作れないから
単純な構造しか扱わないシステムプログラミングにはC++より
優れてると思う >>681
そのへんはどの言語も同じ
作れないとか優劣とかない
言語よりアルゴリズムの差が大きい Rustは、要素のラベルとしてindexを使うが、indexは初心者でも理解し易い。
ところが、C++は、要素のラベルとしてpointerを使う。
pointerを理解できない人は、いつまでもindexを使おうとする。
それでC++の本来の性能が出ないので、C++はRustよりunsafe
と思い込むが、実際はその人の使い方が未熟だからに過ぎない。
多くはC++のせいではなく、その人がまともに使いこなせて無い事が原因。 こういう人は、「汗をかく人」 違った意味で論外>< >>671
vectorの使い方すら知らないのかよ
プログラムを書いたことがないんだな
次の要素や前の要素を指すにはindexを増減させるのではなく
イテレータを使いポインタでアクセスだ
次の要素を指すには++で要素の大きさ分が加算されるだけなので速い >>678
linked listのワーストケースはその通りキャッシュが効かず順にたどるのも遅くなる
そこまでわかっているならvectorが速いことも理解できるだろ >680
>> 必ず隣り合う前後を交換することを2個おきにやった場合の集合を最初から最後まで巡る場合
>この処理がvectorより速くなるとでも?
>遅い速いは相対的なものなんだから何と比べてるかくらいはわかるでしょ?
>686
>イテレータを使いポインタでアクセスだ
コードを晒すのはやめるけど、カスタムリストのポインタ操作で実装したらリストの方が速かった
sizeof(foo)= 256 back swap
std::vector, swap(xs[i],xs[i+1]) 55 15
std::vector, swap(*it1,*it2) 54 15
std::list, swap(*it1,*it2) 22 15
LinkList, modify prev/next 22 4 backは末尾追加(push_backでもemplace_backでも同じ)
swapは>>678が言っている「必ず隣り合う前後を交換することを2個おきにやった場合の集合を最初から最後まで巡る場合」
試してみたら言っている通りだったけど、256バイトなのが効いている結果だと思う
それとstd::listはポインタ操作をやらせてくれなのかな? デカいものはそのようなそれ自体を動かすことはしない
例えばソートする場合でもポインタもしくはインデックスだけをソートする ポインタのインスタンス、だろ。リリースビルドではポインタだけになってるかもしれないけど。 そのvectorに格納している要素が大きな実体なのかそこへのポインタ(アドレス)なのかを理解していないとまずいぜ
要素がポインタを含めて小さい実体ならばvectorの要素の入れ替え・挿入・削除は非常に速い 一斉シフトが起きる形の挿入削除をしないならば実体をそのまま格納が有利 scanは先頭からvalue(int 4Byte)にアクセスしてダミーXOR演算する処理
dual link listのポインタ操作版(LinkList)はswapした後のscanが256Byteで激落ちだった xx
ForwardLinkListはforward linkかつallocate時のnextを別途キープし続けるデータ構造
sizeof(foo)= 256 n=1000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 52.986 3.461 14.739 3.458 74.644
std::list, swap(*it1,*it2) 22.574 6.470 16.252 6.375 51.672
LinkList, modify prev/next 21.940 6.509 4.229 39.717 72.395 xx
ForwardLinkList, modify next 22.824 6.532 4.395 5.861 39.611 *
sizeof(foo)= 128 n=2000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 60.411 6.789 14.340 7.144 88.683
std::list, swap(*it1,*it2) 28.981 13.839 16.871 14.124 73.815
LinkList, modify prev/next 27.969 13.845 10.074 16.062 67.951 *
ForwardLinkList, modify next 28.204 13.980 9.802 13.791 65.778 *
sizeof(foo)= 64 n=4000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 62.683 7.733 14.140 7.768 92.325
std::list, swap(*it1,*it2) 35.496 14.177 18.950 14.136 82.759 *
LinkList, modify prev/next 32.240 14.129 19.082 14.404 79.856 *
ForwardLinkList, modify next 33.547 14.197 19.169 13.922 80.835 *
sizeof(foo)= 32 n=8000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 61.452 8.084 14.104 8.230 91.870 *
std::list, swap(*it1,*it2) 51.176 17.195 22.936 17.336 108.643
LinkList, modify prev/next 47.231 17.226 23.422 17.519 105.398
ForwardLinkList, modify next 48.123 17.387 23.529 17.408 106.447 C++側としては面白がって人の作ったベースを弄り始めたんだけれど、
sortしたり途中挿入削除しないリニアアクセスだけの問題ならどっちでも良いなw
ただし要素が小さくて途中挿入削除しないのならvector
sizeof(foo)= 16 n=16000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 61.891 9.511 13.994 9.677 95.074 **
std::list, swap(*it1,*it2) 90.948 22.966 34.215 23.463 171.592
LinkList, modify prev/next 76.692 22.989 32.457 23.262 155.400
ForwardLinkList, modify next 76.857 23.095 31.426 23.639 155.018
sizeof(foo)= 8 n=32000000 back -> scan -> swap -> scan = total
std::vector,swap(*it1,*it2) 63.911 9.002 14.222 8.933 96.068 ***
std::list, swap(*it1,*it2) 171.875 52.681 71.011 43.465 339.032
LinkList, modify prev/next 142.104 39.655 52.617 40.589 274.964
ForwardLinkList, modify next 141.527 39.843 49.236 39.779 270.385
>>696に同意です >>694
メンバにサイズデカいのがあったら
moveコン呼ばれても結局コピーが発生するね
訂正訂正! moveがコピーをしないという意味のコピーと
今話題になっている入れ替えたりズラしたりする時にコストとなるコピーは別物だろ
moveでもコピーコストは発生する
それがポインタならばそのコピーコストが最小限になるというだけだ >>684
RustもC++と同じだよ
例えばvec.iter()はvecの各要素への参照(ポインタ)列を返すイテレータ
例えば要素のkey1をキーとしてソートするなら
vec.iter().sorted_by_key(|x| x.key1)
これもvecの各要素への参照(ポインタ)列をソート順に返すイテレータ
いずれも各要素自体のコピーは発生しない LinkedListのメリットはメモリを節約できる
の一点だけではないのか? そうじゃなくて頭から最後まで要素を一回走査して挿入削除する場合は一回当たりのコスとはvectorより有利
何度も何度も繰り返し頭から走査する条件下では不利
ここでのベンチ結果は後ろのパターンばかりで不利になるのは当然
コード書いてる人が意図的に間違えてるのかセンスがないかのどちらか
アルゴリズムの意味が理解できてないと言う話 コードを書く人間として常識的に考えればわかる
挿入時に一部だけ鎖つなぎなおすのが低コストか
毎回全部後ろまでずらすのが低コストか
削除時に一部だけ鎖つなぎなおすのが低コストか
毎回全部後ろの要素を前へずらすのが低コストか 実際に色んな仕事をしたことある人たちがvector派という感じ
現実は挿入することが目的ではなくその後に使うところが本場でvectorが有利
稀に比較挿入が主たる場合もあるけど効率の悪いlinked listではなくbinary treeなどを使う >>705
> 頭から最後まで要素を一回走査して挿入削除する場合は一回当たりのコスとはvectorより有利
そのような走査して挿入する利用で最も多いのが何かの順序に並べるケース
その場合は結果的にソートをしていることになるがオーダーO(n^2)となり効率が悪い
効率が良い方法はvectorを利用してまずは順序関係なく要素を追加してしまい
その格納された各要素へのポインタを対象に一気にO(n·log(n))の速いソートするとよい
ソートを必要とする多くのデータ処理はそのようにvectorを利用する形になる なんでソート済みのなかに挿入するのにまたソートするの?
頭悪そう。 だってほら、ソート済みのオブジェクトをひっかきまわす別スレッドがあったり?w (ひっかきまわし Linked Listをソートに使うのはアホ
計算量オーダーを知らないアホ
既に順に並んでるところへ挿入していくから速そうだと勘違いするアホ発見器 >>706
そこはみんなわかってるよ
挿入時や削除時に挿入位置と削除位置を見つけるために
LinkedListの走査が必要な使い方をしたら
性能的にはLinkedListを使う価値がなくなるという話をみんなしてるんだよ VectorとlinkedListってどっちが良いの?スレでやってください Rustユーザは何も書かないのな
こういうのには感心がない人達なのかな? 自分の無知を知らずシッタカするド素人と
自分の無知を察して慎重にシッタカするド素人
自分の無知を察して口をつぐむ慎ましいド素人
三番目なのがRustユーザ QiitaなんかでRustポエムがいいね入れ食いなのから察するに
Rustユーザは>>563みたいな数独パズル状態でどん詰まり傾向かな? 結局、ここに溜まるような奴はみんな、やれといわれたら(どっちもちゃんと)やる人間だろうしね
でも好きずきはある やっぱ俺はC++が好き いやmoveを分かってないレベルのRustユーザがいる
おそらくQiitaでRustポエムをいいねしてる?
一方、C++ユーザは実動コードやらベンチ結果を晒して議論するのを面白がってる様子
どっちも無知でも、どっちが成長するのか目に見えてるなw >>717
スレタイがアホ過ぎるので興味持って貰えんだろw 言語としてはある種似てるとは思うわ
言語仕様としてではなくてね
HaskellとC++とRustはいっしょのカテゴリに入ってる
シッタカしたいタイプのド素人がドヤるための言語
JavaとかPHPをドカタ言語といって蔑むのといっしょ
女さんが自分を飾るブランドを選ぶのといっしょ >>710
違うよ
何かを順番に並べるケースは実際はランダムアクセスなんだよ
そんな簡単なこともわからないの? >>724
おまえ数学できないだろ
計算量のオーダーを算出してみろ >>710
> そのような走査して挿入する利用で最も多いのが何かの順序に並べるケース
頭から走査する回数が増えるとlinkedlistが極端に不利になるって常識的に分かるよね
そういう条件でソートをlinkedlistでやるやつは馬鹿だろ
それなのに何で頭から操作繰り返すアルゴリズムを選ぶのか?
意味分かってないのか? >>725
結果見れば分かるだろ
結果から見るとlinkedlistで単体の件で条件マッチするまでソートはランダムソートのコスト+比較だから
a[x] で仮に条件が合うのがkだとしてそこまでは実際ランダムアクセスでそこまで行ってるのと変わらない
数学の素養がないのは君なんだけど…
多分これを書いても理解できてないよね… >>710
O(n log n) のコムソートはlinked listと相性悪くないよ。 >>718-723
Rustユーザはもういなのでは?www
ユーザが少ないし仕方ないか... 結局linkedlistが役立つケースは非常に限定的に限られるわけだ ランダムアクセスが苦手なのに挿入のたびに実質ランダムアクセスさせられてる
それに気が付いてない人の多いこと
全件を一度走査するのと実質ちまちまランダムアクセスを繰り返す挿入ソートの違いが理解できない馬鹿頭
数学が出来ない人間と話をしても無駄なんだよな…
馬鹿は水掛け論だと誤解してるから… >>728
コムソートは離れたm番目とn番目の要素の比較を頻繁にするからlinked listで不利 ようやく理解できた
局所的に見るとlistが速い部分もあるんだけど
作業全体で比較するとvectorが速くなるってことか 変な話だけど多分この議論の勝者は俺なんじゃないかな
数学的素養のない人間に衝撃を与えてる >>733
nとmの2つのポインタを持って隣に一つずつ走査するだけ。 リスト使うのに挿入時に探索やソートするのは設計ミスってる ちょっと再帰型の例として単方向連結リストが出ただけでここまで話を脱線させることができる集団 >>735
他の人たちの説明はほぼわかったけど
きみの説明だけわからんかった
具体的なコードを出してみたらどう? >話を脱線させることができる集団
やっぱり、LinkedListの無理問答は脱線の仕掛けだったんだ 論理的思考というワードを見るとなぜかテンションが上がる
所有権複製と同じぐらい
プログラムが論理的かどうかを判断するだけなら思考を研究しなくていい
最近のAI界隈が脳科学の研究をしないのと同じ 問: 何からの話題そらしなのか考察せよ
ヒント1: 専ブラの勢いグラフ >>745
>プログラムが論理的かどうか
論理もへったくれもないこと言ってんな
どんなにトチ狂った論理でも論理は論理だし
プログラムには論理以外何も無い
何が言いたいんだこいつは 本質的に雑スレなので、C++ vs Rust みたいな話題が好きそうな人が好きそうな話題ならなんでもいいや 正直リンクドリストなんて一生使わないので興味ないのは図星 >>739
そのコムソートのためにnとmの2つのポインタを持って、
双方向リストでmとnの入れ替えをしようとしたら、
ノードの入れ替えだけでとんでもないコストになった。
読み出しが4回と書き込みが8回もかかる
# mの前後がnを指すようにする
読 m_prev = m->prev
書 m_prev->next = n
読 m_next = n->next
書 m_next->prev = n
# nの前後がmを指すようにする
読 n_prev = n->prev
書 n_prev-> next = m
読 n_next = n->next
書 n_next->prev = m
# mがnの前後を指すようにする
書 m->prev = n_prev
書 m->next = n_next
# nがmの前後を指すようにする
書 n->prev = m_prev
書 n->next = m_next
リストのノードの入れ替えがこれだけ高コストだと、
ノードを入れ替えずに格納している要素の入れ替えをしたほうがよくて、
要素のサイズが大きければポインタのみ収容したほうがよくて、
ベクタが有利になるポインタのみ収容に行き着いてしまう? >>752
ベクタの値の交換とどっちが高コストかはその値のサイズによるでしょ。
値の交換はリンクトリストでもできるわけだからコストの低い方を選べばいい。 ベクタでソートする時は要素を動かす必要はなく各要素を指すポインタをソートする
リストでも同じ方法が可能だがリンクを毎回たどる分だけ不利
領域分割のクイックソートなどもリストでは使えない なんでソートする必要のあるデータを連結リストにぶち込もうと思ったんですか? >>754
リンクを毎回たどるって何のことを言ってんの?>>739のように現在指している要素の隣の要素を参照するだけだよ。
>領域分割のクイックソートなどもリストでは使えない
クイックソートもインデックスでランダムアクセスが必須じゃないからリンクトリストでもいけるみたい。
最初のピボット選択に不利なくらいで。 >>755
「おれじゃない。あいつがやった」と登場人物全員が思ってるから >>755
sort用途も不利となると
linked listの使い途がますます無いな もうないよ
Lispが持っていた機能が延々と引き継がれているだけ ■ このスレッドは過去ログ倉庫に格納されています