ディレクトリに入った大量のファイルへの操作

Linuxマシンの1ディレクトリに、うっかり数十万・数百万ファイル作ってしまうことないですか? 自分は今日もやってしまいました。

ls rm * も帰ってこなくて/エラーが出て身動きがとれなくなってしまいます。 そんな困った時のオペレーションのやり方をまとめておきます。

shell

下準備

まずは空の100万ファイルを生成します。

# time seq -w 1 1000000 | xargs touch

real	0m25.994s
user	0m3.118s
sys	0m17.361s

timeを図る前に、メモリに乗ったslab等を以下のようなコマンドでdropすると正確な値が読み取れるんじゃないかと思います。 ただ今回は面倒なので、Cache/Slabをたっぷり2GB弱メモリに載せてdropさせずに検証しています。

# echo 3 > /proc/sys/vm/drop_caches

ファイルを数える

まず、どんなファイルあるか知りたいですよね。 ただ、軽率にlsを叩くとコンソールが文字で埋まってしまいます。 慎重に数を数える所から行いましょう。

# time ls | wc -l
1000000

real	0m3.944s
user	0m3.410s
sys	0m0.534s

10万ファイルあるようです。 数えるだけで、4秒ほどかかってしまいました。 lsはデフォルトで結果をソートして表示するので、ソートを行わない -U オプションを渡すと早くなります。

# time ls -U | wc -l
1000000

real	0m0.706s
user	0m0.275s
sys	0m0.440s

一部のファイル名を見てみる

先程使ったソートを行わないことで高速化する ls -U を使用します。

# time ls -U | head 
0095772
0571231
0043505
0708529
0119538
0247028
0258524
0876395
0721112
0217545

real	0m0.006s
user	0m0.000s
sys	0m0.008s

非常に高速に先頭10件のファイル名を出力しました。

# time ls -U > /dev/null

real	0m0.690s
user	0m0.292s
sys	0m0.398s

| head が無いと、出力に時間がかかっていることも分かります。

一部のファイルの属性を見てみる

こういった大量にファイルがあるケースというのは、典型的な例で言うとログ等の機械生成されたファイルだと思います。 一部のファイルは退避させたかったりするので、調査のためにファイルサイズ・作成日時・容量などを知りたい場合が多いです。

普段は ls -lh コマンドで詳細を出して | less 等で追うのですが、ファイル数が多いと大変時間がかかってしまいます。 以下の実行結果を見ると、最初のオプションを指定しなかった ls | wc -l の倍ほどの実行時間がかかっていることが分かります。

root@kb:~/many-files# time ls -lh | head
total 0
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000001
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000002
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000003
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000004
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000005
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000006
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000007
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000008
-rw-r--r-- 1 root root 0 Mar 19 19:46 0000009

real	0m7.660s
user	0m4.791s
sys	0m2.867s

これはソートをやめても、実行時間は半分程度にしか減りません。

root@kb:~/many-files# time ls -lhU | head 
total 0
-rw-r--r-- 1 root root 0 Mar 19 19:46 0095772
-rw-r--r-- 1 root root 0 Mar 19 19:47 0571231
-rw-r--r-- 1 root root 0 Mar 19 19:46 0043505
-rw-r--r-- 1 root root 0 Mar 19 19:47 0708529
-rw-r--r-- 1 root root 0 Mar 19 19:46 0119538
-rw-r--r-- 1 root root 0 Mar 19 19:46 0247028
-rw-r--r-- 1 root root 0 Mar 19 19:46 0258524
-rw-r--r-- 1 root root 0 Mar 19 19:47 0876395
-rw-r--r-- 1 root root 0 Mar 19 19:47 0721112

real	0m4.492s
user	0m1.615s
sys	0m2.879s

ファイルの一覧の取得に1秒・詳細情報を取得するのに3秒・ソートするのに3秒というような内訳でしょうか。 lsコマンドは -l を指定すると、全ての結果を作ってから標準出力へと結果を出力するようです。 一部のファイルの情報だけ見たいのに、ほとんどのファイルの情報は捨てられてしまっています。

ls -U | head はメチャクチャに早かったので、これの結果を ls -l コマンドの後に指定することで早くなるのか試してみます。

