2017年9月2日土曜日

libcurl による連続取得における排他処理

HTTPS コネクション時の libcurl による連続GET にて HTTPS への GET リクエストを行う際、curl ハンドラを初期化せずに利用した方が高速にダウンロードされると言う例題を記載しましたが、別のスレッドでタイミングがバラバラに GET したい場合、排他処理にて安全性を確保するための例題です。

libcurl 自体はスレッドセーフ化されているので、curl_easy_init にて毎回ハンドラを初期化し利用すれば何の問題もなく別のスレッドで GET リクエストを行えますが、同じハンドラを使いまわしたい場合には同じタイミングで同じハンドラが利用されないように排他処理を行っておく必要があります(今回のような特別な用途でないかぎりは全く必要のない例題です)。

pthread_mutex_lock にて排他処理を行っているので、GET リクエストが並列化されるわけではないので並列化による恩恵は受けれませんが、HTTPS への要求時における証明書の取得などの内部処理を毎回行わずに済むなどの恩恵が大きい場合に利用します(例題のweb.fc2.comはその恩恵が得られないサイトで、証明書が正しくない場合に起こるようです、別のサーバーにてお試しください)。 実行結果

2017年8月14日月曜日

libcurl-multi による並行GETリクエスト

libcurl-multi による並行GETリクエストを行う例題です。

例題では複数のURLを指定し並列にGETリクエストを行い、その結果を用意した char 型のバッファ内に格納しています。
HTTPS で連続取得を行う場合にはこちらで試した方法の方が高速な場合があります。 実行結果

2017年8月11日金曜日

HTTPS コネクション時の libcurl による連続GETリクエストの高速化の検討

HTTPS コネクション時の libcurl による連続GETリクエストを高速化するための検証用の例題です。

libcurl による GET 取得時の高速化手段として以下のサイトでまとめられています。
https://moz.com/devblog/high-performance-libcurl-tips
(c-ares/PowerDNS 等により DNSのリスポンスを良くするとか libcurl-multi による並列処理化等、上記サイトには書かれていませんが TCP FAST OPEN に対応しているサーバーであれば libcurl からオプションを指定して利用できます)

一方、HTTPS でのリクエストでは証明書の検証等で、単純なデータやり取り以外の部分で内部的に処理が食われてしまうことがあります。
下記の例題では HTTPS にてデータ取得を行うサイトが信頼のあるサイトであることを前提とし、証明書の検証を行っていません。

例題では以下の3つの手法にてサイトからのデータ取得をループし、取得時間を計測しています。
CURL_RequestText1:毎回CURLハンドラを初期化し、データ取得を行う例
CURL_RequestText2:毎回CURLハンドラを初期化し、内部的に確保されているコネクションの再利用等は行わずデータ取得を行う例(CURLOPT_FORBID_REUSE/CURLOPT_FRESH_CONNECT)
CURL_RequestText3:CURLハンドラを予め準備しておき、繰り返し利用する間は再利用する例
結果では、1/2 はサイトにより多少の変化はあるもののあまり差はなく(2の方が若干早い印象)、3 は 1 の半分程度の時間でデータ取得が行われています(この例題では記載していませんが、POST 時の応答も高速化されていました)。
TCP FAST OPEN(CURLOPT_TCP_FASTOPEN)の影響があるかどうかどうかを見てみましたが、特に影響はありませんでした。

HTTPS 接続が正常に機能しているサイトではほぼ 3 の設定で高速化される印象ですが、ブラウザ接続した際に「信頼できる運営者情報はありません」等のアラートが出るサイトでは全く変化はありませんでした(証明書の検証を行っていないにも関わらず)。
新しいサーバーでテストする際等には証明書を正しく入れてもらうようにお願いするしかない状況です(libcurl の設定では対処できない)。

今回テストしたのはサーバーからの応答を取得後に別の応答を取得する必要がある場合の処理です。
URLが異なる複数の結果を予め取得しておく場合には libcurl-multi や CURL ハンドラを複数準備して別スレッドで並行して結果を取得すれば容易に高速化することは可能かと思います(スレッドで用いる場合は CURLOPT_NOSIGNAL を指定)。 実行結果

2017年7月15日土曜日

GDによるグラフ描画(最小二乗法による直線近似)

libgd によるグラフ描画の例題です(最小二乗法による直線近似)。

