Web備忘録

雑多に書いています

「Webフロントエンド ハイパフォーマンスチューニング」を読んだ。

2章 レンダリングのしくみ

ブラウザは、以下のようなレンダリングエンジンとJavaScriptエンジンを用いてレンダリングを行う。

(引用: 久保田 光則. Webフロントエンド ハイパフォーマンス チューニング (Japanese Edition) (Kindle の位置No.589). Kindle 版 より)

レンダリングの流れはLoading, Scripting, Rendering, Painting である。(ローディング、スクリプティングレンダリング、ペインティング)

Loading フェイズ

HTML, CSS, JavaScriptファイルや、Jpeg, SVG, PNGなどの画像ファイルを読み込む。

Scripting フェイズ

JavaScirptコードのトークン列をパースし、コンパイルしたあとコードを実行する。

Renderingフェイズ

スタイルの計算を行なったあと、要素の大きさやパッディング、マージン、位置などを決定する。

Paintフェイズ

内部の低レベルな2Dグラフィック向けの命令を行なったあと、その命令に基づきz軸の存在するレイヤーという単位で1枚ごとに生成する。最終的にそのレイヤーを合成し、レンダリング結果を生成する。

「再レンダリング」するときに、以上の一連の流れを再び必ず行うというわけではなく、状況に応じて内部表現のオブジェクトをなるべく再利用する。ただし、どの程度再利用するかは、場合によって異なる。

DOMContentLoaded vs load - HTML DOM

3章 チューニングの基礎

推測するな、計測せよ。(UNIX格言)

RAILというパフォーマンスモデルがあり、ウェブアプリケーションやハイブリッドアプリに対するパフォーマンスの指標として利用できる。

パフォーマンス指標 RAIL

  • Response(応答) 基準時間: 100ms
    • ユーザーの何らかのアクションに対して応答するまでの時間。
  • Animation(アニメーション) 基準時間: 16ms
    • アニメーションの1フレーム処理の時間(16ms以下だと60FPSを実現できる)
  • Idle(アイドル処理) 基準時間: 50ms
    • アイドル状態(何らかのユーザーのアクションを待っている状態)のJavaScriptの処理時間
    • Responseとあわせて150ms以下で実際のUI上の応答があることになる。
  • Load(読み込み) 基準時間: 1000ms
    • コンテンツ読み込みにかかる時間

計測する手段

  • Chrome DevTools
  • JavaScriptによる計測
  • パフォーマンス診断ツールの利用

    • Chrome Devtools の Light House
      Light House
  • パフォーマンスの継続的監視

4章 リソース読み込みのチューニング - 9章 認知的チューニング

Next.js使ってるとどれが使えるかすぐわからないので、深くは追求せず。

「SQL実践入門」を読んだ。

読んだ理由

DBとSQLをやりたくなった。

1章

  • ストレージ(ディスク)はアクセス低速、メモリは高速。
  • DBMSの実行計画を読んでチューニングしなければならないときがある。

2章

WHEREの条件での 等しくない (!=的なの)は以下

<>
  • CASE式は条件分岐を式で表現できる。式なので様々な場面で使える。
  • ウィンドウ関数は、GROUP BY関数から集約機能を取り除き、カットの機能だけ残したもの。

3章

  • SQL内で手続き型言語でいう「if」のような分岐を使いたくなったときは、CASE式が利用できないか考える。
  • UNIONでの条件分岐はテーブルフルスキャンの回数が増しやすいので、CASE式の利用などで代用できないか検討する。

4章

  • GROUP BY関数やウィンドウ関数は内部的にソートやハッシュの処理が行われている。(メモリを利用するが、足りないときストレージ(TMP領域)を利用し、その場合、処理が低速になる。)

6章

  • 結合の基本はNested Loopで行われる。
  • テーブル同士のレコード数がともに膨大な場合の結合の場合、Hashマージになる場合がある。
  • 結合は、複数のアルゴリズムが存在することから、とつぜん実行計画が変更され、パフォーマンスが急激に変化することがある。