# time ls -l `ls -U | head`
-rw-r--r-- 1 root root 0 Mar 19 19:46 0043505
-rw-r--r-- 1 root root 0 Mar 19 19:46 0095772
-rw-r--r-- 1 root root 0 Mar 19 19:46 0119538
-rw-r--r-- 1 root root 0 Mar 19 19:46 0217545
-rw-r--r-- 1 root root 0 Mar 19 19:46 0247028
-rw-r--r-- 1 root root 0 Mar 19 19:46 0258524
-rw-r--r-- 1 root root 0 Mar 19 19:47 0571231
-rw-r--r-- 1 root root 0 Mar 19 19:47 0708529
-rw-r--r-- 1 root root 0 Mar 19 19:47 0721112
-rw-r--r-- 1 root root 0 Mar 19 19:47 0876395

real	0m0.013s
user	0m0.007s
sys	0m0.009s

十分に早そうです。

名前で検索した上で、ファイルの属性を見てみる

ログなどの場合は、ファイル名に日付・タイムスタンプが利用されていることが多いと思います。 先月のログを削除する前に情報を見ておきたいと思って ls -l 2020-01-* 等と指定する事も多いと思います。

ですが、大量にファイルがある環境ではこの方法が使えません。 ダミーファイルは日付の名前が付いていないので、日付の範囲を指定することを模して10万~40万番のファイルの詳細を確認してみようと思います。

# ls -l 0{1..4}*
bash: /bin/ls: Argument list too long

bashなどのシェルでは、連番を生成する {1..4} ・ファイル名のワイルドカードを指す * 等は、シェルにて全て評価された後にコマンドの引数へと渡ります。 上記のコードでは、30万の引数が ls コマンドに渡ったということになります。 環境・コマンドによって引数の数の制限は異なりますが、典型的には2MB・数千個程度等の制限が掛かっていることが多いようです。

こういった際は、findコマンドを利用すると良いです。findコマンドは、パイプを正しく扱い、ファイル名を素早く返します。 -name フラグはシェル展開とは異なるパターンを受け付けますが、分かりにくいようであれば、 -iregex -regex で正規表現を利用することも出来ます。 パイプで一部のファイル名を返す場合・全てを /dev/null に捨てる場合で実行速度にかなりの差があることが分かると思います。

# time find -type f -name '0[1234]*' | head 
./0100001
./0100006
./0100010
./0100016
./0100022
./0100032
./0100034
./0100040
./0100062
./0100064

real	0m0.152s
user	0m0.076s
sys	0m0.081s

# time find -type f -name '0[1234]*' > /dev/null

real	0m1.423s
user	0m1.032s
sys	0m0.391s

ただ、これだけではファイルの詳細情報が分かりません。 findコマンドで検索した結果をheadで10件取り出し、それを ls -l の引数へと渡してみます。

# time ls -l `find -type f -name '0[1234]*' | head`
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100001
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100006
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100010
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100016
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100022
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100032
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100034
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100040
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100062
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100064

real	0m0.167s
user	0m0.076s
sys	0m0.102s

十分に高速になりました。 ただし、ファイル名にスペースが含まれていると正しく動きません。 これは xargs コマンドと、ファイル名の区切りをスペースではなくNULLバイトで表すオプションを付与することで解決できます。 find コマンドは -print0head コマンドは -zxargs コマンドは -0 フラグで、区切り文字をNULLバイトとして表現が可能となります。

# time find -type f -name '0[1234]*' -print0 | head -z | xargs -0 ls -l
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100001
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100006
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100010
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100016
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100022
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100032
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100034
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100040
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100062
-rw-r--r-- 1 root root 0 Mar 19 19:46 ./0100064

real	0m0.159s
user	0m0.083s
sys	0m0.097s

大量のファイルを削除する

lsと同じく、rmコマンドも引数の制限があるので、いつものようにシェル展開・ワイルドカード等を使うと削除が出来ません。

# time rm *
bash: /bin/rm: Argument list too long

これも、findとxargsを組み合わせることで解決できます。

# time find -type f -print0 | xargs -0 rm 

real	1m0.537s
user	0m3.457s
sys	0m14.028s