文字列画像の作成 にて用意したフォントを用い、sin関数により生成したデータのグラフを作成しています。

graphInit 関数にてグラフ作成時のオプションを指定していますが、グラフの横幅(graw)、縦幅(grah)、余白(padding)、グラフ描画オプション(DrawType BASE_UNDER:最小値を基準としてグラフを作成する BASE_ORIGIN:原点を基準としてグラフを作成する)等を指定できます。
genData の type にて最大値や最小値が異なるデータを生成していますが、グラフは枠内に収まるよう自動的にスケーリングして描画され、縦横軸のラベルもそれに応じて描画されます。

データのグラフ化のついでに、最小二乗法による直線近似も行っています。グラフ化に用いるデータから傾き、切片を計算し、近似する直線を描いています。 実行結果 genData(type=0)にて原点を基準にして作成したグラフ

2017年7月2日日曜日

フォントを指定して文字列画像の作成(libgd)

libgd を利用しフォントを指定して文字列画像を作成する例題です。

OpenType のフォントが Google から配布されていますのでそれをダウンロードし、実行ファイルと同じフォルダ内に保存します(例として NotoSerifCJKjp-[weight].otf を利用)。

libgd のインストールはこのような感じです(OSX、freetype や libjpeg/libpng も予めインストール)。 例題では、余計な余白や画像サイズを手入力で計算する必要がないように、予め文字列の描画に必要な領域を計算しておき画像のサイズを決定しています。
複数行にて表示していますが、行間を広げたい場合等には一行の高さを gdImageStringFT にて予め計算しておき、その高さ+行間をインクリメントして行数分の表示を行います。 実行結果

2017年6月22日木曜日

libasound によりPCM音声録音

libasound によるPCM音声録音の例題です。

Linux や Raspberry にてコマンドラインからマイクに接続し音声を WAVE ファイルとして保存します。
libasound がインストールされていない場合、下記のコマンドによりインストールしてください。 また、例題のような自分で録音用のコーディングをしなくてもツール群が提供されています。 alsa-utils に含まれる arecord コマンドにて録音、aplay コマンドにより再生が可能です。
この arecord コマンドにより、PC にマイク機器が接続されているかを確認することができます。 表示される「カード 1」が例題にて使用するマイク番号で、使用するマイクの記述方法は「hw:1」や「plughw:1」と言う形式になります。
「hw:1」を利用するとマイクからの入力がそのまま録音されるので 44100Hz 等のサンプリングレートを変えることはできませんが、「plughw:1」を利用するとソース上からダウンミックスした形でサンプリングレートを変更することができます(標準は 8000Hz で 他のサンプリングレートも指定できます)。

例題では、使用するマイクの番号、サンプリングレート、保存する秒数を指定して録音を開始し、ファイル名「record_マイク番号.wav」にて保存しています。
実行結果

2017年6月21日水曜日

WAVE/PCM 音声の生成

WAVE/PCM 音声を生成する例題です。