7章

  • サブクエリと結合をウィンドウ関数に代替することでパフォーマンスを改善できることがある。
  • サブクエリを使って、結合対象を先に集約しておくことで、パフォーマンスが改善することがある。

9章

  • モデル変更(データ構造)の変更によって問題を解決できるケースがある。
  • 一度定まってデータ構造を変更するのは困難を伴うケースが多いため、できる限り最初の設計の段階で頭を使う。

10章

  • 選択率をコントロールするためには、UI側(あるいはアプリケーション側)で設計する必要がある。

「プログラミングの基礎」を読んだ。

読んだ理由

関数型言語を触ってみたかった。

環境構築

M1Macを使っている。以下コマンドで構築できる。

 brew install ocaml

昔はDockerとか使って環境作ってたけどわざわざディレクトリ移動してコンテナ起こしたり、終わったら消したりなどが面倒になってしまって、結局自分のローカルに落とすのが楽という結論にいたった。本を読み終わったらしばらく使わないので uninstall する予定です。

8章 レコード

8章からメモ書き始めたので、ここから書いてます。

Ocamlのレコード(連想配列JavaScriptでいうオブジェクトのようなもの)は、先にtype指定しないとエラーが出る。

# { test = "Can I create a record?"};;
# Error: Unbound record field test

先にtypeを宣言しておくと、エラーが消える。

# type test = { test: string };;
type test = { test : string; }
# { test = "Can I create a record?"};;           
- : test = {test = "Can I create a record?"}

9章 リスト

Ocamlでのリスト処理。(配列処理)

# 2 :: 1 :: [];;
- : int list = [2; 1]
# [1; 2; 3;];;
- : int list = [1; 2; 3]

中身の処理はパターンマッチを使う。

# match [] with
    [] -> 0
    | first :: rest -> first;;
- : int = 0

上の例の場合、firstにはリストの0番目、restにはリストの0番目を抜いたリストが格納されている。

Ocamlにおける、リストに0が含まれてるか確認する関数は以下のように再帰的に関数を作ることで定義できる。(定義時、 recursionの略語であるrecを書く必要がある。)

let rec filter list = match list with
    [] -> false
    | first :: rest -> if first 0 then true
                             else filter rest;;

渡されたリストのint[]を全て合計した値を返す関数は以下。

let rec sum list = match list with
    [] -> 0
    | first :: rest -> first + sum rest;;

Ocamlのような関数型言語では、ループは再帰を使って実装するケースが非常に多い。

10章 再起関数を使ったプログラミング

リストの中のリストの先頭に数字を追加する関数の書き方は以下。

let rec add_to_each num list = match list with
  [] -> []
  | first :: rest -> ( num :: first ) :: add_to_each num rest;;

let test1 = add_to_each 1 [] = []
let test2 = add_to_each 1 [[2]] = [[1; 2;]]
let test3 = add_to_each 1 [[2]; [2; 3]] = [[1; 2;]; [1; 2; 3]]

局所変数を使えば、定義した変数を in の後に続く式の中でのみ有効なものとして扱える。

# let x = 2 in x + x;;
- : int = 4

以下のように扱う。

 let rec minimum list = match list with
  [] -> max_int
  | first :: rest -> 
    let min_rest = minimum rest
    if first <= min_rest
    then first     
    else min_rest

10.4について、p.100 に「たとえ一度しか使わなかったとしても rest の集計結果に名前をつけておくことは無駄ではありません。」とあるけれど、どう無駄でないのかよくわからないな…と思ったら、次ページで以下のようにパターンマッチで取り出していた。

let (a, b, c, d) = syukei rest

まだなんともいえないけど、これはつまり、「このパターンマッチでの取り出し」の伏線のためにp.100の記述があったわけで、p.100時点では「一度しか使わない集計結果に名前をつけるのは無駄」ではないだろうか、と思った。

