printf の第二引数がコンパイル時に決まってることなんて、そうそうあるケースだと思えないので最適化なんてしてくれなくていいと思うわけですが…
>いまどきの CPU じゃ,条件分岐とかの負荷に比べたら,除算のコストなんて誤差なんですが
そんなことはないです。
いくら Pen4 とかのパイプラインが深いといっても、
分岐命令が分岐予測を外した場合と div の演算サイクルとは同程度か、
div のほうが長いくらいのはずです。
分岐予測は当たってくれさえすれば、(たいていの分岐命令なら)1サイクル命令相当品なので、
予測をあてやすい分岐かどうかに依存します。
# 出現頻度ベースで言えば普通は分岐の方が除算より圧倒的に多いので、
# 頻度を考慮すれば「負荷」は分岐の方が重いでしょうが、そういう意味でした?
いやー,私の感覚だと,単純で一般的な定数伝播の一種だと思ったので,これくらいは普通にやっておいて欲しかったなーと.
文字列定数を表示するケースってのは,けっこう多い気がします.まぁ,これこそ誤差程度ですし,部分文字列として全部フォーマット文字列にインライン展開されたら,オブジェクトファイルの空間消費量的にも微妙かもしれませんし,トレードオフですけど.
# 当初は 「おぉ,こんな最適化までやってくれるなんてGCCすばらしー」という方向で記事を書き進めていたのですが,予想以上に最適化がかからなかったので,途中から方針を変えたという経緯があったりします.printf はあまりにもよく使われる関数なので,(GCC の変態的イメージだと) かなり細々とした小手先の最適化がいっぱいされてそうなイメージがあったのですが.それほどでもなかったというがっかり感が.
> 文字列定数を表示するケースってのは,けっこう多い気がします
第二引数以降で、ですか? 例えばどういう時に、でしょうか。
あ、あと write はさすがにありえないでしょう… sync だの errno だの考えてくとコードサイズどうなるねんという話でしょうし。
# あと div もなんか書こうと思いつつ調べないと自信持てないにゃーとか
# 思ってたら詳細なコメントが入ってやはりそうかありがたやみたいな
おお,なるほど.ありがとうございます.大変参考になりました.
Pen 4 のパイプラインはとにかくメチャクチャ深いので,ちょっと変なことするとすぐアボーン → 一気に効率低下という偏見がありましたが.DIV ってそんなに遅いんですねぇ.
インテルの命令リファレンスを見たのですが,ちょっと具体的な数値は見つけられませんでしたが… だいたい,ADD を 1 とすると DIV は 40 クロックぐらいなんですかねぇ.そうすると,仮にパイプラインが 20 段として,全部無駄になったとしても,パイプラインの 20 クロック分よりは低いと.なるほど (いや,ハードの仕組みをよくわかってないので,勘で話してますが… すいません)
>> そういう意味でした?
いえ,そこまで考えてません (^-^;
# そういえば,ハード的なプロセッサの分岐予測とかって,どうやってんだろ.全然知らんなぁ…
あー,あんまり多くないか… すんません,またいつものうっかりでした (ハチベエ管理人)
少なくとも,メインルーチンの最内ループとかには出てこないでしょうね.
#define VERSION 2.3
static void print_version() {
printf("version is %d\n", VERSION);
}
みたいなのは,多少はありそうですが… (← 文字列定数じゃないし)
// このケースなら,こうすれば良いし…
#define VERSION "2.3"
#define VERSION_INFO "version is " VERSION
...
puts(VERSION_INFO);
/////
こういうのが,コンパイル時に全部部分評価されるにこしたことはないですが,費用対効果的には微妙ですかね.
>すぐアボーン → 一気に効率低下という偏見
除算が相対的に遅すぎる(比較相手が悪い)ってだけで、
分岐予測を外したペナルティは普通の命令よりは十分大きく、
効率低下するのは偏見では無く概ね事実です ;-)
>具体的な数値
x86系に限れば、
http://www.agner.org/optimize/ が詳しいですね。
乱暴な表現をすると、
・1〜2サイクル圏内
普通の整数演算
L1キャッシュヒットするメモリ操作(mov)命令
分岐予測成功した普通の分岐命令
・数サイクル
乗算命令
普通の浮動小数演算命令(加減乗)
・十数サイクル
L1キャッシュミス、L2キャッシュヒットのメモリ操作命令
分岐予測ミスした普通の分岐命令
・20〜数十サイクル
除算命令(整数、浮動小数)、浮動小数平方根
変な命令の類 (INT 命令とか、ステータスレジスタを書き換えるとか)
・数百サイクル
キャッシュミスして主記憶メモリまでとりにいくメモリ操作命令
くらいです。
> もし,本当に,できる限りのものを言語仕様から追い出して,ライブラリでまかない,言語仕様はできるだけクリーンでコンパクトにしようと思ったら.リテラル表記,データ構造のメモリレイアウト,文法,なにもかもを,全てパラメタライズして,ユーザ定義で変更できるようにする必要があるとおもう.そして,そうなると,「プログラミング言語」 なんていう概念は,事実上消滅する.究極的には,ドメイン特化の仕様記述言語 (= 現在の意味での,ライブラリやフレームワーク) だけが残ることになる.
http://local.joelonsoftware.com/mediawiki/index.php/%28Forum%29_%E7%A7%81%E3%81%AF%E3%81%AA%E3%81%9C%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%8C%E5%AB%8C%E3%81%84%E3%81%8B「汎用ツール構築ファクトリーファクトリーファクトリー」の徹底化でしょうか。自分の目玉をえぐり出したくなりたくはないのですが。
> もちろん,全ステップは等価な変換であるということが証明されていて (こういうのを,運算とかともいうらしいね.知らなかったけど),全ての変換は等価な変換を行うと証明済みのルールによって行われることになる.
安全なレゴプログラミングが可能(であることが証明される)、ということなら画期的ですね。でも
http://local.joelonsoftware.com/mediawiki/index.php/%E3%83%AC%E3%82%B4%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0ソフトウェア開発におけるボトルネックはそこではなさそう。
おお,ありがとうございます.参考になりまくりです.多謝 !!
なるほど,分岐予測できるような,素直な分岐ならば,それほどペナルティにはならないんですねぇ.分岐ミスも,高々キャッシュ中の値拾ってくる程度と (言ってしまえば)
むしろ現在のアーキテクチャだと,クロックに比較してメモリが遅すぎるので,変な最適化のつもりで下手にコードサイズを膨らましてしまうと,もうそれだけで挽回できないくらいの致命的ペナルティになると.メモリ触ったら負けの世界ですね.ましてや HDD 触っちゃったら,もう目も当てられないと.
CPU がどれだけ速くなろうが,メモリがどれだけ大容量になろうが,(いや,x86 みたいな CPU であれば,発達すればするほど) メモリ使用量を抑えるってのは重要であり続けるんですねぇ.
まぁ,最近の CPU は速いから,メモリに収まってるうちは,それほど神経質になる必要はなさそうですが (← ゆとり世代の常套句)
いやー,よくある誤解だと思うのですが,ここらへんは,ライブラリやフレームワークを作る人のためのメタライブラリというかメタフレームワークの話なので.誰も彼もが,イチから車輪を再発明する必要はありませんよ.
出来の良いライブラリやフレームワークを作るためには,何度も試行錯誤する必要があると思います.しかし,今のパラダイムでは,アドホックな理論やコンクリート過ぎる処理系,特定の問題領域に強く依存した非汎用フレームワークばかりで,研究や実験のための理論的・現実的な土台というのは,まだまだ圧倒的に貧弱だと思います.だれもが簡単に,自分の理想の言語仕様やプログラムの最適化手法を実験するような土台が全然整って無くて,研究者たちも,イチから手作りで四苦八苦しているのが現状なのではないかと.
あと,ソフトウェア開発の本質は,プログラムのデバッグなんかではなく,仕様のデバッグ (要求定義の完全性や無矛盾性の検証) だ,の話ですが,確かにそれはその通りです.そして,それはマネージメントやエンジニアリングの領域なので,計算機科学は無力だというのも,また事実だと思います.
しかし,現状,非本質さのほうの足枷も,無視出来ないくらい大きいと思いますよ.プログラマの思考の大部分は,問題の本質を考えるほうよりも,出来の悪いライブラリや言語を乗りこなすための労力に割かれていると思います.
もちろん,抽象化のほつれをゼロにすることは不可能だと思いますが,まだまだたくさん理論的に改善できる部分は多いと思いますね.
あと,いちおう,ここでの話は,1 年後 2 年後の話ではなく,100 年後の理想というか… まぁ,無責任な青二才の戯言と言われてしまえばそこまでなんですが… (^-^;
> いやー,よくある誤解だと思うのですが,ここらへんは,ライブラリやフレームワークを作る人のためのメタライブラリというかメタフレームワークの話なので.誰も彼もが,イチから車輪を再発明する必要はありませんよ
リンク先「私はなぜフレームワークが嫌いか」は正におっしゃられるようなメタ^nフレームワーク(に対する皮肉)の話と読んでいるので、そのあたりは誤解はしていないかと。だから「徹底化」をつけました。
> 研究や実験のための理論的・現実的な土台というのは,まだまだ圧倒的に貧弱だと思います.
貧弱というより、Intel,MS,unix の寡占状態が、より一般的なことを研究するのに必要な多様性を失わせているのではないでしょうか。
> だれもが簡単に,自分の理想の言語仕様やプログラムの最適化手法を実験するような土台が全然整って無くて,研究者たちも,イチから手作りで四苦八苦しているのが現状なのではないかと.
15〜30年ほど前は、まだ比較的手作りがしやすい環境でした。今はスケール拡大による複雑化と上記の寡占のため手作りそのものが厄介さをましています(言語はまだいいほうでしょう、ハードは…)。だから土台が整っていないというより、土台をいかにスケールアウトするかが重要なのではないでしょうか(そういうことをやろうとしてるのだと思うのですが)
> しかし,現状,非本質さのほうの足枷も,無視出来ないくらい大きいと思いますよ.プログラマの思考の大部分は,問題の本質を考えるほうよりも,出来の悪いライブラリや言語を乗りこなすための労力に割かれていると思います.
非本質さのほうの足枷(プログラムのデバッグ、出来の悪いライブラリや言語)が本質的なほうの問題に由来することがありますよね。むしろそのほうが多いし、より時間がかかるではないでしょうか。独立した問題じゃないのでは…と言ってしまうと計算機科学の立ち位置がなくなりますかね?
> あと,いちおう,ここでの話は,1 年後 2 年後の話ではなく,100 年後の理想というか…
それはわかっているつもりです。数学・物理学のようにスケールアウトする形式化を成し遂げることは、意味があることだと思います。ただ「ソフトウェア構築で困難な部分は、この概念的な構成物の仕様を作り、デザインし、テストすること」と形式化が本当に独立した問題なのかが気にかかるのです。
>> そのあたりは誤解はしていないかと。
>> それはわかっているつもりです。
すいません,今見ると,mal さんがどこまで認識して話しているのか,ということを掴みかね,かつ
(僕の中で) 過去に何度も見かけて記事を書いてきたトピックだと,少々うんざりぎみだったので,かなり
感情的に答えてしまってますね.しかし,どうやら釈迦に説法だったようで,汗顔の至りです m(_ _)m
>> メタ^nフレームワーク(に対する皮肉)の話
これは単に,簡単なことは簡単に,の原則を忘れているだけの話という感じがします.従来のやり方で
上手く行っているのならば,無理にそれ以上「先進的」な手法を取り入れる必要は無いのではないかと.
ただまぁ,業界全体のレベルが低く,技術水準が低いから,必要とされる技術も単純で簡単なもので十分になる.
裏を返せば,新しい技術が導入されれば,もっとクオリティの高い製品なりサービスを,より低コストで提供できる可能性
があるにもかかわらず,発展が停滞してしまっている,という可能性もあるので,一概に 「ハンマーで十分」とも言えないのが難しいところですが.
また,
>> 貧弱というより、Intel,MS,unix の寡占状態が、より一般的なことを研究するのに必要な多様性を失わせているのではないでしょうか。
というのも大きいですよね.新しい技術を導入しようにも,過去の制約 (老害) が大きすぎて思い切った改革
はできないという現状があると思います.
Web 系のプログラミングなんかもそうですよね.ただまぁ,基幹インフラを変えるのはほぼ不可能なのが
難しいところですが.
>> ただ「ソフトウェア構築で困難な部分は、この概念的な構成物の仕様を作り、デザインし、
テストすること」と形式化が本当に独立した問題なのかが気にかかるのです。
抽象化とは単純化なので,その仮定で失われるもの,洩れるものは必ずあります.
しかし,凡人は,複雑性を複雑なままで扱うことはできません.メタファクトリに対する皮肉同様,決め打ち
による単純化と,失われる柔軟性のトレードオフの関係ですよね.
また,安易に他分野の理論を借りてくるだけでは不十分で,計算機科学には計算機科学の,体系的な理論
が必要だと思っています.
# 建築のメタファはソフトウェア開発の本質をとらえる上では,もはや有害だ,と言ったのは誰だったかな…
目に見えて,検証が容易な物理的実在である建築物と,目に見えない抽象物であるソフトウェアは全くの別
物というのは正しい認識だと思います.ただ,物理的なハードウェアも絡んでくるので,一概にどっちとも言
えないというのも,計算機科学の本質的困難さの一つなのですが.
現在の技術の姿は,必然性よりも歴史的経緯の方が支配的です.莫大な量の歴史的資産 --- ある意味
では足枷だらけで身動きが取れない --- が積み重なるにつれ,Brooks の時代よりも,はるかに非本質の
比率が高まっているというのもまた真実だと思います.
現在のシステム開発では,仕様を作る際,仕様の本質よりも,既存技術のパラダイムに合わせて,むしろ
(意識的・無意識的に) 仕様の方をねじ曲げて,無理矢理従来的なやり方の押し込めようと不毛な努力が費
されているような場面も多いと思います.
# もちろん,適度かつ自然な制限 (思考の枠組) は,むしろ想像力を膨らませる上で非常に重要な道標 (コ
ンパス) になります.ただ,現在の技術の在り方に,そのような必然性があるかと言われると,私的にはか
なり疑問が残るところです.
私の立ち位置を明確にしておくと,私はもともとは AI 系の研究を志して大学に入ったのですが,その過程で
現在のプログラミング技術の貧弱さに危機感を感じ,まずは土台からしっかり固めていかないと駄目だなぁ
と思いたち,現在に至るという経緯があります.
なので,私が頭の中に思い描いている 「困難な問題」 というのは,いわゆる AI 系の,複雑かつ大規模で,
アルゴリズムなどが不明確な問題です.既に解き方がわかっていて,フレームワークが存在するような問題
領域は,それほど意識していません.だから,「〜 を 10 分で書ける」 的な能力は正直どうでも良いと.
そういう問題を解くためには,何度もプロトタイプを繰り返し,試行錯誤を行う必要があります.必然的に,
記述には高い抽象度から泥くさい部分までを広範にカバーできることが求められますし,かといって,いわ
ゆる従来的な超高級言語というものは,さまざまな抽象化の上に成り立っているものなので,必然的になに
かしらの制約が多くなってくるし,処理効率も十分では無い.
また,解き方がわからないのだから,従来的な意味でのライブラリやフレームワークの充実度は,プログラ
ミング言語を選ぶ上では,それほどアドバンテッジにはならないという前提があります.
# なので,いわゆる職業プログラマに求められるような意味での 「実用性」 の重要度は,それほど高くないという想定があります.
というわけで,抽象的かつ精密な仕様記述手法という枠組から,現実の計算機に即した処理効率の高いプ
ログラムを自動生成するという方向に自然に至ると思います.
そしてそのためには (ハードウェアはよくわかりませんが) 仕様から,現実の計算機の仕様までもがすべて
形式的に記述され,自動導出によって厳密に仕様の意味を保存したプログラムが生成されなければなりま
せん.そして,それぞれのステップは (あいまいですが) 自然かつ透過的でなければいけないと思います
(ブラックボックスをできる限り排除して,すべて明確な世界に持ってくる)
# くどいですが,人間の直感ではどうしようもないようなレベルのシステム構築を念頭においてます.たとえ
ば Joel が相手にしているような,銀行のオンラインシステムとか電子カルテみたいな従来的なシステム構
築は,また別のやり方やプログラミング言語やライブラリフレームワークが必要とされるのでしょうね.
そうなって初めて,人間は非本質から開放され,仕様のデバグに専念し,さらなる高みを目指していけるよ
うになれるのではないかと.
まぁ,私は何の実績も無い,不真面目な青二才院生に過ぎないのですがね.
脈絡が無く,飛躍だらけの乱文長文すいませんでした.
とかなんとか言っておきながらも,建前はともかくとして,計算機が面白いからただ遊んでるだけっちゃそう
なんですけどね (笑)
ちなみに,wo さんのプログラマブルプログラムというネタは非常に面白いと思います (結局また wo さん)
http://d.hatena.ne.jp/w_o/comment?date=20051104#c『そうです!バイナリフォーマットなんかに縛られてはいかんのですよ。
プログラムはまず、実行ファイルをロードするプログラムを実行するインタプリタの初期化から始まるべきです!!』
(本当は,このネタを紹介してさらっと終わらせるはずだったのに,なんでこんな長文になっちゃったんだろう… 疲れたので,あんまり推敲しないで上げます)
>shinhさん、あろはさん
つ inline関数+sprintf
ってことで*printfの引数は折り畳んで欲しいですね。
おー,なるほど.
sprintf 折り畳んで,単なる文字列定数にまでしといてくれたら,なかなか感動ですよね.
個人的には,フォーマット文字列の解釈のオーバヘッドが気持ち悪いんですよね.
だから,末尾に改行を含めたくないとき,単なる文字列を printf("hogehoge") するたびに心が痛んだりします (病気)
printf("文字列定数") は,全部
putchar('文');
putchar('字');
putchar('列');
putchar('定');
putchar('数');
ぐらいやって欲しいかも (いや,これじゃ逆に遅くなるだけかもしれないけど.気持ち的に)
> すいません,今見ると,mal さんがどこまで認識して話しているのか,ということを掴みかね,かつ
(僕の中で) 過去に何度も見かけて記事を書いてきたトピックだと,少々うんざりぎみだったので,かなり
感情的に答えてしまってますね.しかし,どうやら釈迦に説法だったようで,汗顔の至りです m(_ _)m
こちらこそすいません。短い文とリンクでは背景を読み取ってくれというのは無理ですね。1年半ほどここを興味深く読んでます。確かに何度も繰り返されたトピックなんですが、根底にある急所を捉えているリンク先だと思いました。特殊な読み筋なのかもしれませんが、リンク先のフレームワークやレゴプロミングを特定ドメインに対する問題ではなく、むしろ抽象化の必然的な問題として読んでいたもので。
今回も繰り返されたトピックを読んで思うのですが、目的と手段(というより手段による帰結)がうまくかみ合ってないように思えるのです。
> 私が頭の中に思い描いている 「困難な問題」 というのは,いわゆる AI 系の,複雑かつ大規模で,
アルゴリズムなどが不明確な問題です.
既存のアルゴリズム(抽象化・最適化手法)がなく、人間による抽象化も当てに出来ない複雑で大規模な問題に対応する、という目的に対して
> というわけで,抽象的かつ精密な仕様記述手法という枠組から,現実の計算機に即した処理効率の高いプ
ログラムを自動生成するという方向に自然に至ると思います.
(自然に到るとは思えません。ここに飛躍がある気がします)
ハードウェアレベルまでシステム構成を形式化することは、歴史的資産による混乱を整理するのには有効でしょう。しかし応用というか現実レベルで OS,VM,高級言語,フレームワークやライブラリが中途半端ながらもやってきたことでもあります。その帰結が「汎用ツール構築ファクトリー^n」やレゴプログラミングではないでしょうか。そしてプログラム自動生成はあくまでこの流れの理論化、徹底化に位置するものでは?
しかし、歴史的資産の形式化が、解き方もわからない複雑で大規模な問題の直接的な解決に繋がるとは思えません。むしろ「概念的な構成物の仕様を作り、デザインし、テストすること」が必要なのでは?
(shiro さんによる Lisper の戦略「ある構文や意味が常に単純で直交した最小限の規則によって記述されること」の方が、むしろ直接的なアプローチに見えるのですが。
<
http://practical-scheme.net/wiliki/wiliki.cgi?Lisp%3aS%e5%bc%8f%e3%81%ae%e7%90%86%e7%94%b1>)
単に、歴史的資産の形式化によって「概念的な構成物の…」以前の部分にかかわる労力を低減するということをおっしゃりたいのならそれはよくわかります。ただそれはメタ^nフレームワークの進化型であって、本来の目的とは別物じゃないか?と思うわけです。
この目的と手段のズレが、私の誤解でしかないのなら、誤解を解いていただけると嬉しいんですが。
一応前に書いたこととズレがあるので補足しておきます。妄想段階ですが。
メタ^nフレームワークの進化型=プログラム合成 によって「解き方もわからない複雑で大規模な問題」が解決するのではなく、「概念的な構成物の…」の関わる問題が、メタ^nフレームワークの進化型をより複雑で大規模にしてしまうのではないか、と考えています。
おまけ:
> 『そうです!バイナリフォーマットなんかに縛られてはいかんのですよ。
プログラムはまず、実行ファイルをロードするプログラムを実行するインタプリタの初期化から始まるべきです!!』
んー Bootstrapping にしか見えん…
<
http://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29>
プログラミングとは,「人間の頭の中の概念構造物を,現実の計算機で実行可能な形で表現して実行し,問題を解決すること」と定義するならば,「できる限り高い抽象度の仕様記述」を等価変換し続け 「完全に意味が保存されたプログラム」を自動生成するという枠組みを作るということは,これ以上無いほど直接的なアプローチだと個人的には思っているのですが,いかがでしょうか ?
現実の計算機の形式化は,数学的な理論体系に工学的な現実を載せるための一手段ですね.
「Lisper の戦略」も,目指しているところは同じなのでしょうね (ただ,私には,Lisper のやり方は,体系的なアプローチというよりも,個々のプログラマの力量に大きく依存した職人芸に見えますが)
しかし,私は,マクロ展開を言語に組み込む必然性は感じませんし,全てを S 式で書かなければならない必然性も感じません.S 式が万人に対して読み書きがしやすいか,ということは否定的に一定のコンセンサスを得ていると思います.また,Lisper の大部分は,S 式はプログラミング変換に対して有利であるから使っている (という (だけ) のことに過ぎない) と思います.
仕様記述は,パーサによって内部的に扱い易い S 式に変換されれば良いし,その S 式の操作や変換,計算機で実行可能な形にマップする際のデータ構造のレイアウトをどうするかなども,また別のステージで段階的に解決して行くのが望ましい (言語に組み込んでしまうと柔軟性が失われる) と考えております.
もちろん,これはアプローチの一つにすぎませんし,「そんなのは間違っている,別のやり方の方が良い」という方々もたくさんいるでしょうね.
もちろん,例えば「論理式による仕様記述手法」が本当に人間の頭の中の概念構造物の表現に向いているのか ? などの,部分部分の妥当性については,それぞれ議論の余地が大量にあると思います.数学というよりも,デザインの領域の問題もたくさんあるでしょう.ただ,全体的な流れと言うか,大きなパラダイムとしては,それなりに良い線なんじゃないなかーと,現時点では思ってます.もちろん,今後もっと良いやり方だと思うパラダイムが見つかったら,いつでも乗り換えますけどね.
wo さんの話などは,OS とかバイナリフォーマットとか,そういう 「プログラム」として実現されている部分は,せっかくプログラムとして可変なんだから,全部後から再定義できて 「プログラマブル」であるべきだよね,ということだと思います.
従来的には,OS やプログラミング言語処理系などは全部一枚岩として書かれることが多かったと思いますが,それは一気にカスタマイズの敷居を上げます.例えば GCC や Linux Kernel をハックしようと思っても,それはごく一部のスーパハッカ以外には厳しいでしょうね.それが,研究や教育の敷居を上げ,生産性を低下させている要因なのではないかと.
例えば OS Kit や COINS のように,それらが全部ライブラリ (部品) の連結でできていて,誰でもレゴブロックのように :-) 自由にカスタマイズできたらステキだよね ☆ という.
ただし,そういうアプローチは従来もあったのですが
証明付き拡張可能コンパイラ
http://d.hatena.ne.jp/wpw/20050902/1125665297>> ただ、何か一つ新しい解析を入れると、コンパイラに生じる変更は大抵モジュール間を横断するはず。例えば、type-based な escape analysis を OCaml に入れるとしたら、型の定義を変えなくちゃいけないだろうし、メモリ管理も変えなくちゃいけないだろうし、結構イライラすることになるんじゃなかろうか。
できるだけ硬直化した部分を排除した,体系的なプログラム合成は,こういう問題に対して,一つの解答を与えるのではないかと思ってます.AOP や DI なども (アドホックながらも) 目指すところは同じなのではないかと (ちなみに,僕は AOP も DI も良く知りませんが)
あと,もちろん遠い将来的には,ハードウェアさえも全部プログラマブルになって,仕様を最も効率的に実行できるハードウェアが全部自動生成されたら最高だと思います.
うーん想像力なり経験が足りないのだと思うのですが、コンパイル時定数になる inline 関数の結果を sprintf の第三引数以降に渡すようなケースってそうそうあるのでしょうか… > n さん。要は strcpy になれば嬉しいって話ですよね。うーん。
あ、あと sprintf の引数を折り畳むのはわかるのですが、 sprintf を全部そのまま折り畳んじゃうとプログラムの意味変わりすぎですよね… > あろはさん
ふーむ,確かに.
まぁ,どうしても副作用が多い C みたいな言語だと,簡約が単純にはいかなくなってしまうってことですかね.単純な置き換えモデルの意味論に脳が毒されてると,(実用性はともかく) 何でもかんでもちゃんと置き換わってくれてないとキモチワルイという.
というのは言い訳で,僕の場合は単にボケてただけですね > 全部折り畳んじゃうと
文字列定数の strcpy から,ゆくゆくは単なる整数定数の代入になるから,文字列定数を渡してしまっても良い,と安易に考えたんですが.当たり前の話で,それだとバッファを書き換えられなくなっちゃうから全然駄目ですね.すいません.
ちょっと試して見たら.
#include <stdio.h>
#include <string.h>
int main() {
char buf[100];
strcpy(buf, "hogehogefugafuga");
buf[5] = 'a';
printf("%s\n", buf);
}
このぐらいの,ちょー単純なコードだと,単なる代入文と puts(buf) 一文にまで落ちるみたいですね.さすがに副作用が途中で入るので, puts("hogehagefugafuga") まではいきませんが.
movl $1701277544, -116(%ebp)
leal -120(%ebp), %eax
movl $1701277544, -120(%ebp)
movl $1634170214, -112(%ebp)
movl $1634170214, -108(%ebp)
movb $0, -104(%ebp)
movb $97, -115(%ebp)
movl %eax, (%esp)
call _puts
/*
*((int *)&buf[0]) = 'hoge';
*((int *)&buf[4]) = 'hage';
*((int *)&buf[8]) = 'fuga';
*((int *)&buf[12]) = 'fuga';
buf[16] = '\0'
buf[5] = 'a'
puts(buf);
*/
… 今時 strcpy なんて使わないですが (strncpy とかは境界チェックが入るので無理)
あ,sprintf も,第三引数以降に何も無ければ,puts いっこに落ちてますね.
#include <stdio.h>
int main() {
char buf[100];
sprintf(buf, "hogehogefugafuga");
buf[5] = 'a';
printf("%s\n", buf);
}
上と,ほぼ同等のコードに落ちます.
movl $1701277544, -116(%ebp)
leal -120(%ebp), %eax
movl $1701277544, -120(%ebp)
movl $1634170214, -112(%ebp)
movl $1634170214, -108(%ebp)
movb $0, -104(%ebp)
movb $97, -115(%ebp)
movl %eax, (%esp)
call _puts
「プログラムは思ったとおりに動くんじゃない、書いたとおりに動くんだ」
よく聞く格言ですが、重い言葉ですね。
> プログラミングとは,「人間の頭の中の概念構造物を,現実の計算機で実行可能な形で表現して実行し,問題を解決すること」と定義するならば,「できる限り高い抽象度の仕様記述」を等価変換し続け「完全に意味が保存されたプログラム」を自動生成するという枠組みを作るということは,これ以上無いほど直接的なアプローチだと個人的には思っているのですが,いかがでしょうか ?
プログラミングの定義があいまいかも。
A: 「人間の頭の中の概念構成物」-> 「思ったこと」
B: 「現実の計算機で実行可能な形で表現し」-> 「書いたこと」
A と B が等価ならば、挙げられたプログラミングの定義から等価交換によるプログラム自動生成は自然ですね。しかし等価であれば上のような格言は生まれません(プログラマの仕事自体ありませんな^^)
A と B の差異が暗黙知(暗黙の前提、背景、常識、環境、etc...)と呼ばれるものではないでしょうか。
細かく考えると、
Xa: 意識的に考えたこと
Fa: 意識的に考えたことの暗黙知・背景
Fa(Xa): 思ったことの総体・全体としての意味
Yb: プログラム・明示された仕様
Fb: 計算機の実行環境
Fb(Yb): 実行されるプログラム
Fa(Xa) -> Fb(Yb)
: 考えたことの暗黙知を含めた総体を、計算機で実行可能なプログラムに変換する。
になるんじゃないでしょうか。
Fa(Xa) と Fb(Yb) は(ちょっと疑念がありますが)等価になるとおもいます。しかし 等価変換するためには Fa(暗黙知) を何とかしなきゃなりませんが、あろはさんはそこを無視して Yb, Fb を弄りまわしているように見えます。それでは Fa(Xa) が確定した問題(従来のフレームワークなどの領域)しか扱えません。元々の目的である、「複雑かつ大規模でアルゴリズムなどが不明確な問題」は Fa(Xa),特に Fa が不明確な問題ではないでしょうか。
Joel(というより Brooks?) の主張は、Fa を Xa に変換する -- それも効率的でスケールアウトする形にデザインして、テストにより再変換する -- ことが大事であり困難だといっているように見えます。
Lisper は S式やマクロを駆使することで比較的容易に Fa,Xa の対を Yb に動的に(ある意味 Adhoc に)変換していきます。それが容易なのは、たぶん Yb の理論的な構成が S式に近いことと、マクロにより Yb を Xb に近い形に保ちやすいというのがひとつにはあるかと。Lisper じゃなくても別の形でやってることだとは思いますが。
で、あろはさんは Fa(暗黙知) の取り扱いについて等価交換パラダイムで対処する方法を特に言ってないと思うんです。「できる限り高い抽象度の仕様記述」に類したことは何度も言っていますが、これは Xa と Yb の変換フォーマットでしょう。見落としていたなら申し訳ないですが教えてください。
> もちろん,これはアプローチの一つにすぎませんし,「そんなのは間違っている,別のやり方の方が良い」という方々もたくさんいるでしょうね.
ではなく、Fa(暗黙知) の扱い、変換方法をどうするかという難点を見落しているのでは?と言いたい訳です。Fa(暗黙知) の扱いは AI の本流でそれなりに研究されてると思います(静的に学習データや常識を与えるか、動的な試行錯誤の仕組みを取り入れるかなどいろいろアプローチはありますが)し、それがある程度行き詰まりがあるとも思いますが、あろはさんのやり方は Fa(暗黙知)の問題で AI 研究の進展に依存しているとはいえませんか?
> できるだけ硬直化した部分を排除した,体系的なプログラム合成は,こういう問題に対して,一つの解答を与えるのではないかと思ってます.
そうだと思います。 Yb,Fb をより動的にしたうえで、いかに一貫性を与えるかについては、プログラム合成は有効な手段だと思います。しかし本来の目的とは別問題ですよね?
ひとまず Adhoc ですが書きました。一応自分の言っていることが、一歩トンデモに足を踏み入れてるかなぁという感覚もあります。できるだけ変なことは書かないように気をつけたつもりですが、あまりおかしなことを言っているのでしたら、どうか無視してください。
Fortran の John Backus が亡くなったそうで…
Fortran (高級言語) と FP (関数プログラミング) という,全く異なる 2 分野をゼロから築きあげた巨人の死.私は仏教徒なので,まことに勝手ですが心より冥福を祈らせていただきます (こういうのは気持ちの問題だと思うので.むやみに形式に拘る人の気が知れません.キリスト教も仏教も,死者を追悼する気持ちは同じはず)
すいません.今ちょっと時間が無いので,手短に脊髄反射コメントだけ書いておきます.
以下,話が飛び飛びになっていてすいません.
# 後で,もう少し付け足します.
人間の頭の中は,自分自身すらよくわかってないほど混沌としているのが普通です.
ポール・グレアムは,Lisp のように柔軟な油絵の具のような言語を使って,それをスケッチするのがプログラミングだ,と言いました.
表現方法というのは重要で,逆に表記法により学問は進歩するとさえ言えます.数学などは特に顕著でしょう.プログラミングも大きいと思います.
スケッチのための道具としては,私たちの分野では,論理式による表現を使うのが普通です.
論理学というものは,人間の頭の中の,混沌とした論理を,絶対確実な領域に引きずりだすための,最も伝統的でかつ強力な手法だからです.
ちょっとこれ以上精密かつ表現力に富んだ汎用の表現手法は考え付きません (もちろん,問題領域に依存した,特定の DSL は,さまざまなものが考えられますが.逆に,そういう表記法がある程度確立されているような分野は,既に解かれた,比較的枯れた分野とも言えます)
具体的には,論理式から,意味を保存した操作 (メタ計算として,理論的に定式化されています) により,汎用的なルール (Nondeterministic Rule ; N ルール,頭部に複数の制約を記述でき,複数の分岐とデフォルトで並列計算の意味論を持つ置き換えルール) を生成し,それを段階的に変換していき,実行可能なルールを得る,という枠組みになってます.現在の計算機の大部分は決定的かつ逐次的に処理が行われるので,N ルールは D ルール (Deterministic ルール,アトムを置き換える,単純な展開ルール) に変換され,最終的には機械語になります (ここらへんの,低水準のところは,まだあまり理論も実装も進んでなくて,研究中のところなのですが).
基本的には,パターンマッチに成功し,条件を満たした lhs (left-hand side) を rhs に置き換える,というルールの集合がプログラムとなります.
Nrule
H1, H2, H3, ...
{condition 1} ==> {specialize 1}, Bs1;
{condition 2} ==> {specialize 2}, Bs2;
...
{condition n} ==> {specialize n}, Bsn.
Drule
Head_atom, {発火条件} --> Body_atoms.
プログラムの意味は,いわゆる論理プログラムの最小浮動点モデルを一般化したような形で与えられています.その他にも,論理プログラミングの枠組みを,特殊化システムにより,一階の項以外の,汎用のデータ構造を扱えるように拡張・一般化されてます.
という,理論面の話は切りが無いので,ここらへんにして.
なぜコンパイラができないような最適化を,人間はできるのか ?
それは,暗黙の,ソースコード上には表現されていない仕様と,ハードウェアなどに対する知識を,人間は持っているからだと思います.
従来の,プログラムの自動合成の研究が夢物語で終わっていたのは,理論的・技術的・認識的な未成熟さと共に,このような暗黙知を真面目に形式化して,機械が扱えるようにする試みが不十分であったから,というのが大きいと思います.
もちろん,どこまで行っても,プログラミングは最も人間的かつクリエイティブな行動であることは間違い無いでしょう.
しかし,いつまでも,何から何まで人間の能力と学習に頼る職人芸ではいけないと思います.そもそも,コンピュータが最も得意なことは,複雑かつ大規模な処理を高速でやることなのですから,人間の暗黙知を機械が扱えるようにすればするほど,人間の負担は減っていき,より想像的な方面に能力を使うことができるようになっていくと思います.
そのブートストラップにより,あるいはある地点から急速に AI の発展が進むと言うことも期待できます (トンデモ) AI 研究は,不十分な理論,不十分な技術,不十分なハードウェアとい不遇の時代の中で,一時期過剰に期待され,また一気に熱が冷めた後は,長い長い冬の時代を送っているのが現状ですが.
私は,ここらへんの,やるべきことを正しい方向に向かって真面目にやることこそが,現在の計算機プログラミングにおける諸問題を解決するための,逆説的ですが,唯一の銀の玉だと思っております.
# もちろん,現実はそれほど単純ではないでしょうが.例えば,大量に人を雇って,低い技術力でダラダラ作っている方が,人月単価の企業は儲かるとか.企業は,金さえ儲かれば良いので,かならずしも技術は求めてられていない.むしろ新しい技術により人員が余ると,深刻なリストラが起こることにもなりかねません.
# とはいえ,これだけ計算機システムの社会的重要性が増しながらも,いつまでも技術力が貧弱なままの砂上の楼閣の上で暮らすのは,正直ゾっとしませんが.いつオペ中に精密医療機器のバグが発現するか,電車のブレーキがイカれるか,エレベータが落ちるかわからない社会.テストなんてのは,弱い安全性と品質の保証に過ぎません.ちょっと変な使い方をしたらどうなるか,誰も証明はしてないわけですから.常にユーザがテストしているのと同じ.
今日の教訓
誰も使用しない機能がまともにテストされていると思うことなかれ。
http://www.nminoru.jp/~nminoru/diary/2007/03.htmlとまぁ,こちらこそ勢いで殴り書きのトンデモだらけですいません.たぶん質問にちゃんと答えきれていないと思うので,後でもう一度質問を読み返して,足りないところを補足します.
あと,具体的な細かいところや,それが本当に成功するのか ? なんかは,私にもわかりませんし,日々不安だらけですよ.わからないし誰もやってないからこそ研究しているわけですし,日々もっと良い方法は無いか,とサーベイしたり,疑問を検証しているわけなので.毎日,もう少し楽で堅実な道もあったんじゃないのかなー と思ってますし (思ってもしかたないのですが).結局,学位も取れない,あるいは一生無駄でしたね,人生オワタということも有り得ると.しかし.
「そうだな.私は結果だけを求めてはいない.結果だけを求めると人は近道をしてしまうものだ.近道をしたときに真実を見失ってしまうかもしれない.やる気も次第に失せていく.
私は大切なのは『真実に向かおうとする意志』だと思っている.真実に向かおうとする意志さえあれば,たとえ今回は完成しなかったとしても,いつかはたどり着けるのではないか? 向かっているのだから.違うかい?」
「そして真実に『向かおうとする意志』は,あとの者たちが感じとってくれているさ.」
荒木飛呂彦,ジョジョの奇妙な冒険,vol.59,「今にも落ちて来そうな空の下で」
たぶん,ダイクストラ先生か Don Knuth の言葉だったと思うのですが,優れたプログラマは,抽象化がどれだけ進んだとしても,一見,下部のことは完全に忘れてしまってかまわないように見えたとしても,無意識の領域では,常にそこに対する注意を怠らないで,目を光らせているものだ,みたいな格言があったと思います.
# う〜ん,分割統治法の (Pascal の) ニクラウス・ヴィルトの,「アルゴリズム + データ構造 = プログラム」 あたりでの言葉だったかもしれない… それともカーニハン博士かリッチー博士あたりだったかも.誰だったかなぁ
つまり,人間の判断には,(少なくとも,優れたプログラマの判断には) 必ず合理的な理由がある (あるいは,付けることができる).
完全な AI は,おそらくあと数百年ぐらいは無理だと思います.しかし,狭義のプログラミング (= 人間と機械の間のコミュニケーション,インタフェース,意志疎通.より広範な,業務に関する知識や人間社会に対する一般常識とかは含まない) ならば,完全に全て形式化し,人間とコンピュータの間の暗黙知のギャップを埋めることは可能だと思います.
もちろん,大部分は NP 問題なので,完全解は不可能ですが.人間のプログラマが構成できる程度の精度までならば,なんとか可能な気もします.
というわけで,暗黙知というのは,少なくとも計算機の世界の中のものに限れば,埋めることは可能だと思います.抽象化が洩れることが防げないのであれば,できるだけ暗黙知を明示的に機械が処理できる領域に乗せる努力が重要だと思います.
そして,そういう努力は,これまでほとんどされてこなかった (あるいは,誰も思い付かなかった,思い付いてもやろうとしなかった) のではないかと.
私は,プログラミングこそが,AI の技術 (知識処理 (自動推論,演繹) 技術) が最も上手くフィットし,大きな成果を出せる可能性を秘めた領域だと思ってます.
# プログラミングの技術が AI 技術に依存し,AI 技術がプログラミングの技術に依存するので,一筋縄にはうまくいかないとは思いますが.
なんか大きな議論になってるとこから一気にベタなところに戻しますが。
CommonLisp処理系の多くは、formatの文字列が定数ならコンパイル時に「format文字列をコンパイル」
します。つまりformat文字列を解釈して行われるであろう動作を直接コードとして吐き出します。
前にコメントしたcompiler-macroを使ってるんで、デフォルトの最適化に不満があれば
自分で最適化するコードを書いてオーバライドできます。
Allegro CLに書いた正規表現ライブラリでも、正規表現が定数文字列の時はコンパイル時に
「その正規表現に従って実行するプログラム」を生成してコンパイルするようなcompiler-macro
を書きました。
まあ、アドホックな職人技の世界ではありますね。
フォローありがとうございます.
まさしく私がこのネタを書くにあたって頭の中に思い描いていたのが,CL のコンパイルタイムマクロと,shiro さんの正規表現ライブラリ (コンパイルタイムにカリカリに最適化して,文字列定数の時はネイティブコンパイルまで持ってく),でした.
C の printf にそこまで望むのは言いがかりっちゃ言いがかりですが (笑) 最近,ゆるーく続いていた,一連の 「C 言語の限界 (駄目なところ)」 ネタの流れもあったりします.
# CL では (でも) format がちゃんとコンパイルされる,ということまでは知りませんでしたが.そこは,OCaml の printf (一見文字列だけど,実は format という型で,ちゃんと型チェックなどがされる) が頭にありました.
< 以下蛇足 (特に shiro さんには,今さら言うまでも無いことで,思いっきり釈迦に説法ですが) >
CL は,現状で最も 「実用的」 という意味で,非常に素晴らしい 「プログラミング言語」 だと思っております.また,実世界を相手にする際には,アドホックな対処がかならず必要になると思います.実務屋と理論屋は車輪の両輪なので.どっちが欠けてもマズイ.もちろん,両方やるのがベストなんですが...