WAVE 音声に関して以前まとめたものはこちら(WAVEファイルの読込み/再生

WAVE 音声のフォーマットに合わせてヘッダを書き込み(2ch, 44100Hz)、sin 波形の振幅を徐々に大きくした音声を生成しています。 実行結果(Audacity にて表示)

ローカルネットワークから NetBios 名の取得

ローカルネットワークから Windows および Samba でのファイル共有を行っているマシンの NetBios 名を取得する例題です。

例題では、自機のIPアドレスからスキャンを行うIPアドレスの範囲を設定し、それぞれのアドレスに対して NetBios 名を取得しています。
NetBios 名を解決するための手順を省略している部分もあるため上手く取得できない場合もありますが、Windows マシンでは概ね正常に動作しています(Samba のネームサーバー/nbmd に対して取得できない場合も)。 実行結果

2017年6月20日火曜日

CGI に対応した HTTP リクエストによるファイル保存

CGI に対応した HTTP リクエストによるファイル保存の例題です。

ソケット通信を用いて HTTP リクエストを行う場合、レスポンスヘッダに含まれる Content-Length が 0 となり正しく本文が取得できない場合があります(libcurl 等を用いるとその辺の処理は自動的に行ってくれます)。
Apache サーバーに index.html 等のファイルを直接要求した場合にはそのようなことはありませんが、リクエストしたページが CGI で構成されている場合にそういう状況が発生します。
CGI はファイルの読み出しやページの生成の都合に応じて、逐次転送を行いますので、Content-Length の代わりにこれから送信するバイト数を16進数字にて送信しその本文を送信した後、転送が終われば 0 の文字列を送信します。

CGIによるファイル送信の流れ
レスポンスヘッダ
9bd9
[9bd9バイト分のデータ]
e000
[e000バイト分のデータ]
0
例題では、HTTP リクエストを送信後 Content-Length が含まれるかどうかで CGI かどうかを判定し、受信処理により取得された本文をファイルとして保存しています。 実行結果

libjpeg によるカラーバーの作成

libjpeg により JPEG 画像でのカラーバーを作成する例題です。

apt-get や brew(OSX)等により libjpeg をインストール後、libjpeg へのリンク(-ljpeg)を追加しビルドしてください。

例題では、128x32 のサイズの RGB画像が作成され、JPEGファイルとして保存されます。
HDDの使用率等を示すようなカラーバーが作成されます(例題では50%の使用率)。 実行結果(カラーバー:50%の使用率)

NTPサーバーからの時刻取得

NTPサーバーから時刻を取得し表示する例題です。

NTPサーバー(time.asia.apple.com)は適宜変更してください。
実行結果

レーベンシュタイン距離により名前の類似度順に並べ替え

レーベンシュタイン距離により名前の類似度順に並べ替え表示する例題です。

例題では、「佐藤浩市」と言う名前に対する類似度をレーベンシュタイン距離により計算し、その距離に応じて qsort により近い順に並べ替えて表示しています。
実行結果

UNIX名前付きパイプによるスレッド間通信

FIFO(名前付きパイプ)によりスレッドやプロセス間の通信を行う例題です。

FIFO は UNIX 上ではファイルとして作成することができますので、スレッドやプロセス間等様々な状況において簡単に通信を行うことができます。
例題ではソースを分割して用意すると分かりにくくなりますので、main 関数から読み込み用のスレッドに対して文字列を送信すると言う形になっています。
実行結果

2017年6月17日土曜日

重複なしの乱数発生(初期値の指定)/Javascript・Perl版

重複なしの乱数発生 を修正し、100〜200までの値を発生する等、初期値を指定した乱数を発生させる例題です(初期値100、個数100での乱数発生)。

エクセルや既存のサイトのCGI等でそのようなことは可能かもしれませんが、5000個、10000個ともなってくるとスクリプトや従来の重複なしの方法では相当に時間がかかってしまうので、コマンドラインツールとして利用することが主な目的になります。

また、Javascript・Perl版も用意しています。
10000個の乱数発生では、Cで作成した場合には0.1秒、Perlで作成した場合には5秒近くと速度は圧倒的に違いますが、CGI等で利用するための参考として。

randtable.cpp 実行結果 Javascript randtable.pl / Perl

2017年6月2日金曜日

strtok_r によるネスト化されたトークンの切り出し

strtok_r によるネスト化されたトークンの切り出しの例題です。 テキストファイルを読み込み、単語ごとにハッシュ表を作成し、出現する頻度を表示する例題も兼ねています。

strtok 関数はスレッドセーフ化されておらず、再入可能(リエントラント)な利用には向いていません。
strtok 自体が最初の引数で与えられた文字列を変更してしまうためや、内部で確保された文字列の中身を変更してトークンとして切り出しているためです。
while 内に while 文で strtok を呼び出したり、別のスレッドで strtok を呼び出した場合、最後に呼び出された strtok により内部の文字列が変更され、思い通りの結果が得られないと言う状況が発生します。
また、ある関数で strtok を使っていて、その結果から別の関数を呼び出した場合、ソース上からは見えない状況で正しい結果が得られないと言うことにもなり得ます。
strtok_r はこのような状況においても現在扱っている文字列の処理に影響が及ぶことはありません。

以下のソースでは、テキストファイルの内容を全てメモリ内に格納した後、strtok_r にて改行(\n)とスペース( )により単語を抜き出し、ハッシュテーブルへの登録や頻度のカウントを行っています。
loadHashBAD 関数は strtok により処理を行うもので、思い通りの結果が得られていないことが分かります(一行目の単語しか抽出できない)。
loadHash 関数にて strtok_r を利用することにより全ての行の単語が抽出されます。 実行結果(strtok を使用した場合:loadHashBAD) 実行結果(strtok_r を使用した場合:loadHash) file.txt の内容