「無駄ではない」という記述で、局所関数のスコープが再帰的に呼び出した関数の中でも適応内なのかな、と思ってしまいましたが、そういうわけではなさそうです。

11章 自然数再帰

自然数での再帰処理の書き方

(* 階乗を求める関数 *)
let rec fac n = 
  if n = 0 then 1
           else n * fac (n - 1)

(* べき乗を求める関数 *)
let rec power m n = 
  if n = 0 then 1
           else m * power m (n - 1)

13章 一般化と高階関数

(* 実数のリストlstを毛とり、各要素の平方根のリストを返す *)
let rec map_sqrt list = match list with
  [] -> []
  | first :: rest -> sqrt first :: map_sqrt rest

13.4 値として関数

以下の関数 twice7 について「引数として関数を受け取り、その関数を7に対して二回適用するような関数」と記述されていたが、たぶん正確には「引数として関数を受け取り、最初にその関数の引数を7として実行させ、次にその関数の戻り値を引数として再びその関数を実行する」かな、と思った。前者の書き方だと、1回目の関数実行結果を2回目が利用していないことになる。

let twice7 f = f (f 7);;
let add3 x = x + 3;;
twice7 add3;;
- : int = 13

14章 高階関数を使ったリスト処理

この章はかなり勉強になりました。

(* 受け取ったリストlstから正の要素のみを取り出す *)
let rec filter_positive lst = match lst with
  [] -> []
| first :: rest -> if first > 0 then first :: filter_positive rest
                                else filter_positive rest;;
(* init から始めて、lstの要素を右から順に f を施し込む *)
let rec right_fold f lst init = match lst with
  [] -> init
  | first :: rest -> f first (right_fold f rest init);;
(* 局所関数定義を利用しながら、受け取ったリストlstの各要素の和を求める *)
let sum lst = 
  let add_int first rest = first + rest
  in right_fold add_int lst 0;; 

無名関数の作り方は以下.

# (fun x -> x + 1) 5;;
- : int = 6

無名関数を使ってsumを作る

let sum lst = 
  right_fold (fun first result -> first + result) lst 0;;

infix関数をprefix関数として渡す方法は以下。

let sum lst = right_fold (+) lst 0;;

15章 新しい形の再帰

クイックソートの実装。(本書と書き方が違うけど、かなり簡潔にかけた気がした。)

let rec quick_sort lst = match lst with
  [] -> []
  | first :: rest -> quick_sort (List.filter (fun item -> item < first) rest)
                      @ [first]
                      @ quick_sort (List.filter (fun item -> item > first) rest);;

16章 情報の蓄積

let total_distance lst = 
  let rec hoko lst total0 = match lst with
    [] -> []
  | { kyori = k, total = t} :: rest ->
    { kyori = k, total = k +. total0}  :: hojo rest (total0 + .k)
     in hojo lst 0.0 

ウィリアム・ゴールディングの「蠅の王」を読んだ

本屋にふらっと立ち寄ったときに、表紙が赤くてかっこよかったため惹かれ、裏表紙のあらすじも面白そうだったので買いました。 文庫本で300ページほどの小説で、感想を書くとネタバレが避けられない類の小説ですので、ネタバレ有りで感想を書こうと思います。

あらすじ

疎開する少年たちを乗せた飛行機が、南太平洋の無人島に不時着した。生き残った少年たちは、リーダーを選び、助けを待つことに決める。大人のいない島での暮らしは、当初は気ままで楽しく感じられた。しかし、なかなか来ない救援やのろしの管理をめぐり、次第に苛立ちが広がっていく。そして暗闇に潜むという“獣”に対する恐怖がつのるなか、ついに彼らは互いに牙をむいた―。

感想