スペースが含まれないことが分かっていれば、単にこのように書くことも出来ます。

# time find -type f | xargs rm 

find -name find -regex フラグ等を利用し、ファイル名を検索した上で大量のファイルを削除することも容易でしょう。 今回は削除に時間がかかるので、他の例は載せません。

続きを読む

Lenovo T495sはバッテリー駆動時に性能が1/3に下がる

要約

  • Ryzenを搭載したLenovo T495sは、バッテリー駆動時に大幅に性能が下がる
  • CPUクロックが大幅に落ち、あるベンチマークのスコアは1/3に下がった
  • 修理工場に送ったが、「仕様であり修理・交換・返金等は行わない」との回答だった
  • ついでに、サポートの対応が個人的に気に食わない

lenovo t495s slowdown 2020 03 05 23 45 12

「持ち運びやすく高いパフォーマンスの14型スリムノートPC」を唄うT495s。

バッテリー駆動時に性能が下がる

  • Lenovo T495s
  • AMD Ryzen 7 Pro 3700U
  • RAM 16GB (内13.9GB利用可能)

T495sは会社で購入した開発用のマシンだった。 たが、購入当初からビデオ会議の際などに異様に文字入力が遅かったり、Slackでの文字入力が異常に遅くなるタイミングが有った。 色々試していると、AC電源を抜いた瞬間にCPUのクロック数が大幅に落ちているようだった。(Windowsタスクマネージャーで確認)

試しに、ブラウザで行える簡易的なベンチマークを動作させた。 https://chromium.github.io/octane/

octane result

すると、AC接続時・バッテリー駆動時でなんとスコアは三倍の開きが有った。 勉強会のメモを取っている時に異常にバッテリーの減りが遅いな(文字入力も遅い)と思っていたが、ここまで露骨に性能を下げていたのか…

色々やったが治らない

よくある解決法+ネットで同様の症状を他社PCで経験した人のブログを参考に試した。

  • Windowsの電源プランで「最小のプロセッサの状態」を90%に設定する等の変更を行う
  • Lenovo Vantageでのドライバ/BIOSの更新・デバイス診断の実行
  • Windows Update
  • Ryzen向けの電源プランをAMD社のサイトからDL・インストール(3000番代には不要だと思うが…)
  • TDPを変更するツール https://github.com/FlyGoat/RyzenAdj での設定(3000番代には対応していないようだった)

ベンチマークの値はほとんど変らない。 まだ買ったばかりなのでLenovoのサポートに教えてもらうかと考え始める。

サポートに連絡する

200文字しか入力できない問い合わせフォームに、上に書いたことを日頃のTwitterスキルを駆使しながら報告。 大まかな再現方法と、どういった設定などを行えば改善するかという作文を行った。

最初に案内されたことは以下の2つだったが、改善しなかったのでリペアセンターにPCを発送した。

  • リセットホールを押して、内蔵バッテリの無効化
  • 弊社のハードウェア診断 Lenovo Vantageからのシステム更新
  • 上記にて改善がない場合には、リペアセンターにコンピュータをお預かりし、診断修理させていただきたく存じます。

一週間後、このようなメールが送られてきた。

このたびは、お客様へご不便をお掛けし、申し訳ございませんでした。 ご指摘の症状について、弊社テストツールでの負荷状態とタスクマネージャーでのCPU速度の確認にて検証を実施いたしました。 ACアダプタ駆動に比べると、バッテリー駆動ではCPUの速度が低くなることを確認いたしました。 検証の為、検証リカバリも実施いたしましたが、上記現象は変化や改善はされませんでした。 その為、弊社検証機でも同様の検証を実施し、検証機も同様にACアダプタ駆動に比べると、バッテリー駆動ではCPUの速度が低くなることを確認いたしました。

以上の結果より、ご指摘の現象は故障ではないと診断いたしました。

(えっ…検証のためにリカバリするという話は聞いていないんだが…)