序盤は、ページをめくる手がちょっとスローペースでした。主人公であるラルフへの共感がなかなかできなかったからです。特に、あるシーンが自分の心の中でうまく消化しきれませんでした。そのシーンというのは、主人公にあたるラルフという少年と、たまたま彼の近くに漂流したピギーという少年が最初、ともに浜辺を歩いているのですが、ピギーが「自分は学校ではピギー(豚)と呼ばれていたけど、その名前だけでは呼ばれたくないから、みんなには別の名前で読んで欲しい」というような話し合いから展開される一連の流れです。のちに、漂流者たちが集まった会合で、ラルフは特に深い意図もなく、彼のことを「ピギー」とみんなに紹介し(そう呼ばれたくなかったのに。)、ピギーはみんなに笑われ、のちにピギーがその件についてラルフに怒って、「言わないでくれって言ったじゃないか!」と詰め寄るんですが、ラルフが約束を破ってあだ名を公表したことについて特に謝らない、という展開が、自分に悪い印象を残しました。

三人称視点で書かれているということもあり、なぜあだ名を公表したのか、またラルフがピギーに詰め寄られたときどういう感情だったか推察する手段はあまりないのですが、人物同士の関係性が曖昧な序盤において、「主人公であるラルフが大していいやつじゃない」というように自分は感じられたので、「うーん…」となってしまいました。まあ、ラルフは集団のリーダーにもなるので、あんまりぺこぺこ周りに謝れるわけでもないとは思うんですが、やっぱり、主人公は共感できる人物である方が、ページを読む手が進むというもの。

それ以降も、「ピギー」が周りから過小評価され続ける小さないじめ・排斥が続くため、「これ、ピギーがみんなにガチギレして復讐する話なんか?」 とも思いましたが、中盤移行は、様相が変わって、「豚肉 vs 火(のろし)」、どちらが大事かという戦いになり、ラルフは火(のろし)こそ最も重要だと強く主張する人物になるので、そこで主人公に共感できて、物語が展開していった感じがありました。

自分が好きなシーンが以下です。

枝のまだ燃えていない部分が横たわっている黒と白の焚き火の残骸を見て、ラルフは顔をしかめた。自分の気持ちを説明しようとした。

「ぼくは怖いんだ」

ピギーが顔をあげるのが見えた。ラルフはつかえながらも後をつづけた。

「<獣>のことじゃない。いや、それも怖いけど。誰も火の大切さをわかっていないのが怖いんだ。溺れているときにロープを投げてもらったり、お医者さんにこの薬をのまないと死ぬからのみなさいと言われたりしたら――ロープを掴むし、薬をのむだろう? そうするだろう?」

「ぼくはそうするよ」

「それがあいつらにはわからないんだ。理解できないんだ。煙の合図を出さないとここで死ぬんだってことが。ほら、あれを見ろ!」

熱された空気が灰の上で揺らめいていたが、煙は全く出ていなかった。

「ぼくたちは火をひとつ燃やしておくことすらできない。でも、あいつらは気にしないんだ。そしてもっとやりきれないことに――」ラルフはピギーの汗を流している顔に目をすえた。

「もっとやりきれないことに、ぼくもときどきそうなるんだ。でも、ぼくまでそうなったら――気にしなくなったら、ぼくたちはどうなるだろう」

(蠅の王<ハヤカワepi文庫> p.245-246)

自分にとっても周りにとっても、たしかに「正しく見えるはずの自分の正義」が、一定の集団の中において全く効力を持たない。そのとき、その正義を捨てて、集団の価値観に染まるのか、それとも自分の正義を捨てないのか。

たしかに、火(のろし)が万能というわけでもありません。船を呼ぶはずの「のろし」が本当に今の状況で効果的なのかどうかわからないし、のろしを上げるのに苦労しすぎて、食べ物を十分に採取できないといったこともクリアしなければならない。ただ、自分は火が最も重要であるラルフの考えの方がより「生き残るために適している」と考えたので、ラルフを応援しました。

ネタバレなので、もう展開について述べますが、ラルフは負けます。(読みながら、負けるだろうなーと思っていた。)。それも、野性的な力によって負けます。やっぱり、一度暴力が強い力を持つと、それを覆すのは厳しいですね。とくに、ラルフは豚肉派のリーダーに恨まれていましたし、向こう側は派手な化粧を行って、自分の顔色などを匠に隠しもするようになっていたので、理性的な話し合いがもはや通じはしないという状況でした。

これは自分の感想ですが、人間の感情同士の「ねじれ」が発生すると、なかなか解消が大変だよなあと思いました。仲間を殺されると、もう、理性的な話し合いで解決するという、その土台を築きようもありません。相手に対する強い恨み、復讐心が燃え、そして、それを解くためには、相手を「信頼する」必要がある。でも、殺人を犯す相手を、懐に凶器を隠しもつ人間を、どうやってかんたんに信頼できるでしょう。

最後に現れた人物が「もっとうまくやれそうなもんだがな」といいますが、まさにそう思います。もっとうまくやれそうなもんなのに。だって、食料は豊富な島で、火だって漂流の早い段階で起こすことができたんだから。

ただ、何かがずれてしまった。あえて、ラルフに原因の一つを求めるとしたら、それは彼が「言葉の力」を持たなかったことかな、とも思います。ラルフは、「ぼくがリーダーだ!」「ぼくがほら貝を今もっている!(発言権をあらわすルール)」とやたらに繰り返し、一度決めたルールを絶対的に押し付け、周りに従わせようとする素振りも見られます。こういった非柔軟な姿勢が、信用力を落とし、「全員が助かるためには、火がなによりも大切である」という、本当に伝えたかったメッセージの力を落としてしまったのかな、とそんなふうに思いました。

と、ひさしぶりに真面目な感想でした。面白かったです。ただ、このあとに読んだツルゲーネフの「初恋」が自分に強く響いたので、それについての感想もまた書きたいです。

以上です。

コグニカルの数学の基礎コースを学びました

コグニカルとは?

1ヶ月ほど前にはてぶでバズってた数学系の学習サイト。

数学基礎コースや、デジタル信号基礎コースなどがある。

cognicull.com

なんで数学コースを解いたの?

もともと数学をいつか学びなおしたいなあという気持ちがあった。

心理的には数学への苦手意識自体はなかったものの、学生時代は「セオリーを一生懸命覚える」的な勉強法を中心にやっており、 体系的な知識が身についていないな、と思っていた。

そんなときにこのコースを見つけ、試しにいくつか解いてみると、1個あたりが軽くてシンプルで、サクサク解けそうだなと思ったので取り組んだ。

どれくらい時間かかった?

正確に測ったわけじゃないけど、全体だと3~4時間ぐらい。1週間かけてまったりやりました。

どんな講座があるの?

こんなの。

f:id:fujiten3:20210330224333p:plain

学びはあった?

あった、と思う。

具体的になに?

たぶんちゃんと数学やってきた人にとっては「あなたマジですか…」なんだろうけど、

  • 「√2 x √3 = √6」みたいな√同士の掛け算の方法

    • 考えてみれば両辺を二乗すればこれは簡単に証明できるんだけど、改めて見ると「√2 x √3= √2√3」で終わらないのか!みたいな…。(こんなレベルでもエンジニアやっております)
  • 循環少数は分数にできる(ので無理数でない)

  • 入力x 出力f(x) と置いたとき、 f(x)=2x という説明を見て、「f(x)」自体を関数的なもので捉えてたけど、f(x)は数学的には出力でしかないよな みたいな

  • 虚数の概念

  • 三角関数の定義(また単位円での拡張)

  • 因数分解 / 因数定理 / 多変数関数 / 合成関数 / 合成関数の微分 / 対数 / 常用対数 / 自然対数 / ベクトル / 余弦定理 / 内積 / 振幅 / 角周波数 / 周期的な波と周期 / 位相 / sin波とcos波

後半は大学受験で自分は使わなかったので、特に勉強になった。

サイトの製作者さんが完全に謎なのですが、(個人でやっているらしい)、わかりやすいサイトを作って頂き感謝です(恐らく大学教授などをされている方と勝手に想像している…)

次はデジタル信号基礎コースやろうかな。

おわり。