概ね予想通り、故障ではないと報告された。 納得が行かないので電話してみると、電話を受けていただいた男性の方は「性能が1/3に落ちるというのは客観的にみて異常じゃないか」という私の主張に納得していただき、再調査していただく事となった。その際に、以下のことを会話した。

  • 再度リペアセンターにて調査を行うとのことで、再現手順を教えてくれと言われたのでざっくりと伝える
  • バッテリー駆動・AC駆動でブラウザでのベンチマークスコアにて1/3程度に性能が下がる
  • ブラウザ・チャットツール・ビデオ会議ツールなどで日本語入力などが体感として非常に遅くなることを体感できる
  • 故障かはどうでもよいので、この仕様に問題が無いかも確認してほしい
  • 問題が見つからなければ、了承なしに返送して構わない

10日後、以下のメールが届く。

ご指摘の『バッテリー駆動時のパフォーマンス低下』について、再度弊社検証機との比較テストを行いましたが、大きな差異を確認することが出来ませんでした。 また、お客様ご希望のWebブラウザツールでのベンチマークテスト及びチャットツール/ビデオ会議使用時の日本語入力のもたつきについて、検証環境が無い為、検証することが出来ませんでした。

一旦、「検証手順を教えてくれと言ったの君等だろ」とため息を付く。 検証環境が無いのは、Chrome等全て再現環境が入った状態のPCを、君等が了承なしにリカバリーしたからでは???

とりあえず、電話で以下のことを中心に聞いた。

  • 検証環境が無いってどういうこと?無いならインストールして良いよ
  • 性能が1/3に下がるという仕様は、あまりにも無理がないか ノートパソコンだぞ
  • Lenovoとして、品質の基準を満たしている?他のPCでも起き得る?
  • 今回の事を理由に、PCの交換・返金等は行えないか
  • 結局、この使い物にならないPCをどうすればいいの?

回答は概ねこんな感じだった。

  • 細かい個別のソフトウェアの検証をリペアセンターでは行わない
  • 性能が下がるのは、仕様の範囲内
  • 基準を満たしているので、他のPCでも起き得る
  • 故障でないという判断になっているので、修理・交換・返金は一切行わない
  • AC電源に繋いで使って頂ければ

まとめ

LenovoとしてノートPCはバッテリー駆動時に性能が1/3になっても品質に問題は無い。 パフォーマンスモデルであっても、AC電源に繋がないとゲーム・写真加工・快適なブラウズが行えないことを許容しなければならない。

結局どうすればいいんだろうか… Lenovoの事がこの一件で嫌いになりました。

情報提供のお願い

同様のケースで同じ事で悩んでいる人は @kamijin_fanta までご連絡頂ければ嬉しいです。 傷をなめ合いましょう。

番外: サポートの対応が気に食わない

今回のコミュニケーションの中で、様々な食い違いが起きている。。。

リカバリーされることを聞いていなかった

実は、丁寧に最初のメールにて以下の同意を取られていた。

HDD不具合/OS不具合時の初期化作業について:了承 / 未了承

お預かりするコンピュータの故障状況によりましては、修理のためにハードディスクの初期化を実施して、コンピュータを返却させていただきます。

今回「問題が起こっていないので、修理は行っていない」と報告された。 別のPCで再現するという話なのに、なんで客のPCをリカバリするんだ… 検証のためだけにOSの初期化を行うことは説明されている範囲を超えているんじゃないかと感じる。

一般的に、修理に出したPCのデータが飛ぶのはあり得ると思ってたのでバックアップは取っていた。 が、また環境構築に1時間とか時間取られるのか勘弁してくれという気持ち。

再現手順を聞いておきながら、弊社そういうのやってないので…

電話口で「もう1回検証するから再現手順を教えてほしい」と言われたから色々と説明したのに、 次の連絡で「リペアセンターでは個別のソフトの動作は調べない」と返してくるのは、コミュニケーションとしてあんまりでは。

この間1ヶ月

出来る限り最速でメールは返信したし、即電話も行ったが返送までちょうど1ヶ月くらいかかった。 1月あたりの償却は5千円ほどするし、何も状況は良くならずストレスだけが残ったという悲しさ。

雑記

  • ネット上で記事を書くことは大丈夫かとサポートの人に確認済み
  • Thinkpad T14sでは治るんだろうか…
  • 腹たってきたのでそろそろやめます
続きを読む