<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://twydev.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://twydev.github.io/" rel="alternate" type="text/html" /><updated>2025-07-01T06:41:11+00:00</updated><id>https://twydev.github.io/feed.xml</id><title type="html">wyblog</title><subtitle>software. practical stuff.</subtitle><author><name>wy</name></author><entry><title type="html">Research on: Crypto</title><link href="https://twydev.github.io/research/crypto/" rel="alternate" type="text/html" title="Research on: Crypto" /><published>2025-06-17T00:00:00+00:00</published><updated>2025-06-17T00:00:00+00:00</updated><id>https://twydev.github.io/research/crypto</id><content type="html" xml:base="https://twydev.github.io/research/crypto/"><![CDATA[<blockquote>
  <p>[!info]</p>
  <ul>
    <li>Understanding Crypto Fundamentals: Value Investing in Cryptoassets and Management of Underlying Risks, Thomas Jeegers 2023, ISBN 978-1484293096</li>
    <li>The Book of Crypto: The Complete Guide to Understanding Bitcoin, Cryptocurrencies and Digital Assets, Henri Arslanian 2023, ISBN 978-3030979515</li>
    <li>Crypto Decrypted: Debunking Myths, Understanding Breakthroughs, and Building Foundations for Digital Asset Investing, Jake Ryan and James Diorio 2023, ISBN 978-1394178537</li>
  </ul>
</blockquote>

<h1 id="money-is-about-trust">Money Is About Trust</h1>

<ul>
  <li>It is common among crypto books to dedicate the first chapter to talk about money
    <ul>
      <li>The definition of money (a unit of accounting, a store of value, a medium of exchange)</li>
      <li>A history of money and how it evolves in different civilisations</li>
      <li>The problem of inflation</li>
      <li>And how bitcoin, the first crypto product, can be seen as a form of money</li>
      <li>Traditional currencies are based on trust in a central authority (the government)</li>
    </ul>
  </li>
  <li>And in later chapters of those books they will address how cryptocurrencies can potentially overcome certain challenges that traditional currencies face
    <ul>
      <li>As a decentralised system, cryptocurrencies can avoid inflation caused by a central authority printing excessive amount of money (this can be regulated in the source code)</li>
      <li>No single party can manipulate the decentralised ledger in theory, so there is no need to worry that a central authority may seize your money</li>
    </ul>
  </li>
  <li>While the authors’ theories sound compelling, the real-world truth is that <strong>money’s value hinges entirely on public trust and the expectation of stable purchasing power over time</strong>
    <ul>
      <li>If everyone lose faith in the US dollar and stop accepting it as payment, the dollar will lose its value overnight, and that has nothing to do with the flaws of the paper fiat currency not being a cryptocurrency</li>
      <li>One may argue that the dollar runs the risk of losing value precisely because of a central authority managing the dollars, and the central authority may mess up</li>
      <li>But cryptocurrencies do not run the same risk (secured on blockchain, decentralised, regulated by open source code)</li>
      <li>While that may be true, it doesn’t mitigate the risk of people simply losing faith in a cryptocurrency (look at all the pump-and-dump of meme coins)</li>
    </ul>
  </li>
  <li>In fact having a central authority that does a good job can make people trust the currency more
    <ul>
      <li>The government can use their political or even military power to secure the use of the currency they issue. Ultimately, power grows out of the barrel of a gun. Will the use of cryptocurrencies liberalise a closed-off country and benefit the people in those countries? Likely not.</li>
    </ul>
  </li>
  <li>Let’s have a thought experiment: imagine a popular MMORPG with in-game currency
    <ul>
      <li>Just assume, by some techno-magic, the currency is secure, immutable, and the game is scalable, resilient, 100% reliability</li>
      <li>Then this in-game currency is as good a candidate as any cryptocurrencies to be used as money for real life transactions</li>
      <li>So the only advantage that cryptocurrencies bring is decentralisation</li>
      <li>Decentralisation eliminates certain risks, but does not guarantee the longevity of the currency. It is ultimately based on trust.</li>
    </ul>
  </li>
</ul>

<h1 id="technical-details">Technical Details</h1>

<ul>
  <li>It is probably better to read up on the various papers published regarding blockchains and consensus mechanism</li>
</ul>

<blockquote>
  <p>[!todo]
Bitcoin whitepaper
Ethereum whitepaper
Various consensus mechanism
DAO
DeFi apps
Privacy coins</p>
</blockquote>

<h1 id="investments">Investments</h1>

<ul>
  <li>I think it only makes sense to view cryptocurrencies as a hedge and to only allocate a small portion of your diversified portfolio to it</li>
  <li>Potential investment vehicles
    <ul>
      <li>Directly purchasing cryptocurrencies and holding them in your own wallet</li>
      <li>Staking cryptocurrencies in mining pools to obtain a yield (yield could be provided by the design of the blockchain as reward for proof-of-stake mining)</li>
      <li>Staking cryptocurrencies in liquidity pools for DeFi applications (commission or transaction fee from transactions happening on the apps)</li>
      <li>Buying cryptocurrencies ETF from stock exchanges or fund managers (only bitcoin for now)</li>
      <li>Buying shares of publicly traded companies that operates cryptocurrencies (like Circle Internet) or holds a large reserve of cryptocurrencies (like Microstrategy)</li>
      <li>Backing new crypto projects by buying their tokens or coins (highest risk)</li>
    </ul>
  </li>
</ul>

<h1 id="non-financial-risks">Non-Financial Risks</h1>

<ul>
  <li>51% Attack Risk
    <ul>
      <li>When a malicious party has control over a sufficient majority of miners/validators of the blockchain to manipulate the transactions in their favour. Having a majority control can ensure that the malicious party always mine every block in the chain.</li>
      <li>But such attacks can only be short term, as the market will reach equilibrium quickly and the cryptocurrency will lose its value once people are aware of the attack</li>
      <li>For bitcoin or blockchains that have reach a global scale, this attack is unrealistic as there is no single malicious attacker that can gather sufficient computing power, not even if all big tech companies collude and combine their computing power.</li>
    </ul>
  </li>
  <li>Miner Concentration or Ownership Concentration
    <ul>
      <li>If a single party owns most of the miner, there is a risk that the party can control the mining</li>
      <li>The same applies for proof-of-stake blockchains, if a single party owns most of the cryptocurrencies on the chain and can control the mining</li>
    </ul>
  </li>
  <li>Quantum Computing
    <ul>
      <li>Advances in this field can potentially break existing cryptographic schemes.</li>
      <li>Blockchains can adopt quantum resistant cryptographic algorithms</li>
      <li>Wallets can also choose to only expose a public hash of the public key as wallet address, and to never use the same key twice for transactions</li>
      <li>For example, after receiving funds in an address, forward it to a brand new wallet address that has never exposed its public key for longer term storage</li>
    </ul>
  </li>
  <li>Regulatory Risk
    <ul>
      <li>This may shutdown your access to the crytocurrencies depending on your government</li>
    </ul>
  </li>
  <li>Developer and Community Risk
    <ul>
      <li>The protocol of the blockchain may introduce new bugs in a new patch, even if it is open source, this can happen</li>
      <li>The developers of smaller products may also be malicious or act against the interest of everyone else buying their products (platform risk or custody risk)</li>
      <li>The community that owns the product may also disagree with the product direction, which can kill adoption</li>
      <li>Client and platform applications that allows you to transact cryptocurrencies may not be securely implemented even though the underlying blockchain is secure</li>
    </ul>
  </li>
  <li>Oracle Risk
    <ul>
      <li>Similar to the above risk, the oracles may not be securely implemented and they may be unavailable or provide wrong information, which may cause smart contracts to execute on inaccurate outcomes</li>
    </ul>
  </li>
  <li>DDOS Attack
    <ul>
      <li>Like any applications available on the internet, it can be made unavailable by overwhelming bad requests sent into the network</li>
    </ul>
  </li>
  <li>Scams and Market Manipulation
    <ul>
      <li>Pump-and-dump schemes</li>
      <li>Financial fraud by the platform providers</li>
    </ul>
  </li>
</ul>

<p>Some DeFi products offer insurance against some of the risks mentioned above and is worth considering, since the paying out of benefits is automated using smart contracts</p>

<h1 id="financial-risk">Financial Risk</h1>

<ul>
  <li>Credit Risk
    <ul>
      <li>When counterparty could not meet their debt obligations. This can happen to a centralised platform that you use.</li>
    </ul>
  </li>
  <li>Liquidity Risk
    <ul>
      <li>There is insufficient sellers or buyers in the market to match your transactions.</li>
    </ul>
  </li>
  <li>Market Risk
    <ul>
      <li>Changing market conditions like interest rates can cause a change in the value of your investment</li>
    </ul>
  </li>
  <li>Value at Risk
    <ul>
      <li>Is an estimate, the P probability of losing at most D dollars over a T time period</li>
      <li>Analytical VaR method uses statistics and a probability distribution to estimate the risk (but this is built based on assumptions of probability of certain events)</li>
      <li>Historical VaR method uses historical values to estimate future values</li>
      <li>Monte Carlo VaR uses the Monte Carlo simulation method to simulate changes in multiple variables and estimate the overall risk</li>
      <li>Roy’s Safety-First Criterion: to manage portfolio by maximising return under the constraint that probability of loss of a certain amount does not exceed a certain desired limit</li>
    </ul>
  </li>
  <li>Shortfall Risk
    <ul>
      <li>Similar to Roy’s criterion. This is the risk of a portfolio failing to achieve a desired level of returns</li>
      <li>The level of returns may be a critical objective of the investor (e.g. to maintain a certain retirement lifestyle)</li>
    </ul>
  </li>
  <li>Expected Shortfall
    <ul>
      <li>Estimates the expected loss on a portfolio over a specific time horizon beyond a specific percentile of the probability distribution (e.g. worst 10% scenarios)</li>
    </ul>
  </li>
  <li>Backtesting
    <ul>
      <li>Can be used to check the portfolio performance against historical data</li>
      <li>But historical events is never 100% representative of future returns</li>
    </ul>
  </li>
  <li>Stress Testing
    <ul>
      <li>Similar to Monte Carlo method</li>
      <li>Assumes certain scenarios and estimate how the portfolio would perform</li>
      <li>Scenarios can include non-financial risks mentioned above</li>
    </ul>
  </li>
</ul>

<h1 id="valuation-assessment">Valuation Assessment</h1>

<p>Some factors to consider when valuing a crypto product</p>

<ul>
  <li>Problem space
    <ul>
      <li>What problem is the product trying to solve (usefulness)</li>
      <li>How important is the problem (scale and impact)</li>
      <li>How does the product solve the problem (effectiveness, efficiency, scalability)</li>
      <li>Best candidate solution (competitors, barriers to entry, unique value proposition)</li>
    </ul>
  </li>
  <li>Product team
    <ul>
      <li>Is the team reliable (credentials, track records, incentives, transparency)</li>
      <li>Local regulatory environment (red tapes, blockers)</li>
    </ul>
  </li>
  <li>Tokenomics (the economic aspects of the crypto product as implemented by the code)
    <ul>
      <li>Token distribution schedule (who, when, and how tokens are given)</li>
      <li>Token governance (how are decisions made)</li>
      <li>Token supply design (inflationary or deflationary)</li>
      <li>Consensus mechanism</li>
      <li>Interoperability</li>
      <li>Are incentives of all parties aligned with the interest promoted by the product</li>
    </ul>
  </li>
</ul>

<h2 id="value-investing-analysis">Value Investing Analysis</h2>

<ul>
  <li>Net Present Value (NPV) analysis
    <ul>
      <li>Commonly used to analyse traditional company stocks</li>
      <li>Estimate the NPV considering future cashflow</li>
      <li>NPV becomes a common metrics to compare different companies</li>
    </ul>
  </li>
  <li>Valuations based on Multiples
    <ul>
      <li>Multiples like Price-to-Earning ratio, Price-to-Book ratio</li>
      <li>These also provide common metrics to compare different companies</li>
    </ul>
  </li>
  <li>Problems with the above two analysis approach
    <ul>
      <li>Companies in different industries have different multiples and different expectation of future earnings</li>
      <li>Analysts often have to make adjustments to their analysis to consider the industry differences (which are subjective and based on their judgement)</li>
    </ul>
  </li>
  <li>Alternative approach
    <ul>
      <li>Suggested by Benjamin Graham and David Dodd</li>
      <li>Measure real net asset value</li>
      <li>Measure earnings power</li>
      <li>Measure value of growth (this is intangible and subjective)</li>
      <li>Three separate analysis</li>
    </ul>
  </li>
  <li>Applying the same analysis to crypto products
    <ul>
      <li>Net Asset Value can be approximated using Replacement cost (electricity cost of mining)</li>
      <li>Earnings Power Value can be approximated using Staking yields (since proof-of-stake is much less intensive than proof-of-work, electricity costs will not be a good estimate)</li>
      <li>Valuing Growth is subjective
        <ul>
          <li>Use high margin of error since the future in this space is highly uncertain</li>
          <li>Prefer to be more conservative in all estimates</li>
          <li>Consider number of nodes in the network</li>
          <li>Consider activity on the network, like the number of transactions (easily manipulated) and total transaction costs (less incentive to be manipulated)</li>
          <li>Consider Total Value Locked (TVL) which is a similar measure to Assets Under Management (AUM)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Market Capitalisation is NOT a good measure for valuation</li>
  <li>Stock-to-Flow (S2F) ratio model (total quantity of the asset vs new supply of the asset)</li>
  <li>Using traditional commodities as a model to approximate the value of crypto products that behaves like commodities</li>
  <li>Network Value to Transaction (NVT) ratio can be used along side other metrics to check if a bubble is forming</li>
  <li>The Fulcrum Index, constructed based on the likelihood of credit default in G20 government currencies, can be used to approximate the value of crypto assets (assuming these assets are a hedge against government defaults)</li>
  <li>There are probably a lot more indicators and metrics out there that we can use to form our own valuation judgements</li>
</ul>]]></content><author><name>wy</name></author><category term="research" /><category term="crypto" /><summary type="html"><![CDATA[[!info] Understanding Crypto Fundamentals: Value Investing in Cryptoassets and Management of Underlying Risks, Thomas Jeegers 2023, ISBN 978-1484293096 The Book of Crypto: The Complete Guide to Understanding Bitcoin, Cryptocurrencies and Digital Assets, Henri Arslanian 2023, ISBN 978-3030979515 Crypto Decrypted: Debunking Myths, Understanding Breakthroughs, and Building Foundations for Digital Asset Investing, Jake Ryan and James Diorio 2023, ISBN 978-1394178537]]></summary></entry><entry><title type="html">Notes for: A Map of the New Normal: How Inflation, War, and Sanctions will Change Your World Forever</title><link href="https://twydev.github.io/notes/a-map-of-the-new-normal/" rel="alternate" type="text/html" title="Notes for: A Map of the New Normal: How Inflation, War, and Sanctions will Change Your World Forever" /><published>2025-06-10T00:00:00+00:00</published><updated>2025-06-10T00:00:00+00:00</updated><id>https://twydev.github.io/notes/a-map-of-the-new-normal</id><content type="html" xml:base="https://twydev.github.io/notes/a-map-of-the-new-normal/"><![CDATA[<blockquote>
  <p>[!info]
title: A Map of the New Normal: How Inflation, War, and Sanctions will Change Your World Forever
author: Jeff Rubin
published: 2024
edition: 1
ISBN: 978-0735246119</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<p>This book (published in year 2024) highlight some of the important events that has happened around the world, that leads us to where we are at right now.</p>

<h1 id="key-events">Key Events</h1>

<h2 id="monetary-policies">Monetary Policies</h2>

<ul>
  <li>Quantitative Easing (2021)
    <ul>
      <li>Performed during Covid pandemic by governments worldwide</li>
      <li>Government bonds are financed by Central Banks printing money and paying above market price</li>
      <li>This kept interest rates on those bonds artificially low</li>
      <li>No one complains since corporate and household debts are benchmarked to the government bonds</li>
      <li>Low interest rate environment is unsustainable due to rising inflation</li>
    </ul>
  </li>
  <li>Rising Inflation
    <ul>
      <li>Energy price shocks has caused periods of high inflation historically
        <ul>
          <li>Higher energy prices leads to higher food prices (production and transport cost)</li>
          <li>That leads to lower purchasing power and workers demanding higher wages</li>
          <li>If central banks does not intervene, increase in prices should lead to contraction in demand and the market should reach equilibrium</li>
          <li>But if central banks intervene to soften the blow (politically popular), it may lead to wage-price spiral that worsens inflation</li>
        </ul>
      </li>
      <li>During Covid years, oil price hit historical lows but rebounded rapidly after the pandemic (potential energy shock)
        <ul>
          <li>Union movements around the world demand higher wages to combat rising cost of living. Politicians may support these movements to maintain popularity.</li>
          <li>The “Great Resignation” during Covid created vacancies in the market, driving up wages</li>
        </ul>
      </li>
      <li>Unemployment must rise to combat inflation (through loss of jobs and hence loss of demand)</li>
      <li>Stagflation is the worst-case scenario (rising inflation and unemployment)
        <ul>
          <li><strong>Authur Okun’s Misery Index</strong></li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Deleveraging
    <ul>
      <li>Period of low interest rate fuelled the boom of mortgages and property market</li>
      <li>It also made investors allocate more to stocks in their portfolio
        <ul>
          <li>Traditional portfolio balances between stocks and bonds (which are not performing)</li>
          <li>Bond yields also gets eliminated by inflation</li>
          <li>But the boom in stock market disproportionately benefited the wealthiest as they owns most of the stock market</li>
        </ul>
      </li>
      <li>If central banks try to perform Quantitative Tightening, the debt market will unravel (politically unpopular)</li>
      <li>Central banks balance sheets will go into deficit as well (bonds purchased from government during QE is losing value due to rising market yield)
        <ul>
          <li>They can always print money, and the deficit will go away</li>
          <li>But that will worsen inflation</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Moving away from petrodollar
    <ul>
      <li>Changing relationship between US and Saudi Arabia has prompted Saudi to now accept other currencies for their oil trade (perhaps the US thinks that they will be fully self-sufficient with shale oil production)</li>
      <li>Sanctions on Russia has also prompted Russia to rublise their oil and commodities trade</li>
      <li>Reduced usage of the dollars for oil trade also means reduced demand for US treasuries</li>
    </ul>
  </li>
  <li>Weaponising the dollar
    <ul>
      <li>The US has frozen Russia’s foreign dollar reserve in retaliation for invasion of Ukraine</li>
      <li>Central banks around the world are now concerned about their own dollars potentially getting frozen (China for example has a large dollar reserve) and they will be thinking about rebalancing their reserves with other currencies</li>
    </ul>
  </li>
</ul>

<h2 id="energy-shock">Energy Shock</h2>

<ul>
  <li>Constrained Oil Supply
    <ul>
      <li>During years of good earnings, oil companies shifted their focus to dividend or share buybacks and neglected capex on oil production</li>
      <li>Environment, Social, and Governance (ESG) push by the regulators also made companies reduce their investments in new fossil fuel production</li>
      <li>Sanction on Russia for the Ukraine war has also caused a drop in global oil supply</li>
      <li>The USA tried to work with Iran and Venezuela to secure new supply of oil but didn’t work out
        <ul>
          <li>The Keystone XL pipeline to bring oil from Canada to USA was cancelled by ESG concern</li>
          <li>USA started draining their Strategic Petroleum Reserves (SPR) (USA can depend on shale production in the future)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>EU Energy Prices
    <ul>
      <li>Sanction on Russia caused EU to lose access to cheaper energy, increasing costs overall (manufacturing sector loses competitiveness)</li>
      <li>Germany started importing LNG from USA (big win for USA)</li>
    </ul>
  </li>
  <li>Trade-off Between Energy Security and Sustainability
    <ul>
      <li>EU turns to coal for energy to make up for the loss of access to Russian oil</li>
      <li>Moved away from nuclear energy after Fukushima disaster in 2011 (but may explore Nuclear again starting in 2025)</li>
      <li>Challenging for EV industry, as source of power becomes more expensive and is no longer green</li>
    </ul>
  </li>
</ul>

<h2 id="global-supply-chain">Global Supply Chain</h2>

<ul>
  <li>Disrupted Global Supply Chain
    <ul>
      <li>Globalisation is basically wage arbitrage leveraged by low transport cost</li>
      <li>Companies shift production offshore to keep cost low, which kept inflation low (prevented wage spiral) during the years of economic booms</li>
      <li>The pandemic exposed how fragile the supply chain is as shipments are cancelled or delayed causing inflation</li>
      <li>Friendshoring became a focus as global tension rises</li>
    </ul>
  </li>
  <li>Food Shortages
    <ul>
      <li>War between Russia and Ukraine disrupted grain and fertiliser supply for the world, driving up prices</li>
      <li>Climate change causes agriculture yields to fall in China and the USA</li>
      <li>Major food exporter gains significant geopolitical leverage</li>
    </ul>
  </li>
</ul>

<h2 id="russia">Russia</h2>

<ul>
  <li>Russia has been used to sanctions thrown at them historically
    <ul>
      <li>They are good at being self-sufficient as they were always prepared to face sanctions</li>
      <li>When EU closed their airspace from Russian airlines (and Russia also retaliated by closing off access to western airlines), it is the western airlines that got disproportionately impacted</li>
      <li>When Boeing and Airbus stop supplying and servicing planes for Russia, they simply seized the existing planes, and developed their own domestic airline industry</li>
      <li>We also see the same domestic market developments in China that are spurred by sanctions</li>
    </ul>
  </li>
  <li>Sanctions Effectiveness
    <ul>
      <li>It is especially damaging for countries that are not self-reliant (but Russia and China are self-reliant to some degree)</li>
      <li>But overtime as more countries become self-reliant (incentivised by existing sanctions), they lose their effectiveness, and may end up hurting the countries that initiated and imposed those sanctions (energy situation in EU)</li>
      <li>It also depends on how big are the target economies (China and Russia are simply too big)</li>
      <li>Russia pivoted to export oil and gas to China, India, and even Saudi Arabia</li>
    </ul>
  </li>
  <li>No-limits Partnership with China
    <ul>
      <li>Sanctions has prompted Russia and China to strengthen their partnership so that they have more capacity to handle the threats from the West</li>
    </ul>
  </li>
  <li>Existential Threat
    <ul>
      <li>Ukraine joining NATO is an existential threat for Russia. Russia has to respond.</li>
      <li>See Prisoners of Geography by Tim Marshall
        <ul>
          <li>journal: [[202507011339-prisoners-of-geography]]</li>
          <li>web: <a href="https://twydev.github.io/notes/prisoners-of-geography">Russia</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="china">China</h2>

<ul>
  <li>A Multi-polar World
    <ul>
      <li>Shanghai Cooperation Organization (SCO) and BRICS expanding their influence in Asia</li>
      <li>Improving trade relations and volume between countries in these two organisations speeds up the shift away from dollars</li>
      <li>China has been helpful in bailing out countries that are defaulting from Belt and Road initiative. This buys them influence.</li>
      <li>Alternatives to IMF and World Bank established by BRICS, and lending by China to individual countries, have less stringent conditions on the borrowing countries and may be seen as a fairer deal for developing economies</li>
    </ul>
  </li>
  <li>Reaction to Sanctions
    <ul>
      <li>US have tried to limit the development and export of Chinese technology, by banning imports and sale of certain tech to China, delisting Chinese tech companies, or banning use of certain Chinese software.</li>
      <li>Similar to Russia, this prompted the China government to focus on self-reliance and develop those technology with more investments and seek other markets</li>
      <li>These measures also hurt US as China is a big exporter of solar and battery tech</li>
    </ul>
  </li>
  <li>Dollar Reserves
    <ul>
      <li>China has a large dollar reserve and has been funding US government debts</li>
      <li>If China unwinds their reserve and US treasuries, it will make cause future US government spending to be more expensive</li>
    </ul>
  </li>
  <li>Taiwan
    <ul>
      <li>US has traditionally not declared that they will provide military aid to Taiwan in the event of a Chinese invasion, to prevent escalation by China</li>
      <li>Biden’s administration changed their stance due to the increasing importance of semiconductor chips for all advanced manufacturing</li>
      <li>Taiwan’s silicon shield is heavily reliant on imports of rare earth elements (REE), which China is the largest exporter</li>
      <li>US could attempt to destroy Taiwan chips manufacturing facilities preemptively if they cannot secure Taiwan from a potential invasion</li>
      <li>Both China and US are in a semiconductor race to improve their self-reliance. And this also includes self-reliance on REE mining. However, chips produced outside Taiwan are expected to be much more expensive.</li>
    </ul>
  </li>
</ul>

<h2 id="middle-east">Middle East</h2>

<ul>
  <li>Israel, Saudi Arabia, and Iran
    <ul>
      <li>US has been trying to improve relationship between Israel and Saudi Arabia, establishing military partnership. This may be an existential threat for Iran.</li>
      <li>Terror attack by Hamas on Israel in Oct 2023 stalled the plans, which is aligned with Iran’s interest</li>
      <li>China has been brokering improved trade relationship between Iran and Saudi Arabia, which are long time rivals</li>
      <li>Saudi Arabia may enjoy the best of both worlds through improved relationship with Israel and Iran (which are proxies for US and BRICS), so they are not incentivised to take a side in conflicts</li>
    </ul>
  </li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="geopolitics" /><summary type="html"><![CDATA[[!info] title: A Map of the New Normal: How Inflation, War, and Sanctions will Change Your World Forever author: Jeff Rubin published: 2024 edition: 1 ISBN: 978-0735246119]]></summary></entry><entry><title type="html">Notes for: Hidden Games: The Surprising Power of Game Theory to Explain Irrational Human Behaviour</title><link href="https://twydev.github.io/notes/hidden-games/" rel="alternate" type="text/html" title="Notes for: Hidden Games: The Surprising Power of Game Theory to Explain Irrational Human Behaviour" /><published>2025-06-07T00:00:00+00:00</published><updated>2025-06-07T00:00:00+00:00</updated><id>https://twydev.github.io/notes/hidden-games</id><content type="html" xml:base="https://twydev.github.io/notes/hidden-games/"><![CDATA[<blockquote>
  <p>[!info]
title: Hidden Games: The Surprising Power of Game Theory to Explain Irrational Human Behaviour
author: Moshe Hoffman, Erez Yoeli
published: 2023
edition: 1
ISBN: 978-1529376845</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<ul>
  <li>Identifying hidden games (Game Theory) to explain seemingly irrational human behaviours.</li>
  <li>My objective is to form an intuitive understanding of how human behaviours can be explained using Game Theory, so I will skip over the algebra and calculations, and even some of the experimental evidence.</li>
  <li>The book takes the approach of laying down the assumptions of each hidden game, and then try to challenge each assumption to see if the assumptions relates to reality, which helps to justify the model. The book also explores the Nash equilibrium and subgame perfect equilibrium of each game.</li>
</ul>

<p>Some background concepts related to human behaviour:</p>

<ul>
  <li><strong>Learning</strong>
    <ul>
      <li>Reinforcement learning helps us acquire specific skills</li>
      <li>Social learning occurs through interacting with the community</li>
      <li>Behaviours, beliefs, and preferences can all be shaped by learning</li>
      <li>Lags: learned attributes carry over time, even when conditions appropriate for those attributes no longer exists</li>
      <li>Spillover: learned attributes is applied to other situations where corresponding conditions for those attributes do not exists</li>
    </ul>
  </li>
  <li><strong>Primary vs Secondary rewards</strong>
    <ul>
      <li>Primary rewards are things we have evolved to be motivated to pursue (food, shelter, health, comfort, safety, effort, time, prestige, power, sex), and they are often social.</li>
      <li>Primary rewards are usually universally liked and evolutionary sensible.</li>
      <li>Secondary rewards are things we find rewarding but we are not evolved to like them (fitness, conscious goals, psychological rewards, financial incentives).</li>
      <li>For secondary rewards, we can learn to like them, therefore we can also unlearn them, making them suspiciously flexible.</li>
      <li>Although financial incentives are powerful, they are no social.</li>
    </ul>
  </li>
  <li><strong>Proximate-Ultimate distinction</strong>
    <ul>
      <li>Proximate answers are explanations or justifications people tell themselves, or people use to convince themselves why they behave or make certain decisions. You can dig deeper to find underlying explanations beneath proximate answers.</li>
      <li>Ultimate answers are the final level of answers after digging deeper, or the good enough answers to justify the behaviours and decisions convincingly.</li>
    </ul>
  </li>
  <li><strong>Emic-Etic distinction</strong>
    <ul>
      <li>Emic explanations are used by people within a community to justify the practices of the community</li>
      <li>Etic explanations are used by people outside the community, to objectively justify the practices of the community</li>
    </ul>
  </li>
</ul>

<p>We are interested in understanding human behaviours driven by primary rewards explained using ultimate and etic answers.</p>

<ul>
  <li><strong>Nash equilibrium</strong>
    <ul>
      <li>If any players in the game cannot gain more benefit by deviating from their current strategy unilaterally</li>
      <li>Players have optimised their actions, taking into other players also optimising their actions</li>
    </ul>
  </li>
</ul>

<h1 id="sex-ratio-the-gold-standard-of-game-theory">Sex Ratio: The Gold Standard of Game Theory</h1>

<ul>
  <li>In the game, assume population of 100 players</li>
  <li>Two types of players: male and female</li>
  <li>Objective of players: players wants to have more grand offspring (grandchildren)</li>
  <li>Actions players can take: Choose the sex of their offspring (simplified assumption)</li>
  <li>We are interested in finding what will be the final sex ratio of the population</li>
  <li>Assumptions:
    <ul>
      <li>Both male and female offspring are equally costly to produce</li>
      <li>Mating is exogamous (no inbreeding)</li>
      <li>Every male and female is equally likely to be chosen as mate</li>
    </ul>
  </li>
  <li>Nash equilibrium:
    <ul>
      <li>Occurs when the population maintains 1:1 sex ratio</li>
    </ul>
  </li>
  <li><strong>Initial sex ratio not 1:1</strong>
    <ul>
      <li>If this was the starting state, the minority sex will naturally be more popular, and more opportunity to mate</li>
      <li>Parents will also choose to birth the minority sex since the minority sex is more competitive</li>
      <li>Over a few generation, the ratio will equalise at 1:1</li>
    </ul>
  </li>
  <li><strong>Parents cannot choose sex of offspring (reality)</strong>
    <ul>
      <li>I don’t understand the explanation given by the book, why sex ratio will still equalise at 1:1</li>
      <li>I think the book says that, the minority sex is more competitive and have more offspring</li>
      <li>Therefore, those offspring inherits the innate ability/gene/attribute to have a higher chance of producing the minority sex</li>
    </ul>
  </li>
  <li><strong>If one sex is more costly to produce</strong>
    <ul>
      <li>If e.g. female is 3x more costly to produce than male, then the system expects female to have 3x more offspring than male, to balance the tradeoff</li>
      <li>This is observed in some animal population, like ants (e.g 1 queen: 8 male ants)</li>
    </ul>
  </li>
  <li><strong>Optimisation process</strong>
    <ul>
      <li>The force is natural selection, so the currency is fitness (proxied by number of offspring)</li>
      <li>If this game is about conscious decisions, then currency is likely pleasure or other factors</li>
      <li>If the force is learning, then currency is primary reward</li>
      <li>Lags and spillovers are more likely for learned behaviour from evolution, instead of conscious decisions.</li>
    </ul>
  </li>
</ul>

<h1 id="hawk-dove-game-rights">Hawk-Dove Game (Rights)</h1>

<ul>
  <li>Two players, contesting over some kind of resource</li>
  <li>Each with two actions: Hawk (H) or Dove (D)</li>
  <li>Payoffs for players
    <ul>
      <li>If both player choose Hawk, they may split the resource but both pay the cost of aggressive contesting</li>
      <li>If players choose Hawk and Dove (or Dove and Hawk) combination, the Hawk player gains the resource, and the Dove player avoid paying the cost of contesting</li>
      <li>If both player choose Dove, they split the resource</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>If the cost of contesting is expensive (more expensive than potentially splitting the resource with another player), then (H,D) or (D,H) are the only equilibrium</li>
      <li>Which player gets the resource may be based on arbitrary events (like who were at the location first), but that player is expected to play Hawk</li>
      <li>Knowing that the first player will play Hawk, the second player will play Dove to avoid the cost of contesting</li>
      <li>This practice then becomes a self-fulfilling expectation</li>
    </ul>
  </li>
  <li>Uncorrelated asymmetry
    <ul>
      <li>The arbitrary event which player condition on to play Hawk depends on many factors, like context, culture, precedent, efficiency</li>
      <li>But the event has no relation with the payoffs of the game (uncorrelated)</li>
      <li>And the event is asymmetrical because it can be used to differentiate between the players</li>
    </ul>
  </li>
  <li><strong>Real World Asymmetries</strong>
    <ul>
      <li>Who was there first
        <ul>
          <li>Property rights in many countries and cultures are determined by who was there first</li>
          <li>Society is conditioned to accept that those who were at a location first has the rights to that piece of land and resource</li>
          <li>Society also accept that those who were there first will fight aggressively for their rights</li>
        </ul>
      </li>
      <li>Who has possession of the resource</li>
      <li>Who built it</li>
      <li>Some of these asymmetries were built into law and enforced as rights</li>
    </ul>
  </li>
  <li><strong>Shared Expectations</strong>
    <ul>
      <li>People need to recognise the rights and expect them to be enforced, in order to behave as predicted by the theory</li>
      <li>However, shared expectations is dependent on culture and environment (laws and rights are different across countries)</li>
    </ul>
  </li>
  <li><strong>At the Workplace</strong>
    <ul>
      <li>If your role is a leader or manager, and depending on who you are working with (your team members or your management) you might be expected to play Hawk or Dove</li>
      <li>If you find that you are stuck at work, you might not be grasping the shared expectation or playing the wrong move</li>
    </ul>
  </li>
  <li><strong>Internalised Racism/Sexism and Stockholm Syndrome</strong>
    <ul>
      <li>People may be playing Dove as a form of self-preservation</li>
      <li>The power asymmetries is real and can hurt they player</li>
    </ul>
  </li>
</ul>

<h1 id="costly-signalling-aesthetics">Costly Signalling (Aesthetics)</h1>

<ul>
  <li>Two players: Sender and Receiver
    <ul>
      <li>Sender may be of High type (desirable) or Low type, which is predetermined with some probability</li>
    </ul>
  </li>
  <li>Payoffs for players
    <ul>
      <li>Sender wants to be accepted by Receiver. Fixed payoff regardless of type.</li>
      <li>Receiver wants to accept Sender of High type. The payoff is lower for accepting a Low type.</li>
    </ul>
  </li>
  <li>Actions for Sender
    <ul>
      <li>Sender can send a costly signal to Receiver</li>
      <li>The cost is higher for a Low type than a High type Sender</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>The Sender only sends a signal, only if it is the High type</li>
      <li>The Receiver only accepts a Sender, if the Sender sends a signal</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>The signal must be easier to send for the High type, or more difficult to send for the Low type, for it to be effective</li>
      <li>The Low type Sender will avoid sending the costly signal as the payoff of getting accepted is not worth the cost</li>
      <li>If the signal becomes easier to send for all Senders, it will cease being used as it loses its effectiveness</li>
    </ul>
  </li>
  <li><strong>Peacock and Peahen</strong>
    <ul>
      <li>Peacock large tail is a costly signal, which poses a danger to the peacock (affects hunting and hygiene)</li>
      <li>It signals to the peahen that the peacock with a larger tail is fitter and more adept at surviving (High type)</li>
      <li>Which is why peahen chooses to mate with peacock with larger tail</li>
      <li>Peacock which are less fit, if they were unfortunately born with a large tail, they will have a hard time surviving before reaching maturity to mate</li>
    </ul>
  </li>
  <li><strong>Luxury goods</strong>
    <ul>
      <li>People infer something desirable from someone carrying luxury goods, otherwise those traits are hard to observe’</li>
      <li>The signal is wasteful (e.g. Rolex watches don’t functionally perform better than a Casio G-shock, or leather bags are definitely less durable and more destructible than plastic bags)</li>
      <li>The signal is less costly for some High type (which are supposedly rich and wealthy people)</li>
      <li>People like the signal less if it becomes easier for others to send the same signal</li>
    </ul>
  </li>
  <li><strong>Fattening</strong>
    <ul>
      <li>Some culture in South Nigeria, practice fattening the bride before a marriage</li>
      <li>It signals that the family can afford to fatten the bride in an environment where food is scarce</li>
    </ul>
  </li>
  <li><strong>Sugar and Spice</strong>
    <ul>
      <li>In late 17th century Europe, only the wealthy families can afford the use of sugar and spices in their meals</li>
      <li>But as the price of sugar and spice falls and becomes easily accessible to everyone, the wealthy families also changed their preference and taste</li>
    </ul>
  </li>
  <li><strong>Long pinky nail</strong>
    <ul>
      <li>A practice in olden days China, Thailand, Northeast India and Egypt</li>
      <li>It signals that these men don’t need to be involved in hard labour (must be wealthy or of certain status in the society)</li>
    </ul>
  </li>
  <li><strong>Pale skin</strong>
    <ul>
      <li>Valued in East and Southeast Asia</li>
      <li>Women who can maintain pale and fair skin signals that they don’t need to be working in the sun</li>
    </ul>
  </li>
  <li><strong>White dress shirt</strong>
    <ul>
      <li>Signals white collared job or wealth</li>
      <li>The environment allowed these people to easily keep their shirt clean and white</li>
      <li>Which contrasts with blue collared jobs. People are dressed in blue overalls and blue jeans to hide the dirt and oil stain</li>
    </ul>
  </li>
  <li><strong>Authenticity in art</strong>
    <ul>
      <li>Objectively speaking, there isn’t much functional difference between authentic and imitation art</li>
      <li>But being able to consume or possess authentic art signals wealth, as it is not accessible to most people</li>
    </ul>
  </li>
  <li><strong>Etiquette</strong>
    <ul>
      <li>Signals that you have been brought up well, which usually only occurs at well-to-do families</li>
    </ul>
  </li>
  <li><strong>Wine connoisseurship</strong>
    <ul>
      <li>Signals that you have class, not just cash</li>
      <li>Even if you can afford the wine, you may not be able to afford the training to appreciate the wine</li>
    </ul>
  </li>
  <li><strong>Rhyming</strong>
    <ul>
      <li>Signals talent and cleverness of the songwriter</li>
      <li>It is a self-imposed constraint which makes writing the song harder, and not everyone can achieve that</li>
    </ul>
  </li>
  <li><strong>Religious rules and worship</strong>
    <ul>
      <li>Joining a religious community will unlock a lot of benefits, which may create a free-rider problem</li>
      <li>The rites and rituals becomes a costly signal, so that only those who observe the rules may join the community and share in the benefits</li>
      <li>The community can then trust that those who joined will stick around when times are hard</li>
      <li>The more onerous the obligations, the longer the community will survive</li>
      <li>The greater the need for cooperation, the more onerous the practices</li>
    </ul>
  </li>
</ul>

<h1 id="buried-signal-modesty">Buried Signal (Modesty)</h1>

<ul>
  <li>This game is a modification on top of costly signalling game</li>
  <li>Some examples
    <ul>
      <li>Anonymous giving</li>
      <li>Concealing excitement (call me maybe?)</li>
      <li>Japanese Shibui (subtle virtuosity) and art where “less is more”</li>
    </ul>
  </li>
  <li>Possible explanations
    <ul>
      <li><strong>Many positive signals</strong>: the Sender is capable of sending so many different signals, that this particular signal in question is muted</li>
      <li><strong>Long-term relationships</strong>: the Sender is expecting the Receiver to find out about the signal at a later time</li>
      <li><strong>Outside options</strong>: the Sender is not interested in this particular Receiver picking up the signal</li>
      <li><strong>Devoted fans</strong>: the Sender is only expecting a particular group of Receiver (e.g. devoted fans) to pick up the subtle signal</li>
      <li><strong>Specific observers</strong>: the Sender only wants their targeted Receiver to receive the signal (e.g. identity of anonymous donors are actually known by the charity organisation)</li>
    </ul>
  </li>
</ul>

<h1 id="evidence-game-spin">Evidence Game (Spin)</h1>

<p>Three variants of a similar evidence game. This is commonly observed in any news, announcement from government and organisations.</p>

<h2 id="evidence-revelation">Evidence Revelation</h2>

<ul>
  <li>The world has two true states: High or Low</li>
  <li>There are two players: Sender and Receiver
    <ul>
      <li>Sender has a persuasive motive</li>
      <li>Sender wants Receiver to believe that the world state is High</li>
      <li>Payoffs for Sender is increasing the Receiver’s (posterior) perceived probability that world state is High</li>
    </ul>
  </li>
  <li>Conditions
    <ul>
      <li>Sender may or may not receive Evidence that reveals the world state</li>
      <li>Evidence is considered Supportive: probability of receiving High-state evidence is higher than receiving Low-state evidence</li>
    </ul>
  </li>
  <li>Actions
    <ul>
      <li>Sender can choose to reveal Evidence, if they receive it</li>
      <li>Receiver will update their (prior) belief about the world state, based on
        <ul>
          <li>Whether Evidence is supportive</li>
          <li>Whether Receiver expects Sender to reveal Evidence or not</li>
          <li>Whether Sender indeed reveals Evidence or not</li>
        </ul>
      </li>
      <li>There is no payoffs for Receiver</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Sender will only reveal Evidence that is supportive</li>
      <li>Receiver expects this as the Sender’s strategy and update their belief accordingly</li>
      <li>If Sender do not reveal any Evidence, Receiver will simply assume it wasn’t obtained, without considering if non-supportive Evidence was suppressed</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>People will likely present biased evidence when they have persuasion motive, as that is their dominant strategy</li>
      <li>Any evidence revealed will therefore be supportive</li>
      <li>It is easier to withhold evidence than to fabricate it</li>
    </ul>
  </li>
</ul>

<h2 id="evidence-search">Evidence Search</h2>

<ul>
  <li>The world is based on the above game, but with some modifications</li>
  <li>Actions
    <ul>
      <li>Sender can choose to search for Evidence or not (instead of being handed Evidence like game 1)</li>
      <li>Sender can choose to search with minimal effort (no cost), but will obtain an Evidence with lower probability</li>
      <li>Sender can choose to search with maximum effort (with some cost), but will obtain an Evidence with higher probability</li>
      <li>Then Sender can still choose to reveal the Evidence or not</li>
      <li>Receiver does not observe whether a search has been conducted or not, but is aware of these search options</li>
      <li>Receiver updates their belief based on all the same factors in game 1
        <ul>
          <li>And with expectation how hard the Sender has searched for the Evidence</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Sender will only search for supportive Evidence maximally if the cost is small relative to an increase in the Receiver’s posterior</li>
      <li>Receiver always expects the Sender to have searched for the Evidence maximally, whether it is revealed or not</li>
      <li>So even when shown supportive evidence, Receiver’s posterior may not increase much due to the expected bias search</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>Sender will never search for non-supportive evidence</li>
      <li>Sender will always search maximally if it is not too costly</li>
      <li>Sender will always be biased in their search when they have a persuasion motive, and that is what Receiver expects</li>
    </ul>
  </li>
</ul>

<h2 id="testing">Testing</h2>

<ul>
  <li>The world is based on game 1, but with some modifications</li>
  <li>Actions
    <ul>
      <li>Sender can choose to perform a Test to check the world state (from a set of possible Tests, each Test has some probability of generating Evidence)</li>
      <li>Tests are considered Confirmatory, if there is a high probability of obtaining Evidence related to world state</li>
      <li>Tests are considered Diagnostic, if the test is much more likely to generate supportive Evidence for the High state</li>
      <li>Sender may or may not obtain Evidence from the Test (depends on whether the test is confirmatory)</li>
      <li>Sender can then choose whether to reveal the Evidence or not</li>
      <li>Receiver does not observe which Test has been conducted, but is aware of the possible Tests</li>
      <li>Receiver updates their belief based on all the same factors in game 1
        <ul>
          <li>And with expectation which Test the Sender have chosen</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Sender will choose a Confirmatory Test</li>
      <li>Sender will also choose the Test that has higher chance to generate supportive Evidence</li>
      <li>Receiver will always expect Sender to choose a Confirmatory Test and may not update their posterior much</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>Sender will always choose confirmatory tests when they have a persuasion motive and the details of the test are hard to observe</li>
    </ul>
  </li>
</ul>

<h1 id="motivated-reasoning">Motivated Reasoning</h1>

<ul>
  <li>Last chapter explores how a Sender might try to persuade a Receiver</li>
  <li>This chapter explores how people might try to convince themselves</li>
  <li><strong>Overconfidence</strong>
    <ul>
      <li>People have the tendency to overestimate their abilities and underestimate their weaknesses</li>
    </ul>
  </li>
  <li><strong>Asymmetric updating</strong>
    <ul>
      <li>When presented supportive evidence about their abilities, people are more responsive to them</li>
      <li>When presented with evidence about their weakness, people tend to overlook those evidence</li>
    </ul>
  </li>
  <li><strong>Asymmetric search</strong>
    <ul>
      <li>When the weighing scale shows a desired weight, people will be happy and will walk away</li>
      <li>When the weighing scale shows over/under weight, people may try to test their weight again, hoping for a different outcome</li>
    </ul>
  </li>
  <li><strong>Attitude polarisation</strong>
    <ul>
      <li>Once people pick a side or form certain belief, it is hard to convince them otherwise even when presented with strong contrasting evidence</li>
    </ul>
  </li>
  <li><strong>Internalised persuasion</strong>
    <ul>
      <li>Using the above actions, people convince themselves to believe in a subject</li>
      <li>It is easier to convince someone else, if you truly believe in the subject as well (or it is harder to make a mistake and having your lie fall through, if you simply don’t lie)</li>
    </ul>
  </li>
</ul>

<h1 id="repeated-prisoners-dilemma-altruism">Repeated Prisoner’s Dilemma (Altruism)</h1>

<ul>
  <li>The game has two players playing in rounds</li>
  <li>In each round, the Prisoner’s Dilemma is played</li>
  <li>There is a probability that a new round will be played after each round (but also a possibility that the game will end)</li>
  <li>In Prisoner’s Dilemma, the players can choose to Cooperate (C) or Defect (D)</li>
  <li>The payoffs for the players
    <ul>
      <li>If player Cooperate, they pay a cost, and the other player will get the full benefit if they Defect (benefit is greater than cost)</li>
      <li>If both player Defects, they get nothing</li>
      <li>If both player Cooperate, they both get the benefit, minus the cost of Cooperating</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Players will consider past actions in their decision for each round</li>
      <li>Some strategy available are:
        <ul>
          <li>Tit for Tat: start with C, but play whatever the opponent played last round</li>
          <li>Grim Trigger: start with C, but switch to D permanently when opponent Defect</li>
          <li>Always Defect or Always Cooperate</li>
        </ul>
      </li>
      <li>Always Defect strategy can always be sustained in equilibrium</li>
      <li>Cooperative equilibrium like Tit for Tat and Grim Trigger can be sustained if the probability of a next round of game happening is high
        <ul>
          <li>Higher probability of repeated rounds making the risk of cooperating worth it</li>
          <li>The payoffs (benefit - cost) but also be enticing enough</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>To sustain cooperation, rounds must be repeated, and actions must be observable</li>
      <li>Cooperation is condition on other players also becoming cooperative in the future rounds</li>
      <li>Since Always Defect is always an available equilibrium, players will be sensitive to expectations, context, framing, etc. to influence other players to make cooperative decisions</li>
    </ul>
  </li>
</ul>

<h1 id="norm-enforcement">Norm Enforcement</h1>

<ul>
  <li>There are multiple players in the game (more than 2)</li>
  <li>Game is played over multiple rounds
    <ul>
      <li>First round, a random player is chosen to make a decision: Comply or not</li>
      <li>Complying has a personal cost to the player</li>
      <li>In subsequent rounds, players are randomly paired up</li>
      <li>Each player then chooses whether to punish their paired partner</li>
      <li>Punishment comes at a cost, and will hurt the other player by a certain amount</li>
    </ul>
  </li>
  <li>There is a probability whether the next round of the game will occur or the game will end</li>
  <li>Nash equilibrium
    <ul>
      <li>The first player will comply</li>
      <li>Subsequent rounds, players will punish anyone who didn’t comply in the first round (third party punishment)</li>
      <li>Players will also punish other players who didn’t punish those that should have been punished (higher-order punishment)</li>
      <li>This equilibrium occurs when probability of repeated rounds is high, and cost of complying or dishing out punishment is relatively cheaper than the hurt of the punishment</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>Observability must be high, shirking must be punished, and punishment itself must be incentivised</li>
      <li>Comply action in round one is arbitrary, depending on context and culture, which establishes the norm</li>
      <li>Higher-order beliefs is observed, as player need to recognise that a norm has been violated, and they must be motivated to dish out higher-order punishment</li>
    </ul>
  </li>
  <li>Practical advice on norm enforcement
    <ul>
      <li><strong>Increase observability</strong>
        <ul>
          <li>So that it is not easy to violate the norm in private</li>
        </ul>
      </li>
      <li><strong>Eliminate plausible excuses</strong>
        <ul>
          <li>So that it is not ambiguous when a norm is violated and punishment can be dished out</li>
        </ul>
      </li>
      <li><strong>Communicate expectation</strong>
        <ul>
          <li>So that people will know what is the norm and how to behave</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Motivations for punishment
    <ul>
      <li>Those who benefit from the norm might compensate the punisher</li>
      <li>Institutions might be developed to punish norm violation</li>
      <li>The punisher is motivated to punish, as a way to signal their own commitment to the norm</li>
      <li>Punishment isn’t always costly to the punisher</li>
    </ul>
  </li>
</ul>

<h1 id="categorical-norms">Categorical Norms</h1>

<ul>
  <li>The game is about coordination between players based on signals from the world state</li>
  <li>There are two variants of the game, continuous or discrete state</li>
</ul>

<h2 id="continuous-state">Continuous State</h2>

<ul>
  <li>The world state is between 0 and 1</li>
  <li>Players receive signal of the world state, with some margin of error from the true world state value</li>
  <li>Players then simultaneously choose their action in the coordination game to apply sanction or not
    <ul>
      <li>Payoffs for the players are not affected by the world state or the signal</li>
      <li>Players want to apply sanction only if other players also apply sanction</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Players may try to set a threshold, and apply sanction if the signal they receive is above the threshold value</li>
      <li>But there cannot be any equilibrium for threshold strategy, unless there is zero margin of error (every player receive the exact same precise signal)</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>Norms cannot be sustained if they are conditioned on continuous information, unless the information is error-free</li>
      <li>We can also reach an equilibrium if players can share information</li>
      <li>Therefore in the real world, governments will not use continuous value to coordinate their actions unless those values are error-free</li>
    </ul>
  </li>
</ul>

<h2 id="discrete-state">Discrete State</h2>

<ul>
  <li>Same game as the above continuous variant, except the world state is either exactly 0 or 1</li>
  <li>The players receive a signal that indicates either 0 or 1, but with also some margin of error (a chance to receive the opposite signal)</li>
  <li>Nash equilibrium
    <ul>
      <li>The players may try to apply sanction if they receive a signal value 1</li>
      <li>This will be in equilibrium if the margin of error is sufficiently small</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>As seen in the real world, governments and international bodies apply sanctions as long as a categorical norm has been violated</li>
      <li>They do not use continuous values (like number of casualties or degree of damage) as every player may receive a different signal</li>
      <li>It is hard to come to a consensus, and applying sanction wrongly carries risk for players</li>
      <li>Therefore, if a discrete categorical value is used (like, chemical weapon has been used, violating human rights), it is easy for all players to align and apply sanction</li>
    </ul>
  </li>
</ul>

<h1 id="higher-order-beliefs">Higher-Order Beliefs</h1>

<ul>
  <li>The base game is similar to Categorical Norms coordination game</li>
  <li>This chapter explores some variants</li>
</ul>

<h2 id="shared-signals">Shared Signals</h2>

<ul>
  <li>Conditional sanctioning can be an equilibrium (players sanction when they receive 1 signal) that is conditioned on the signal</li>
  <li>This requires the signal to be highly observable</li>
  <li>This can also work if the players are sharing their signal or observing a common signal</li>
</ul>

<h2 id="plausible-deniability">Plausible Deniability</h2>

<ul>
  <li>The game is modifies Shared Signals</li>
  <li>There is a chance for false positive (signal is 1, but world state is 0) but no chance of false negative (if signal is 0, world state must be 0)</li>
  <li>Players are informed of the chance of a false positive</li>
  <li>Nash equilibrium
    <ul>
      <li>Players can conditionally apply sanction if they receive positive signal, and know that the probability of false positive is low</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>The higher the chance of false positive, the more plausible deniability other players have</li>
      <li>Other players might have received a 1 signal but still chose not to apply sanction, since no one can see their signal</li>
    </ul>
  </li>
</ul>

<h2 id="higher-order-uncertainty">Higher-Order Uncertainty</h2>

<ul>
  <li>The game is modifies Shared Signals</li>
  <li>Player 1 knows the true world state</li>
  <li>Player 2 gets a noisy signal about the world state
    <ul>
      <li>There are no false positives (if signal is 1, world state is 1)</li>
      <li>There is a chance of false negative (if signal is 0, the world state may be 1)</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Player 1 sanctions if world state is 1</li>
      <li>Player 2 sanctions if they receive a signal 1</li>
      <li>This equilibrium cannot hold if chance of false negative is high, or if the chance of the true world state being 1 is low</li>
    </ul>
  </li>
</ul>

<h2 id="higher-order-signal">Higher-Order Signal</h2>

<ul>
  <li>Based on the higher-order uncertainty game with all the same conditions</li>
  <li>Player 1 additionally gets a noisy signal about the signal that player 2 receives with the following properties
    <ul>
      <li>No false positives: when player 2 gets a 1, player 1 also gets a 1</li>
      <li>Chance of false negative: when player 2 gets a 1, player 1 may get a 0 with certain probability</li>
    </ul>
  </li>
  <li>Nash equilibrium
    <ul>
      <li>Player 1 sanctions if they receive a signal saying that player 2 has received 1</li>
      <li>Player 2 sanctions if they receive a signal 1</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>The additional signal about other player’s signal only helps if the signal is observable</li>
      <li>The additional signal is useful if the other player’s signal is somewhat noisy (if not, the game reduces to the base game, where player 1 just need to condition on their own signal and knowledge)</li>
    </ul>
  </li>
</ul>

<h2 id="real-world-coordination">Real World Coordination</h2>

<ul>
  <li>It is therefore insufficient to only know the world state</li>
  <li>If coordination is required, we also need to know about other factors
    <ul>
      <li><strong>Observability</strong>: was the state easy to observe for other players?</li>
      <li><strong>Correlation</strong>: do other players have access to the same signal as you?</li>
      <li><strong>Plausible Deniability</strong>: can other players find excuses for their coordination action that is not related to the world state?</li>
      <li><strong>Higher-order Uncertainty</strong>: even if we know the world state, and the signal others have received, do they know about our knowledge?</li>
    </ul>
  </li>
  <li><strong>Symbolic Gestures</strong>
    <ul>
      <li>Apologies, or other gestures, may signal our intent and our possible coordination actions in the future, which others can depend on to condition their own action</li>
    </ul>
  </li>
  <li><strong>Indirect Communication</strong>
    <ul>
      <li>Helps to communicate our intent and higher-order beliefs, which others can use to condition their own actions</li>
      <li>But it is subtle, may not be observable to all players, and may be plausibly deniable</li>
      <li>Which makes it a good tool</li>
    </ul>
  </li>
  <li><strong>Omission-Commission Distinction</strong>
    <ul>
      <li>It is often more acceptable that inaction leads to a disaster, than a direct action leading to the same disaster (trolley problem)</li>
      <li>Because a direct action is observable, undeniable, and correlates to your intention, to cause the disaster</li>
      <li>So even if you did intent to cause disaster with your inaction, this cannot be observed</li>
      <li>Therefore, other players cannot punish you based on your omission</li>
      <li>But this assumptions only holds, if coordination is required to dish out punishment (e.g. vigilantes can always take matters into their own hands)</li>
    </ul>
  </li>
  <li>Some manifestation of omission-commission
    <ul>
      <li>Avoiding the ask: detour to avoid the Salvation Army</li>
      <li>Strategic ignorance: not getting tested for infectious disease, even when you know you are high risk</li>
      <li>Means vs by-product distinction:
        <ul>
          <li>Using humans as shields to stop an attack (means)</li>
          <li>Launching an attack but hurt some people due to collateral damage (by-product) is more acceptable</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="subgame-perfection-justice">Subgame Perfection (Justice)</h1>

<ul>
  <li>The game is the repeated prisoner dilemma</li>
  <li>The subgame perfect strategy is
    <ul>
      <li>Player 1 will not Defect, as long as Defection was always punished. If not, Player 1 will always try to Defect</li>
      <li>Player 2 will punish player 1 if player 1 has Defected this round, and Defection is always punished. If not, player 2 does not punish</li>
    </ul>
  </li>
  <li>Analysis
    <ul>
      <li>Transgressions are deterred in equilibrium by the threat of punishments</li>
      <li>And punishments must be incentivised. Whenever a transgression happens, punishment MUST be applied, to act as deterrence against further transgression</li>
    </ul>
  </li>
  <li><strong>Slippery Slope</strong>
    <ul>
      <li>If any transgression goes unpunished, the player will simply take more advantage in the future</li>
    </ul>
  </li>
  <li><strong>Apologies</strong>
    <ul>
      <li>If the transgression did not benefit the player, an apology may be sufficient (benefit weighed against punishment)</li>
    </ul>
  </li>
  <li><strong>Moral luck</strong>
    <ul>
      <li>A person may decide to cause harm but luckily the hurt did not materialise</li>
      <li>People tend to look past this, as intention is harder to observe, than materialised transgression</li>
    </ul>
  </li>
</ul>

<h1 id="hidden-role-of-primary-rewards">Hidden Role of Primary Rewards</h1>

<ul>
  <li>To answer the question of why some people become extremely passionate about certain things</li>
  <li><strong>Requires Time</strong>
    <ul>
      <li>Passion requires investment of time</li>
      <li>But the tradeoff is a potential for substantial primary rewards (fame, legacy, respect, romantic opportunities)</li>
    </ul>
  </li>
  <li><strong>Social Value</strong>
    <ul>
      <li>Passion must have social value</li>
      <li>People will hardly develop passion for something that has no social value, as they will not deem it as a good use of their time</li>
      <li>Social recognition provides a helpful feedback signal for us to know if the social rewards will be a sufficient payoff</li>
    </ul>
  </li>
  <li><strong>Strengths and Weaknesses</strong>
    <ul>
      <li>People tend to develop passion for something they are good at</li>
      <li>And because they are bad at other stuff, which if they invest their time to develop those other skills, it will be a waste and unproductive</li>
    </ul>
  </li>
  <li><strong>Economics of Superstar</strong>
    <ul>
      <li>Being able to obtain fame and fortune is a motivation that drives passion</li>
    </ul>
  </li>
  <li><strong>Probability of Success</strong>
    <ul>
      <li>If you have a higher chance to be a superstar (due to connections, opportunities etc.) then you will be more likely to pursue your passion</li>
    </ul>
  </li>
</ul>

<h1 id="tips-on-analysing-hidden-games">Tips on Analysing Hidden Games</h1>

<ul>
  <li>Focus on primary rewards</li>
  <li>Set assumptions to model the game</li>
  <li>Break each of the assumptions to see if they correspond with reality</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="economics" /><summary type="html"><![CDATA[[!info] title: Hidden Games: The Surprising Power of Game Theory to Explain Irrational Human Behaviour author: Moshe Hoffman, Erez Yoeli published: 2023 edition: 1 ISBN: 978-1529376845]]></summary></entry><entry><title type="html">Notes for: Behavioral Economics: The Basics</title><link href="https://twydev.github.io/notes/behavioral-economics-the-basics/" rel="alternate" type="text/html" title="Notes for: Behavioral Economics: The Basics" /><published>2025-05-27T00:00:00+00:00</published><updated>2025-05-27T00:00:00+00:00</updated><id>https://twydev.github.io/notes/behavioral-economics-the-basics</id><content type="html" xml:base="https://twydev.github.io/notes/behavioral-economics-the-basics/"><![CDATA[<blockquote>
  <p>[!info]
title: Behavioral Economics: The Basics
author: Philip Corr, Anke Plagnol
published: 2023
edition: 2
ISBN: 978-0367764326</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<p>Behavioural economics uses insights from social sciences and psychology to inform economic thinking and theorising.</p>

<ul>
  <li>The fundamental problem of economics: how to allocate scarce resources to maximise some benefit or “utility”</li>
  <li>Mainstream economics emphasises the depiction of ECON (homo economicus)
    <ul>
      <li>Concerned with how economic agents should behave under certain conditions</li>
      <li>ECON is cold, rational, calculating, self-interested</li>
    </ul>
  </li>
  <li>Behavioural economics emphasises the depiction of HUMAN (homo psychologicus)
    <ul>
      <li>Concerned with how people actually behaves in reality</li>
      <li>HUMAN is limited in processing capability, and prone to biases, errors, influences, emotions in how they make decisions</li>
    </ul>
  </li>
  <li>There are many examples of people “misbehaving” and deviating from how ECON is assumed to act under certain economic conditions and assumptions
    <ul>
      <li>People would go out of their way to save $10 on a $20 item, but would not do so to save $10 on a $1000 item. We are thinking in terms of discount on the item value, but the savings are absolutely equal, both are $10.</li>
      <li>In 2008 global financial crisis, both the banks and the consumers took too much risk beyond what an ECON would do.</li>
      <li>NYC taxi drivers plan their work one day at a time, instead of optimising their income for the entire month or year, which defies traditional economic thinking.</li>
      <li>GDP or GNP does not measure happiness, and people may behave and make decisions that maximise their happiness, which is not adequately reflected in GDP numbers.</li>
    </ul>
  </li>
  <li>Perhaps, the ECON model is a bad approximation of human economic behaviour after all</li>
</ul>

<h1 id="history-of-economics">History of Economics</h1>

<blockquote>
  <p>[!quote]
The famous Chicago economist George Stigler was fond of saying that there was nothing new in economics; Adam Smith had said it all.
<em>Thaler, 2015</em></p>
</blockquote>

<h2 id="classical-economics">Classical Economics</h2>

<p>Key ideas of classical economics:</p>

<ul>
  <li>Prudent (people’s tendency to look after themselves) self-interest benefit the whole society by allocating scarce resources efficiently</li>
  <li>The individual knows what is best for them</li>
  <li>Government intervention will lead to inefficiencies</li>
  <li>Free trade benefits the whole economy</li>
  <li>Efficient productivity comes from division of labour</li>
  <li>Accumulation of profit and allocation of capital are necessary for efficient production processes</li>
  <li>Private capitalism is for the good of the public, and deprivations (like subsistence level of wages) are the necessary tradeoff</li>
  <li>International trade is required to overcome the scarcity of natural resources limiting increase in productivity</li>
  <li>Laws and punishments are necessary to regulate free economic system (to deter immoral economic behaviour from affecting general efficiency of trade)</li>
</ul>

<p>Influential thinkers and their main ideas:</p>

<ul>
  <li>Adam Smith (1723-1790)
    <ul>
      <li>Prudence drives people to optimise their self-interest, but moral impulses also causes people to empathise and act for the benefit of others, because humans are social creatures.</li>
      <li>Greatest happiness in life comes not from materialism but from companionship of fellow men and women.</li>
      <li>Justice system is required to regulate individual behaviours and maintain social harmony.</li>
      <li>Individuals optimising their own interest leads to efficient production and benefit the society as a result (invisible hands)</li>
    </ul>
  </li>
  <li>David Ricardo (1772-1823)
    <ul>
      <li>Comparative cost advantage (absolute cost advantage is not necessary) allows all parties in international trade to benefit</li>
      <li>Law of diminishing marginal returns</li>
      <li>Iron Law of Wages, which says paying wages above the level of subsistence would erode work motivation</li>
    </ul>
  </li>
  <li>Jeremy Bentham (1748-1832)
    <ul>
      <li>Utilitarianism: greatest happiness of the greatest number should be the foundation of morals and legislation.</li>
      <li>Consequentialism: actions, policies, rules, should be judged based on their consequences, or the utility that they yield</li>
    </ul>
  </li>
  <li>Thomas Robert Malthus (1766-1834)
    <ul>
      <li>Food supply grows arithmetically but population grows geometrically</li>
      <li>People can change their behaviour and curb population growth when appropriately incentivised</li>
    </ul>
  </li>
  <li>Jean-Baptiste Say (1767-1832)
    <ul>
      <li>Say’s Law: supply creates its own demand</li>
    </ul>
  </li>
  <li>John Stuart Mill (1806-1873)
    <ul>
      <li>Developed the idea of Opportunity Cost</li>
      <li>The utility of society is maximised by allowing people to make their own free choices (market economy)</li>
    </ul>
  </li>
  <li>William Stanley Jevons (1835-1882)
    <ul>
      <li>Marginal view of economic decisions</li>
      <li>Diminishing marginal utility of consumption</li>
    </ul>
  </li>
</ul>

<h2 id="neoclassical-economics">Neoclassical Economics</h2>

<ul>
  <li>A general approach to economics (still rooted in classical) with a set of common assumptions and principles</li>
  <li>Still assumes the ECON model but is more relaxed about assumptions</li>
</ul>

<p>General principles:</p>

<ul>
  <li>People have consistent preferences “revealed” through their choices</li>
  <li>People behave “as if” their decisions and judgements can be derived from logical reasoning</li>
  <li>People strive to maximise their “utility”, and optimise allocation of scarce resources</li>
  <li>People process information in an unbiased way</li>
  <li>People are very sensitive and reactive to incentives</li>
  <li>People are “selfish” and place their welfare above others</li>
  <li>Self-interest of individuals benefits society</li>
  <li>Government interventions likely leads to inefficiency</li>
  <li>Economic principles are best expressed in mathematical forms, construct models from basic assumptions/tenets/principles/axioms to arrive at logical conclusions</li>
</ul>

<p>Clearly, the reality of actual human behaviour deviates from these assumptions and principles, sometimes by a large margin.</p>

<h2 id="critics-of-neoclassical-economics">Critics of Neoclassical Economics</h2>

<ul>
  <li>Thorstein Veblen (1857-1929)
    <ul>
      <li>Conspicuous consumption: production of socially valued artefacts at the expense of general welfare of society</li>
      <li>Economic life serves social ends, which is not captured by neoclassical ideas</li>
    </ul>
  </li>
  <li>John Kenneth Galbraith (1908-2006)
    <ul>
      <li>Firms conspiring against the welfare of consumers (also mentioned by Adam Smith)</li>
    </ul>
  </li>
  <li>John Maynard Keynes (1983-1946)
    <ul>
      <li>Failure of market equilibrium</li>
      <li>Economic conditions are influenced by “animal spirit” and do not merely reflect evaluation of numerical facts</li>
      <li>Use fiscal means to smooth out the troughs and peaks of business cycle</li>
    </ul>
  </li>
</ul>

<h2 id="reality-of-economics">Reality of Economics</h2>

<ul>
  <li>Economics is not at all like natural sciences</li>
  <li>Variables that make up the economic system are fluid, changing, subject to influence from social and political environment, conditioned by individual and collective psychology</li>
  <li>Economic system is dynamic and people are active participants in it</li>
</ul>

<p>And these are what behavioural economics assumes</p>

<h1 id="econ-homo-economicus">ECON (Homo Economicus)</h1>

<ul>
  <li>The assumptions and principles of neoclassical economics are exemplified by the ECON: a rational person. The main tenets are:
    <ul>
      <li>Rationality, expressed in consistent (maybe latent/hidden) preferences revealed in choice behaviour when incentives are sufficient to motivate individuals</li>
      <li>Individuals aim to maximise personal utility by allocating their scarce resources most efficiently by employing good-enough mathematical (may not be conscious) calculations</li>
      <li>Individuals act on full and relevant information (or at least good enough information)</li>
      <li>Individuals look out for themselves first and foremost, and when they help others, it is assumed that it leads to an increased in their own utility (warm glow effect from charity), this is an example of non zero sum game</li>
    </ul>
  </li>
  <li>These simplifying assumptions allows rigorous mathematical modelling of human behaviour</li>
  <li>Therefore, neoclassical economics is also characterised by being more normative: it prescribes or predicts what should happen, which often deviates from what really happened</li>
  <li>Marginalism
    <ul>
      <li>Alfred Marshall focuses his work on “the firm” economic entity, the focus of the subfield of Microeconomics</li>
      <li>Introduces demand and supply graphs, which represents the concepts of market equilibrium, law marginal of utility/returns, and price elasticity</li>
      <li>Firms and consumers making decision using marginal analysis leads to the most efficient outcome for resource allocations</li>
    </ul>
  </li>
  <li>Consistent Preferences
    <ul>
      <li>Related to the Rational Choice Theory</li>
      <li>Assumes that people are innately motivated to have a preference for certain choices and they are not influenced by how the choice outcomes communicated/framed</li>
      <li>Completeness: the consumer can rank their choices in a preference ordered list</li>
      <li>Transitivity: if the consumer prefers A over B, and B over C, then they must prefer A over C</li>
      <li>More of a good is always better, with all else being equal</li>
      <li>Continuity: a small change in a bundle of goods should not lead to a jump in preferences</li>
      <li>Convexity: mixtures of goods (averages) are preferable to extremes</li>
    </ul>
  </li>
  <li>Rationality
    <ul>
      <li>The rational behaviour expected by neoclassical economics (encompasses all the above assumptions) may not align with how decisions are made in reality:</li>
      <li>People often consider sunk cost when making decisions. But they should only consider the marginal analysis and seek to maximise their utility.</li>
      <li>People often do not consider their opportunity cost when making decisions.</li>
      <li>People often do not react to incentives the way that economics have predicted</li>
      <li>Given full information, people still did not behave in informed or unbiased way</li>
    </ul>
  </li>
  <li>Expected Value/Utility Theory (EUT)
    <ul>
      <li>Risk: possible outcomes are known, and each outcome has some assigned probabilities</li>
      <li>Uncertainty: outcomes are hard to predict and hard to know the probability profile</li>
      <li>The theorem (by von Neumann and Morgenstern) is a form of precise mathematical reasoning for rational decision making to arrive at optimal outcome considering the risks</li>
      <li>However, in reality the probability are subjective to the person playing the economic game, and different people may then derive a different expected utility from the situation</li>
      <li>Even when real people makes the theoretical rational choice, they may unfortunately obtain a bad outcome in the short term. They may not have sufficient resources to play the same game for the long term (which is why people may be loss averse in reality)</li>
      <li>People may make choices that deviates from EUT when the choices are framed differenly</li>
      <li>People may also be “present bias” and prefers obtaining outcome in the short term than the long term</li>
    </ul>
  </li>
  <li>Bounded Rationality
    <ul>
      <li>People are not perfect, but under certain conditions, people still behaves rationally and therefore the economic theories are still relevant and can even be good predictors</li>
    </ul>
  </li>
  <li>Heterodoxy
    <ul>
      <li>The market in reality is not the perfectly competitive market structure described by Alfred Marshall</li>
      <li>It is made up of small and big firms</li>
      <li>Firms uses advertising and collusion to maximise their profit</li>
      <li>Within firms, management are optimising other utility like their salary or free time, which can happen at the expense of overall welfare of society</li>
    </ul>
  </li>
</ul>

<h1 id="human-homo-psychologicus">HUMAN (Homo Psychologicus)</h1>

<p>Common behaviour of HUMAN that is not accounted for by neoclassical economics:</p>

<ul>
  <li>Loss Aversion
    <ul>
      <li>People have a stronger tendency to prefer avoiding losses to achieving gains of the same magnitude</li>
      <li>Endowment effect: when asked to price their own cup vs the same cup on the market, people tend to price their possession higher (people value the things they own much higher than the same things they do not)</li>
    </ul>
  </li>
  <li>Money Mental Accounting
    <ul>
      <li>Scenario 1: you are going for a concert, ticket costs $10, but you lost $10 before you reach the venue. People are likely to go ahead and buy the ticket.</li>
      <li>Scenario 2: you already bought the concert ticket, but when you reach the venue, you realised you lost the ticket. People are less likely to buy the ticket again.</li>
      <li>(People tend to compartmentalise their money for different purposes even though the money is fungible)</li>
    </ul>
  </li>
  <li>Availability Heuristic
    <ul>
      <li>E.g. thinking that you are more likely to encounter a terrorist attack (People tend to overestimate the frequency of occurrence of an event if that event easily springs to mind)</li>
      <li>Illusory correlation: the first few people you met in a foreign country are friendly, so you assume everyone in this country are generally friendly (perceive a relationship between two unrelated event because they come to mind at the same time)</li>
      <li>Confirmation bias: we already have a preconceived belief, and any events we encounter, we simply choose to think that they confirm our belief</li>
    </ul>
  </li>
  <li>Representativeness Heuristic
    <ul>
      <li>Stereotype</li>
      <li>Dilution Effect: when more facts are provided about a subject, it helps to dilute the representativeness (breaking stereotype)</li>
      <li>Base Rate Fallacy: people tends to over/under estimate the probability of an event occurring</li>
    </ul>
  </li>
  <li>Conjunction Fallacy
    <ul>
      <li>Scenario: Alex is a boy that loves pokemon. Which of the following sentence is more likely to be true?</li>
      <li>Sentence 1: Alex is a student.</li>
      <li>Sentence 2: Alex is a student who collects pokemon cards.</li>
      <li>People tend to choose the Sentence 2, when in fact Sentence 1 than Sentence 2, will always have equal or higher probability of being true.</li>
      <li>The conjunction does not contribute to higher probability, but people that to think it does.</li>
    </ul>
  </li>
  <li>Anchoring and Adjustment Heuristic
    <ul>
      <li>When asked to estimate a figure, if a person is provided a number, they tend to anchor to that number and make adjustments around it, even if the number is very far off from the correct figure (Coherent Arbitrariness)</li>
    </ul>
  </li>
  <li>Affect Heuristic
    <ul>
      <li>Our emotions about a given scenario and options influence our decision</li>
    </ul>
  </li>
  <li>Framing Effect
    <ul>
      <li>The way options are presented influence the decision people make</li>
      <li>Prominence effect: when an option emphasise on personal/individual impact, that option exerts more influence on people, than an option that talks about the general population</li>
    </ul>
  </li>
</ul>

<h2 id="systemic-deviation-from-rationality">Systemic Deviation from Rationality</h2>

<ul>
  <li>Risk Aversion
    <ul>
      <li>Instead of making decisions based on EUT, people prefers to eliminate risk all together instead of maximising expected value</li>
    </ul>
  </li>
  <li>Ambiguity/Uncertainty Aversion
    <ul>
      <li>People also tend to prefer making decisions in scenarios where all possible outcomes are known</li>
    </ul>
  </li>
</ul>

<h2 id="prospect-theory">Prospect Theory</h2>

<ul>
  <li>A simple model to accommodate the separate effects of the above</li>
  <li>Represents human judgement and decision making under uncertainty</li>
  <li>It is about how people actually behave, instead of how they should behave</li>
  <li>It has the following focus:
    <ul>
      <li>Certain outcomes are preferred over uncertain ones, even when uncertain outcomes offer greater expected utility</li>
      <li>Greater sensitivity to loss than to gain, of the same magnitude</li>
      <li>Decisions about loss and gain are made from a reference point, not in absolute terms</li>
      <li>How information is framed is critical, and the same content framed differently can lead to different behavioural outcomes</li>
    </ul>
  </li>
  <li>Status Quo Bias
    <ul>
      <li>People tend to prefer to stay with the status quo</li>
      <li>Changes are perceived as incurring loss, due to possible “transaction cost” associated with making a change (e.g. hassle, mental effort)</li>
    </ul>
  </li>
</ul>

<h2 id="system-1-and-2-thinking">System 1 and 2 Thinking</h2>

<ul>
  <li>From Daniel Kahnemann (Thinking Fast and Slow)</li>
  <li>System 1 is fast, reflexive, automatic, biased, intuitive, emotional, habitual, non-conscious, prepotent. Also called implicit/procedural.</li>
  <li>System 2 is slow, reflective, controlled, effortful, can be conscious. Also called explicit/declarative.</li>
  <li>Problem of neoclassical economics
    <ul>
      <li>Most activities are carried out by System 1 thinking</li>
      <li>The perfectly rational ECON agent requires System 2 thinking for all decisions</li>
      <li>Therefore neoclassical assumptions are fundamentally at odds with how our mind works</li>
    </ul>
  </li>
</ul>

<h1 id="social-emotional-personality-factors">Social, Emotional, Personality Factors</h1>

<p>Other factors that affects human behaviours:</p>

<ul>
  <li>Priming
    <ul>
      <li>The scientific theory which states that thoughts, emotions, acts make further thoughts, emotions, acts more readily accessible</li>
    </ul>
  </li>
  <li>Prudent Self-interest and Trust
    <ul>
      <li>People coordinate their behaviour to ensure harmonious social relations</li>
      <li>Non-selfish cooperative behaviour is also observed even in non-repeated/one-time games</li>
    </ul>
  </li>
  <li>Nash Equilibrium
    <ul>
      <li>best equilibrium in non-cooperative games involving two or more players</li>
      <li>People in reality often violates Nash equilibrium</li>
    </ul>
  </li>
  <li>Inequality Aversion
    <ul>
      <li>People make decision to avoid being seen as unfair, or they feel guilty of taking too much advantage</li>
    </ul>
  </li>
  <li>Social Norms
    <ul>
      <li>It is the accepted rules of behaviour, communicated and received implicitly</li>
      <li>It leads to compliance, obedience, and conversion</li>
      <li>Violating the rules leads to punishment, which is typically social exclusion</li>
      <li>It helps reduce uncertainty about how to behave appropriately, in ambiguous situation</li>
      <li>It helps to coordinate individual’s behaviour, facilitate group cohesion</li>
      <li>It helps constrain an individual’s impulsive response, and reduce cognitive and emotional load</li>
    </ul>
  </li>
  <li>Emotion and Mood
    <ul>
      <li>The current emotion and mood of a person will affect their decision, even if their decision is made for an outcome that only arrives some time in the future.</li>
      <li>Empathy Gap: being in a particular emotional state now (e.g. happy) may make people less able to empathise with others in an opposite emotional state (e.g. sad)</li>
    </ul>
  </li>
  <li>Personality
    <ul>
      <li>There are many different framework, that classifies personality</li>
      <li>In general, personality that is more responsive to rewards may make more selfish decisions (in economic games) and personality that is more agreeable to others may make more cooperative decisions</li>
      <li>The personal preference that is mentioned by neoclassical economics may ultimately depend on personality</li>
      <li>Economic success (employment) of an individual is correlated to their self-control and conscientiousness personality</li>
      <li>Cognitive Dissonance: people change their attitudes/beliefs to be consistent with behaviours that have already performed, even when they were initially against those attitudes/beliefs</li>
    </ul>
  </li>
</ul>

<h1 id="nudge">Nudge</h1>

<ul>
  <li>Concept of Nudge
    <ul>
      <li>People do not make good decisions (in terms of their own true preferences)</li>
      <li>The nudge is any aspect of the choice architecture that alters behaviour in a predictable way without forbidding any options or changing economic incentives</li>
      <li>A nudge must be easy and cheap to avoid</li>
      <li>According to Nudge, the choice architect do not decide what is best for people, because people still have the freedom to choose any choice (this is merely Libertarian Paternalism)</li>
    </ul>
  </li>
  <li>Choice Architecture
    <ul>
      <li>The design of how choices are presented</li>
      <li>A choice architect makes decision on how to design the choices</li>
    </ul>
  </li>
  <li>BASIC model (Danish)
    <ul>
      <li>Behavioural mapping: collect data to define the problem</li>
      <li>Analysis: why people are behaving the way they are currently</li>
      <li>Solution mapping: scientific and systematic process of making suggestions</li>
      <li>Interventions: test the possible nudges before full implementation</li>
      <li>Continuation: ongoing monitoring and adjustment of the implemented solution</li>
    </ul>
  </li>
  <li>MINDSPACE framework (UK)
    <ul>
      <li>Messenger: who is delivering the message greatly influence the receiver</li>
      <li>Incentive: the timing, type, and magnitude of incentive matters. Social aspects of incentives also matters. The mind takes mental shortcuts (system 1 thinking), and people can be incentivised to adjust their behaviour through messaging.</li>
      <li>Norms: reinforcing and reminding social norms influences social behaviours</li>
      <li>Defaults: people tend to go with the flow and avoid making changes, so the default option is a powerful tool to fix their choice</li>
      <li>Salience: information that are novel, accessible, and simple, captures more of our attention, and what captures more attention will also be given more cognitive processing</li>
      <li>Priming: subconscious cues and stimuli like sights, sounds, smells can prime subsequent behaviour</li>
      <li>Affect: emotional association</li>
      <li>Commitment: people seek to be consistent with their public promises and reciprocate acts</li>
      <li>Ego: people act in ways that makes them feel better about themselves</li>
    </ul>
  </li>
  <li>EAST framework (UK)
    <ul>
      <li>Easy, Attractive, Social, Timely</li>
    </ul>
  </li>
  <li>Critics on the theory
    <ul>
      <li>The nudge choice architecture may not be making people better off as judged by themselves, but they may be better off as judged by the choice architect</li>
      <li>The best choice may be subjective as it depends on situation, and the people may have certain true preferences, that contrast with the best choice that the architect assumed</li>
      <li>According to the Nudge theory, the chooser bears the responsibility of the choice, but what if a poor default option set by the choice architecture unfairly burdens the chooser</li>
      <li>The choice architecture relies on the architect being benign and acts in the interest of the chooser</li>
    </ul>
  </li>
  <li>Social Comparison and Subjective Well-being
    <ul>
      <li>People tend to feel that the measure of their happiness and well being depends on they compare to their peers</li>
    </ul>
  </li>
</ul>

<h1 id="commercial-and-political-persuasion">Commercial and Political Persuasion</h1>

<ul>
  <li>Evaluative Conditioning
    <ul>
      <li>Powerful and ubiquitous phenomenon in all areas of life</li>
      <li>Change in attitude toward some stimulus</li>
      <li>Pairing one stimulus (that evokes certain valence) to another one to evoke the same response</li>
      <li>E.g. Lewis Hamilton advertising Rimowa luggage</li>
    </ul>
  </li>
  <li>The Butterfly Effect (Sutherland)
    <ul>
      <li>Finding a small, preferably inexpensive nudges, that have disproportionately large effect on behaviour</li>
    </ul>
  </li>
  <li>Psychological Value
    <ul>
      <li>People perceive certain goods, typically branded, or luxury, goods with additional intangible psychological value, and this affects their purchase decisions</li>
      <li>Veblen Goods: demand of the goods increases as price increases</li>
    </ul>
  </li>
  <li>Commercial Preferences
    <ul>
      <li>Experiments and commercial successes by advertising firms have proven that consumers are susceptible to all sorts of nudges</li>
      <li>Effective advertisements may have no rational content</li>
      <li>Familiarity principle: mere exposure to advertisements can influence people</li>
      <li>Social proof: people tend to mimic the behaviour they see</li>
      <li>Internet shopping: uses browser activity and behaviour tracking to nudge people to make purchase decisions</li>
    </ul>
  </li>
</ul>

<h2 id="techniques-of-persuasion">Techniques of Persuasion</h2>

<ul>
  <li>Information Processing Model
    <ul>
      <li>Before 1980s, most theories of persuasion assumes systematic processing (the recipient change their attitude because of the message they processed)</li>
    </ul>
  </li>
  <li>Cognitive Response Model
    <ul>
      <li>Late 1960s, this alternative model states that response elicited is determined by what the recipient heard rather than what is said</li>
    </ul>
  </li>
  <li>A/B Testing
    <ul>
      <li>Testing the effectiveness of different variation of messages</li>
    </ul>
  </li>
  <li>Transaction utility
    <ul>
      <li>People perceived that the same goods sold by a luxury place should cost more, even if it does not offer higher consumption utility</li>
    </ul>
  </li>
  <li>Customer journey
    <ul>
      <li>In a physical store, guide the customers through sections to entice more purchases</li>
    </ul>
  </li>
  <li>Buyer’s Remorse and Regret Theory
    <ul>
      <li>Consumers wants to avoid making a bad and regretful purchase, and may decide on a more expensive but more “trusted” brand of the same goods</li>
    </ul>
  </li>
  <li>Reactance Theory:
    <ul>
      <li>Trying to get people to not consume certain goods may backfire and make them consume more of those goods.</li>
    </ul>
  </li>
  <li>Anchoring Effects
    <ul>
      <li>Saying that one customer is only allowed to buy one piece of an item creates the illusion that this is a popular item that must be bought</li>
    </ul>
  </li>
  <li>Framing
    <ul>
      <li>Saying that digital payment enjoys $2 discount is more acceptable than saying that cash payment will suffer a $2 penalty (this is also related to Loss Aversion)</li>
    </ul>
  </li>
  <li>Decoy Pricing
    <ul>
      <li>Given 3 choices, Standard, Premium, and Super Premium, people tend to go with the middle Premium choice</li>
      <li>Having a decoy option helps to nudge people into making certain purchase</li>
      <li>Attraction Effect: it is easier to compare options if they have similar attributes</li>
      <li>Compromise Effect: people tend to avoid extremes (also called Extremeness Aversion). Perhaps they don’t want to be labelled as a “cheapskate” or a “show off” so they go with the middle option.</li>
    </ul>
  </li>
  <li>Hedonic Adaptation
    <ul>
      <li>Pleasure, satisfaction, utility from goods and services wane over time.</li>
      <li>People need another hedonic stimulus to return to previous hedonic level.</li>
      <li>This demands that commercial goods and services are novel, exciting, and the “new thing to have”.</li>
      <li>Due to this hedonic treadmill, people are bad at forecasting how satisfied they will be before they made their purchases</li>
    </ul>
  </li>
  <li>Social Media
    <ul>
      <li>The goldmine of behavioural data</li>
      <li>Also a good platform to nudge their behaviour</li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>[!quote]
The first principle is that you must not fool yourself and you are the easiest person to fool.
<em>Richard Feynman, 1985</em></p>
</blockquote>

<h1 id="other-books-mentioned">Other Books Mentioned</h1>

<ul>
  <li><strong>Misbehaving</strong>, Richard Thaler, 2015</li>
  <li><strong>The Theory Of Moral Sentiments</strong>, Adam Smith, 1759</li>
  <li><strong>An Inquiry into the Nature and Causes of the Wealth of Nations</strong>, Adam Smith, 1776</li>
  <li><strong>The Theory of the Leisure Class</strong>, Thorstein Veblen, 1899</li>
  <li><strong>The Affluent Society</strong>, John Kenneth Galbraith, 1958</li>
  <li><strong>New Industrial State</strong>, John Kenneth Galbraith, 1967</li>
  <li><strong>The General Theory of Employment, Interest, and Money</strong>, John Maynard Keynes, 1936</li>
  <li><strong>Principles of Economics</strong>, Alfred Marshall, 1890</li>
  <li><strong>Theory of Games and Economic Behaviour</strong>, John von Neumann, Oskar Morgenstern, 1944</li>
  <li><strong>Predictably Irrational</strong>, Dan Ariely, 2008</li>
  <li><strong>The End of Alchemy, Money, Banking, and the Future of the Global Economy</strong>, Mervyn King, 2016</li>
  <li><strong>Nudge: Improving Decisions About Health, Wealth and Happiness</strong>, Richard Thaler, Cass Sunstein, 2008</li>
  <li><strong>Alchemy</strong>, Rory Sutherland, 2019</li>
  <li><strong>Confessions of an Advertising Man</strong>, David Ogilvy, 1983</li>
  <li><strong>Influence: The Psychology of Persuasion</strong>, Robert Cialdini, 2007</li>
  <li><strong>Scientific Advertising</strong>, Claude Hopkins, 1923</li>
  <li><strong>The New Psychology of Money</strong>, Adrian Furnham, 2014</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="economics" /><summary type="html"><![CDATA[[!info] title: Behavioral Economics: The Basics author: Philip Corr, Anke Plagnol published: 2023 edition: 2 ISBN: 978-0367764326]]></summary></entry><entry><title type="html">Notes for: I Have Something to Say</title><link href="https://twydev.github.io/notes/i-have-something-to-say/" rel="alternate" type="text/html" title="Notes for: I Have Something to Say" /><published>2025-05-27T00:00:00+00:00</published><updated>2025-05-27T00:00:00+00:00</updated><id>https://twydev.github.io/notes/i-have-something-to-say</id><content type="html" xml:base="https://twydev.github.io/notes/i-have-something-to-say/"><![CDATA[<blockquote>
  <p>[!info]
title: I Have Something to Say: Mastering the Art of Public Speaking in an Age of Disconnection
author: John Bowe
published: 2020
edition: 1
ISBN: 978-1400062102</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<p>In this book, the author described how his short time as a member of a Toastmasters club helped him overcome his fear of public speaking and establishing connection with his audience. Each chapter described how he approached one of the 10 exercises from the Toastmasters manual.</p>

<h1 id="key-takeaways">Key Takeaways</h1>

<ul>
  <li><strong>preparation</strong> and <strong>understanding the audience</strong></li>
  <li>Many members of the Toastmasters organisation found that their experience with the club also changed their personality.</li>
  <li>Learning to think from the perspective of the audience, have made them more inclined to think about other people in their daily interactions.</li>
  <li>Being prepared for a speech gives you more freedom and flexibility to dynamically make adjustments during the speech without losing focus.</li>
</ul>

<h1 id="tips-from-the-author">Tips from the Author</h1>

<p>The 5 steps to make a great speech.</p>

<h2 id="thinking-about-your-audience">Thinking About Your Audience</h2>

<ul>
  <li>What is the size of the audience, and composition (age, race, religion, gender, nationality, ethnicity, education etc.)</li>
  <li>What is the occasion for the speech and why are you chosen to speak</li>
  <li>What does the audience know about you and the topic</li>
  <li>What is the layout of the venue</li>
  <li>What is the program of the day in relation to your speech</li>
  <li>Will there be visual aids or printed materials etc.</li>
</ul>

<h2 id="define-the-purpose-for-speaking">Define the Purpose for Speaking</h2>

<p>Start with a single value proposition sentence</p>

<blockquote>
  <p>As a result of the speech, the audience will know X and respond by doing Y</p>
</blockquote>

<ul>
  <li>The purpose could be to merely inform or entertain</li>
  <li>Cut any unnecessary content that doesn’t serve the key purpose</li>
  <li>The audience at all time need to know 3 things
    <ul>
      <li>What are you talking about</li>
      <li>Why should they care</li>
      <li>What is in it for them</li>
    </ul>
  </li>
  <li>Every decision made in designing the speech must demonstrate that you are talking for the audience’s benefit</li>
</ul>

<h2 id="outline-and-organize">Outline and Organize</h2>

<p>The typical outline of a speech consists of an introduction and a conclusion, with 3-5 main parts in between. Here are the common organisational structures:</p>

<ul>
  <li><strong>Chronological</strong>: explains the topic along a timeline</li>
  <li><strong>Spatial</strong>: explains the topic according to physical space, location, or directional relationships</li>
  <li><strong>Topical</strong>: arranges information according to different areas within a larger topic or category</li>
  <li><strong>Cause and Effect</strong>: explains causes for a phenomenon and the resulting effects (or vice versa)</li>
  <li><strong>Problem and Solution</strong>: defines a problem and describes the solution</li>
</ul>

<p>Regardless of the structure chosen, we should inform the audience early about the structure we will be using.
At every transition, we should also mention where the speech is going.
This helps to keep the audience focused.</p>

<h2 id="compose-the-speech">Compose the Speech</h2>

<ul>
  <li><strong>Use Words Effectively</strong>
    <ul>
      <li>People are bad at listening. Do everything to help them hear and understand. Short words, sentences, paragraphs.</li>
      <li>Use physical, concrete, vivid images to appeal to the senses.</li>
      <li>Use active verb choices instead of abstract/passive language.</li>
      <li>Eliminate filler words and nonperforming words (“like”, “just”, “really” etc.)</li>
      <li>Eliminate lazy catch-phrases and cliches (“at the end of the day”, “it is what it is” etc.)</li>
      <li>Eliminate business jargon, pretentious words, bloated/imprecise expressions</li>
      <li>Eliminate slang/subculture expression that not all audience will understand</li>
      <li>Avoid vague descriptive words and be precise so that all audience have the same understanding</li>
      <li>Compose the speech using cognitive lexicon of the audience, and use localised and relatable examples</li>
    </ul>
  </li>
  <li><strong>Use Data Effectively</strong>
    <ul>
      <li>Use data sparingly</li>
      <li>Always cite the source of information, and use trusted sources that wouldn’t antagonise the audience</li>
      <li>Contextualise the data to local context of the audience</li>
      <li>Vary the sources of data (stats, scientific studies, personal anecdotes, witness testimony, historical records) to be expressive and stimulating to the audience</li>
      <li>Engage the audience by telling them how you feel about the data and why it matters (instead of letting the data speak for itself)</li>
    </ul>
  </li>
  <li><strong>Using Visual Aids</strong>
    <ul>
      <li>These helps to keep presentation fluid and lively (slides, charts, props, graphics, videos, physical demo)</li>
      <li>Keep it simple and avoid cluttering visual elements</li>
      <li>Limit the number of images (your voice is still the main focus)</li>
      <li>Never turn your back on the audience</li>
      <li>Never read from the slides</li>
      <li>Visual aids must be varied in colour and style. Stimulate the audience’s senses as much as possible</li>
    </ul>
  </li>
</ul>

<h2 id="practice-the-speech">Practice the Speech</h2>

<ul>
  <li><strong>Find your Voice</strong>
    <ul>
      <li>Start with reading the speech aloud.</li>
      <li>Measure the duration and ensure it is meets your objective. Average listeners can absorb 125-150 words per minutes, so pace accordingly.</li>
      <li>Continue practicing to enunciate words clearly and focus on the feel and sound of the words.</li>
      <li>Record the speech or practice in front of others</li>
      <li>Look for opportunities to vary the tone, speed, pitch, to correspond to the content</li>
      <li>Memorise the introduction and conclusion. This may help to avoid brain freeze at the start, and ensures that the speech ends strong with impact.</li>
    </ul>
  </li>
  <li><strong>Use your Body</strong>
    <ul>
      <li>Avoid unconscious gestures that will distract the audience from the speech</li>
      <li>Practice every part of the speech as if the actual delivery, to figure out how you will be moving on the stage</li>
      <li>Use gestures to emphasise certain points or enhance emotions of the speech</li>
      <li>Incorporate movements on stage. This avoids being too rigid, and allows you to interact with different sections of the audience</li>
      <li>Script your gestures if needed (where to pause, when to walk where), it will become more natural with practice</li>
    </ul>
  </li>
</ul>

<h1 id="other-books-mentioned">Other Books Mentioned</h1>

<ul>
  <li><strong>Ars Rhetorica</strong>, Aristotle</li>
  <li><strong>Words Like Loaded Pistols: Rhetoric from Aristotle to Obama</strong>, Leith, Sam</li>
  <li><strong>Simply Speaking: How to Communicate Your Ideas with Style, Substance, and Clarity</strong>, Noonan, Peggy</li>
  <li><strong>The Fall of Public Man</strong>, Sennett, Richard</li>
  <li><strong>Personally Speaking</strong>, Smedly, Ralph C.</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="communication" /><summary type="html"><![CDATA[[!info] title: I Have Something to Say: Mastering the Art of Public Speaking in an Age of Disconnection author: John Bowe published: 2020 edition: 1 ISBN: 978-1400062102]]></summary></entry><entry><title type="html">Notes for: The Return of Great-Power Diplomacy</title><link href="https://twydev.github.io/notes/the-return-of-great-power-diplomacy/" rel="alternate" type="text/html" title="Notes for: The Return of Great-Power Diplomacy" /><published>2025-05-27T00:00:00+00:00</published><updated>2025-05-27T00:00:00+00:00</updated><id>https://twydev.github.io/notes/the-return-of-great-power-diplomacy</id><content type="html" xml:base="https://twydev.github.io/notes/the-return-of-great-power-diplomacy/"><![CDATA[<blockquote>
  <p>[!info]
title: The Return of Great-Power Diplomacy: How Strategic Dealmaking Can Fortify American Power
author: Wess Mitchell
source: Foreign Affairs
published: 2025-05-01</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<p>The author is suggesting these diplomatic options for America because he believes the United States is in a precarious geopolitical situation that demands a fundamental shift in its foreign policy approach.
He argues that the post-Cold War era of American unipolarity, where the USA could rely primarily on military and economic might to achieve its goals and even attempt to transform other nations into liberal democracies, is over.</p>

<h1 id="motivations">Motivations</h1>

<ul>
  <li><strong>Return of Great-Power Rivalry:</strong> The author asserts that “great-power rivalry is back, and systemic war is a very real possibility.” The USA now faces formidable, continent-sized rivals in China and Russia, both with significant economic and military capabilities. The previous USA approach of attempting to “overpower everybody” is no longer viable.</li>
  <li><strong>Finite Means vs. Infinite Threats:</strong> The United States no longer has a military capable of fighting and defeating all its foes simultaneously, nor can it drive another great power to ruin through sanctions alone. There’s a growing “gap between the United States’ finite means and the virtually infinite threats arrayed against it.” Diplomacy, in its classical, hard-nosed form, is needed to manage this imbalance.</li>
  <li><strong>Historical Precedent:</strong> The author draws heavily on historical examples (Archidamus II of Sparta, Roman and Byzantine empires, Metternich, Bismarck) to illustrate how great powers have successfully used strategic diplomacy to:
    <ul>
      <li><strong>Buy time and prepare for conflict:</strong> Allowing a nation to strengthen its domestic resources and cultivate new alliances before engaging in war.</li>
      <li><strong>Form alliances to constrain enemies:</strong> Splintering rival coalitions and limiting their options.</li>
      <li><strong>Cultivate favourable balances of power:</strong> Projecting influence beyond immediate material capabilities.</li>
    </ul>
  </li>
  <li><strong>Avoiding a Two-Front War (Sequencing Rivals):</strong> A central tenet of the author’s argument is that the USA cannot afford to confront China and Russia simultaneously. Strategic diplomacy is crucial for “rearranging power in space and time” to avoid “tests of strength beyond their ability.” This directly informs his suggestion to seek a détente with the “weaker” rival (Russia) to focus resources and attention on the “stronger” one (China).</li>
  <li><strong>Rectifying Past “Mistakes”:</strong> The author criticises past USA foreign policy, particularly the post-Cold War approach to China, where Washington believed economic engagement would lead to liberalisation. Instead, this inadvertently strengthened China’s economic and military power, creating a profound dependence of the USA military on Chinese-made products. Strategic diplomacy aims to reverse such “unforced errors.”</li>
  <li><strong>Enhancing Leverage for Negotiations:</strong> By rebuilding domestic economic strength, recalibrating alliances for greater reciprocity, and strengthening military presence in key regions (like the Indo-Pacific), the USA can create a “position of strength” from which to negotiate more effectively with its rivals. This is distinct from “appeasement” because it’s about gaining advantages, not making concessions without benefit.</li>
  <li><strong>Rethinking Alliances for Shared Burden:</strong> The author argues that alliances should not be seen as a one-way street where the USA bears the primary burden. He advocates for a more reciprocal relationship, especially with European allies, where they assume greater responsibility for their own conventional defense, freeing up USA resources to address the primary challenge posed by China.</li>
  <li><strong>The Ultimate “Why”:</strong> Beyond mere survival, the author suggests that strategic diplomacy is the “best shot America has at shoring up its position for protracted competition.” It’s about enabling the USA to “live until you die” – to maintain its strength, influence, and way of life in a dangerous world, thereby serving its national interest. The job of diplomacy, for the author, is not to usher in a utopian world but to “succeed at geopolitics” and ensure the state’s survival amidst constant competition.</li>
</ul>

<h1 id="options-for-us">Options for US</h1>

<ul>
  <li><strong>Re-engagement with Major Adversaries (Russia, China, Iran):</strong>
    <ul>
      <li><strong>Direct talks with Russian President Vladimir Putin:</strong> To seek an end to the war in Ukraine.</li>
      <li><strong>Communication with Chinese leader Xi Jinping:</strong> Regarding holding a summit to discuss relations.</li>
      <li><strong>Letter to Iranian Supreme Leader Ali Khamenei:</strong> Aimed at resolving Iran’s nuclear program.</li>
      <li><strong>Shift from “overpowering everybody” to strategic deal making:</strong> Acknowledging that the USA no longer possesses the military or economic capability to simultaneously defeat all foes or drive another great power to ruin through sanctions.</li>
    </ul>
  </li>
  <li><strong>Recalibrating Alliances for Greater Reciprocity:</strong>
    <ul>
      <li><strong>Renegotiating the balance of benefits and burdens:</strong> Ensuring allies contribute more to their own defence and align with USA economic interests.</li>
      <li><strong>“Nixon-style arrangement” with Europe:</strong>
        <ul>
          <li>USA provides extended deterrence and certain strategic systems (e.g., nuclear protection).</li>
          <li>European allies provide the bulk of conventional frontline fighting capabilities.</li>
          <li>Demand reciprocity in market access and stipulate that allies benefit from USA innovation only if they adopt American regulatory standards.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Prioritising and Managing Rivalries (Focus on Russia to isolate China):</strong>
    <ul>
      <li><strong>Seek a détente with Russia:</strong> Leveraging Russia’s “depleted state” after the Ukraine war to disadvantage Beijing.</li>
      <li><strong>End the war in Ukraine favourably for the USA:</strong>
        <ul>
          <li>Prioritise an armistice, pushing wider political settlements into a separate, long-term process (likened to the 1950s Korea model).</li>
          <li>Insist on Ukrainian sovereignty as a precondition for talks.</li>
          <li>Use USA sanctions, military assistance, and seized Russian assets as leverage.</li>
          <li>Establish a defence relationship with Ukraine akin to Israel’s (not a formal alliance, but consistent provision of defensive needs).</li>
        </ul>
      </li>
      <li><strong>Complicate Russia’s relationship with China:</strong>
        <ul>
          <li>Exploit areas where Russian and Chinese interests diverge (e.g., Russia’s dependence on China, China’s inroads in Central Asia and Russian Far East).</li>
          <li>Resist Russian efforts for a grand bargain involving USA concessions in eastern NATO states.</li>
          <li>Seek a “compartmentalised détente” with Russia, relaxing constraints where interests align and heightening them where they conflict.</li>
          <li>Potentially lift restrictions preventing Asian allies from offering investment alternatives to China in Russia’s eastern territories in exchange for Russian concessions on Ukraine.</li>
          <li>Propose a revised arms control framework with Russia (similar to Reagan-Gorbachev in the 1980s) to force Russia to accept risk in its strategic arsenal, allowing the USA to focus nuclear attention on China.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Addressing the Iran Nuclear Threat:</strong>
    <ul>
      <li><strong>Leverage Israel’s recent military actions:</strong> Expand on the Abraham Accords template by fostering Israeli-Saudi normalisation.</li>
      <li><strong>Peel off old Iranian surrogates:</strong> Like Lebanon and Syria, potentially by promoting an internal balance of power in Syria that favours the Kurds and keeps Islamist factions at bay.</li>
      <li><strong>Work with Turkey:</strong> On shared interests like Ukraine, and encourage reconciliation between Turkey and USA allies in the region.</li>
      <li><strong>Negotiate from a position of strength:</strong> Given Iran’s currently weaker position, to derail its nuclear ambitions and limit the need for future USA military interventions.</li>
    </ul>
  </li>
  <li><strong>Strategic Engagement with China:</strong>
    <ul>
      <li><strong>Isolate China:</strong> By turning off its viable options for forming anti-American coalitions.</li>
      <li><strong>Build the biggest possible coalitions against Beijing:</strong> While simultaneously strengthening the USA domestic economy.</li>
      <li><strong>Encourage a regional balance of power in Asia:</strong> Utilising existing tensions between China and its neighbours (e.g., India, Nepal, Japan, Philippines, Vietnam).</li>
      <li><strong>Minimise rhetoric and maximise actions:</strong> To enhance USA leverage for direct diplomacy.</li>
      <li><strong>Strengthen alliances in the Indo-Pacific:</strong> Pressing for greater reciprocity in tariffs and defence burden-sharing, and strengthening USA military deterrent.</li>
      <li><strong>Prioritise India:</strong> Treat New Delhi as a key ally for technology transfers and ramp up plans for economic corridors to counter China’s Belt and Road Initiative.</li>
      <li><strong>Avoid criticising India on unrelated issues:</strong> Such as human rights or Russian arms/oil purchases, which have previously hindered cooperation.</li>
      <li><strong>Negotiate for a more favourable balance of power:</strong>
        <ul>
          <li>Insist on a reduced trade deficit.</li>
          <li>Expanded access for American financial institutions in China.</li>
          <li>Encourage Chinese investment in targeted USA industries.</li>
          <li>Potentially a currency revaluation (stronger renminbi, weaker dollar) that benefits both countries.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Domestic Reinforcement for Diplomacy:</strong>
    <ul>
      <li><strong>Increase energy production, reduce the deficit, and deregulate:</strong> To strengthen the USA economy and create a position of strength.</li>
      <li><strong>Reorient the USA Foreign Service:</strong>
        <ul>
          <li>School officers in negotiation as a core competency.</li>
          <li>Train them in military and economic matters.</li>
          <li>Align diplomatic funding and priorities with the National Security Strategy.</li>
          <li>Bar diplomats from promoting “progressive causes” that may alienate allies or embolden opponents.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="geopolitics" /><summary type="html"><![CDATA[[!info] title: The Return of Great-Power Diplomacy: How Strategic Dealmaking Can Fortify American Power author: Wess Mitchell source: Foreign Affairs published: 2025-05-01]]></summary></entry><entry><title type="html">Notes for: Outlive</title><link href="https://twydev.github.io/notes/outlive/" rel="alternate" type="text/html" title="Notes for: Outlive" /><published>2025-05-16T00:00:00+00:00</published><updated>2025-05-16T00:00:00+00:00</updated><id>https://twydev.github.io/notes/outlive</id><content type="html" xml:base="https://twydev.github.io/notes/outlive/"><![CDATA[<blockquote>
  <p>[!info]
title: Outlive: The Science &amp; Art of Longevity
author: Peter Attia
published: 2023
edition: 1
ISBN: 978-0593236604</p>
</blockquote>

<h1 id="introduction">Introduction</h1>

<ul>
  <li>fast deaths are acute injuries and infections for example</li>
  <li>slow deaths are chronic diseases</li>
  <li>Modern medicine (Medicine 2.0) focuses on treating fast deaths and reacting to slow deaths after diseases manifest
    <ul>
      <li>Medicine 2.0 follows benchmark for health indicators to prescribe treatments</li>
      <li>but individuals are different and conditions may not manifest</li>
      <li>it may also be too late to stop chronic diseases</li>
      <li>inaction may end up causing more harm</li>
    </ul>
  </li>
  <li>Medicine 3.0 (proposed by the author) emphasises proactive prevention of slow deaths</li>
  <li>The Four Horsemen of slow deaths
    <ul>
      <li>Type 2 Diabetes and metabolic dysfunction</li>
      <li>Heart disease (Atherosclerotic Cardiovascular Disease ASCVD)</li>
      <li>Cancer</li>
      <li>Neurodegenerative Disease</li>
    </ul>
  </li>
  <li>Healthspan, not just lifespan, is the focus. To live better (quality) for longer, maintaining physical and cognitive function.</li>
</ul>

<h1 id="the-four-horsemen">The Four Horsemen</h1>

<ul>
  <li>genetics definitely play a part and increase our inherent risk</li>
  <li>but a lot of risk also comes from our diet and lifestyle</li>
  <li>after learning about the four horsemen, it seems like eating too much coupled with lack of exercise is really a core problem to most diseases.</li>
</ul>

<h2 id="type-2-diabetes--metabolic-dysfunction">Type 2 Diabetes / Metabolic Dysfunction</h2>

<ul>
  <li>metabolic dysfunction is the core as it is highly correlated to most chronic diseases</li>
  <li>it boils down to eating too much glucose</li>
  <li>when there is too much glucose in your blood, it will be converted to fats</li>
  <li>the next symptom is visceral fats (fat around the organs)</li>
  <li>Nonalcoholic Fatty Liver Disease NAFLD is highly correlated with obesity and hyperlipidemia (excessive cholesterol)
    <ul>
      <li>liver turns fatty when it needs to store excess glucose in the blood as fats, and more fats enters than exits the liver</li>
      <li>the next stage of the disease is Nonalcoholic Steatohepatitis NASH, which is fatty and inflamed liver like hepatitis</li>
    </ul>
  </li>
  <li>NAFLD and NASH are reversible, as the liver is a very resilient, almost miracle organ. Most common approach is to fight obesity.</li>
  <li>a note on fructose, they are metabolised differently from glucose and will take a shortcut to be stored as fats in our liver
    <ul>
      <li>so drinking large quantity of liquid fructose (found in all our syrups and drinks) is bad</li>
    </ul>
  </li>
  <li>insulin resistance is the next symptom to surface after fatty organs
    <ul>
      <li>when too much glucose is in the blood, the pancreas need to produce more insulin</li>
      <li>insulin is a hormone to signal to cells to absorb sugar from the blood</li>
      <li>the cells build resistance to insulin, becoming a vicious cycle where more insulin is required to regulate glucose level</li>
      <li>eventually, the pancreas becomes overworked, leading to diabetes</li>
    </ul>
  </li>
</ul>

<h2 id="heart-disease">Heart Disease</h2>

<ul>
  <li>lipoproteins, are part protein and part lipid molecules. The protein is responsible for transporting the lipid part in our blood around the body, because lipid are not water-soluble.
    <ul>
      <li>Low-density lipoprotein LDL is commonly known as the “bad cholesterol”</li>
      <li>High-density lipoprotein HDL is commonly known as the “good cholesterol”</li>
      <li>but technically there is no difference between the lipid carried by both proteins.</li>
      <li>so objectively speaking lipids are not bad for your body, they are essential for our body to function</li>
      <li>LDL tends to get stuck in our blood vessels due to its molecular structure (it can also oxidise and react with other molecules to cause blockages)</li>
      <li>which is why LDL is correlated with higher risk of heart diseases</li>
      <li>the LDL protein part is called apolipoprotein B (apoB), and to be more specific, this is the protein signature that causes problem</li>
    </ul>
  </li>
  <li>there is insufficient evidence to correlate cholesterol ingested in our diet with the cholesterol circulating in our body
    <ul>
      <li>most of the cholesterol in our blood is produced by our own body</li>
    </ul>
  </li>
  <li>metabolic dysfunction, when insulin resistance has occurred, causes liver to produce more LDL
    <ul>
      <li>it also interferes with production of HDL, lowering HDL level</li>
      <li>insulin resistance also interferes with cholesterol absorption, which stimulate production of more LDL</li>
      <li>therefore fixing metabolic problems, by improving diet and lifestyle, can also lower LDL</li>
    </ul>
  </li>
</ul>

<h2 id="cancer">Cancer</h2>

<ul>
  <li>cancer is a disease of aging, and it becomes more prevalent as we age</li>
  <li>cancer is hard to detect, and it is hard to treat. every cancer is different</li>
  <li>cancer cells don’t grow faster than normal cells, their main problem is they don’t stop growing
    <ul>
      <li>a gene called PTEN mutated and no longer signals to the cell to stop growing</li>
    </ul>
  </li>
  <li>cancer is also a problem because of metastasis, which spreads the cancer to other parts of the body</li>
  <li>cancer metabolism is slightly different from normal cells
    <ul>
      <li>normal aerobic cellular respiration converts glucose to many ATP, yielding energy for the cells</li>
      <li>the Warburg effect or anaerobic glycolysis, where cancer cells consume a lot of glucose but only produce a small amount of ATP and a lot of chemical by-products</li>
      <li>almost as if producing energy is a by-product, and those chemical outputs are the main objective</li>
      <li>finding sites with high glucose concentrations indicate possible presence of tumours</li>
      <li>those chemical products are useful building blocks to create new cells, promoting cancer cells to proliferate</li>
    </ul>
  </li>
  <li>therefore, again this links to too much glucose in the blood, and obesity is also strongly correlated with cancer</li>
  <li>while we cannot prevent genetic mutations causing cancer, we can prevent too much glucose from providing a hotbed for cancer to thrive</li>
  <li>immunotherapy is a promising and emerging field for treating cancer</li>
  <li>early detection is key, therefore the author recommends getting tested once in a while even when you feel perfectly healthy</li>
</ul>

<h2 id="neurodegenerative-diseases">Neurodegenerative Diseases</h2>

<ul>
  <li>Alzheimer’s disease is caused by a handful of mutation in some specific genes that promotes accumulation of certain protein in the brain
    <ul>
      <li>although there are also reports that shows that patients without build up of those proteins (like Amyloid-beta) also exhibits symptoms of the disease</li>
      <li>detection is usually too late, and modern medicine has not been able to cure this disease</li>
      <li>the same can be said for other diseases like Parkinson’s (which affects movements)</li>
    </ul>
  </li>
  <li>one way to slow the damage
    <ul>
      <li>building cognitive reserves (brain’s capacity to perform) by doing new stimulating tasks</li>
      <li>building movement reserves (the body’s capacity to perform) by exercising</li>
    </ul>
  </li>
  <li>the brain metabolise glucose differently by absorbing glucose directly from the blood, without insulin signals
    <ul>
      <li>this ensures the brain is a top priority to function in our body</li>
      <li>insulin resistance, and prolonged period of high blood glucose level, will damage the brain (mechanistically)</li>
      <li>hippocampus has high concentration of insulin receptors, and high insulin level is affecting memory functions</li>
    </ul>
  </li>
  <li>deep sleep is like a garbage collection process
    <ul>
      <li>poor or disrupted sleep increases risk of dementia</li>
      <li>accompanied by high stress and elevated cortisol levels, it multiplies the risk</li>
      <li>hypercortisolemia (excess cortisol due to stress) inhibits release of melatonin, which makes sleep more difficult</li>
    </ul>
  </li>
  <li>some other approaches that can help
    <ul>
      <li>surprisingly, oral and gum health is related to inflammatory markers, which seems to correlate with the disease</li>
      <li>therefore, brush and floss regularly</li>
      <li>regular saunas (4 x 20min sessions, 82 degree Celsius) seems to also help reduce risk</li>
      <li>intake of vitamin B and omega-3 fatty acid also seems to help</li>
    </ul>
  </li>
  <li>strategy in summary
    <ul>
      <li>what is good for the heart is also good for the brain (vascular health)</li>
      <li>what is good for the liver is also good for the brain (metabolic health)</li>
      <li>time is key, start early</li>
      <li>most powerful prevention is exercise</li>
    </ul>
  </li>
</ul>

<h1 id="framework-and-tactics">Framework and Tactics</h1>

<h2 id="exercise">Exercise</h2>

<ul>
  <li>therefore four pillars to take note of</li>
</ul>

<h3 id="aerobic-efficiency-zone-2">Aerobic Efficiency: Zone 2</h3>

<ul>
  <li>long, steady-state cardio for mitochondrial health and fat oxidation</li>
  <li>approximately 70-85 percent of peak heart rate</li>
  <li>the talk test - the exercise is working you hard, but you are still able to talk and complete a sentence</li>
  <li>zone 2 output is highly variable and based on individual fitness</li>
  <li>about 3 hours per week (4x 45 mins sessions)</li>
  <li>measure the watts we produce per kg of our mass to track our zone 2 progression
    <ul>
      <li>2 watts/kg, for a reasonably fit person</li>
      <li>3 watts/kg, very fit</li>
      <li>4 watts/kg, professional athletes</li>
    </ul>
  </li>
</ul>

<h3 id="maximum-aerobic-output-vo2-max">Maximum Aerobic Output: VO2 Max</h3>

<ul>
  <li>Interval training for cardiovascular fitness and longevity</li>
  <li>Longer intervals than HIIT, ranging from 3-8 minutes
    <ul>
      <li>e.g. 4 mins high effort, 4 mins jogging pace, 6 reps</li>
    </ul>
  </li>
</ul>

<h3 id="strength">Strength</h3>

<ul>
  <li>building and maintaining muscle mass</li>
  <li>this is crucial to prevent muscle loss that comes from aging, and also help maintain body functions</li>
  <li>we lose muscle faster as we get older</li>
  <li>bone density also improves when we perform high load-bearing activity
    <ul>
      <li>bones respond to mechanical tension</li>
      <li>estrogen is the key hormone in mediating the mechanical signal (weight-bearing) to a chemical one, telling the body to lay more bones</li>
    </ul>
  </li>
  <li>actions to focus on, which helps with movements at older age
    <ul>
      <li>grip strength</li>
      <li>concentric (muscle shortening) and eccentric (muscle lengthening) loading for all movements.
        <ul>
          <li>this allows us to lift weights up and put them back down, steadily with control.</li>
          <li>eccentric strength in our quads is what allows us to move down hill safely without injuring our knees</li>
        </ul>
      </li>
      <li>pulling motions</li>
      <li>hip-hinging movements</li>
    </ul>
  </li>
</ul>

<h3 id="stability">Stability</h3>

<ul>
  <li>as we age, our muscle loss accelerates. if we are injured and that disrupts our training routine, it will snowball our decline</li>
  <li>therefore, not injuring ourselves is important</li>
  <li>proper breathing</li>
  <li>proper forms during training</li>
  <li>mobility trainings</li>
</ul>

<h2 id="nutrition">Nutrition</h2>

<ul>
  <li>the key questions to answer before we solve our problems are
    <ul>
      <li>are we under-nourished or over-nourished</li>
      <li>are we under-muscled or adequately muscled</li>
      <li>are we metabolically healthy or not</li>
    </ul>
  </li>
  <li>depending on our current state, some diets may or may not suit us</li>
</ul>

<h3 id="categories-of-diet">Categories of Diet</h3>

<ul>
  <li>Caloric Restriction CR
    <ul>
      <li>make sure we take in fewer calories without compromising on nutrients</li>
      <li>it is challenging as we need to measure our intakes and fight the hunger to not snack or cheat</li>
    </ul>
  </li>
  <li>Dietary Restriction DR
    <ul>
      <li>avoid intake of specific type of food</li>
      <li>this only works if it also results in CR</li>
      <li>perhaps because your main problem is consuming too much soda, and you are cutting it for example</li>
      <li>so it is specific to individual needs, and not all diet will work for everyone if it doesn’t address the specific problem</li>
    </ul>
  </li>
  <li>Time Restriction TR
    <ul>
      <li>also called intermittent fasting</li>
      <li>this cuts calories as it limits the time we eat</li>
      <li>but a common downside is we end up protein deficient</li>
      <li>it will be worst if a person ends up losing weight because of muscle loss</li>
      <li>but it may be necessary to use fasting to perform a metabolic reboot in extreme cases</li>
    </ul>
  </li>
</ul>

<h3 id="macronutrients">Macronutrients</h3>

<ul>
  <li>alcohol
    <ul>
      <li>there is no health benefit to alcohol.</li>
    </ul>
  </li>
  <li>carbohydrates
    <ul>
      <li>it is the primary source of energy</li>
      <li>but overconsumption causes increased blood glucose level, which is bad</li>
      <li>continuous glucose monitoring CGM can be a useful tool, even for healthy people, to monitor changes in glucose levels as we go about our daily lives</li>
    </ul>
  </li>
  <li>lessons from CGM
    <ul>
      <li>refined carbs causes faster and higher glucose spike.</li>
      <li>less processed carbs (contains more fibre), blunt the glucose impact. recommend 50g of fibre a day? (that’s a lot)</li>
      <li>rice and oatmeal are surprisingly glycemic (cause sharp rise in glucose level)</li>
      <li>brown rice is only slightly less glycemic than long-grain white rice</li>
      <li>fructose does not get measured by GCM</li>
      <li>timing, duration, intensity of exercise affects glucose level
        <ul>
          <li>aerobic exercise is most efficacious at removing glucose from circulation</li>
          <li>high-intensity exercise and strength training tend to increase glucose transiently</li>
        </ul>
      </li>
      <li>bad night sleep or less sleep causes a jump in peak glucose response and overall levels</li>
      <li>stress, via cortisol and other stress hormones, impact glucose level even while fasting or having CR.
        <ul>
          <li>it is difficult to quantify, but it is most obvious during sleep or periods long after meal, when the body releases more glucose into circulation</li>
        </ul>
      </li>
      <li>non-starchy vegetables (broccoli, spinach) have virtually no impact on blood sugar</li>
      <li>foods high in protein and fat (eggs, beef short ribs) have no effect on blood sugar</li>
      <li>but large quantity of lean protein (chicken breast) elevates glucose level slightly.</li>
      <li>protein shakes, especially if low in fat, have a more pronounce effect on elevating glucose level</li>
      <li>stacking all above effects in positive or negative directions, can be powerful</li>
      <li>tracking our own behaviour using CGM may create its own Hawthorne effect, which compels us to change our behaviour</li>
    </ul>
  </li>
  <li>protein
    <ul>
      <li>recommended amount 1.6g protein per kg body mass per day for maintenance</li>
      <li>for active people, can consume up to 2.2g per kg</li>
      <li>it can be a lot of protein, so recommended to split into 4 servings a day</li>
      <li>no need to worry about consuming too much</li>
      <li>the most important macronutrients</li>
    </ul>
  </li>
  <li>fats
    <ul>
      <li>it has a bad rap of leading to higher cholesterol</li>
      <li>fats are important to maintain metabolic balance and brain health</li>
      <li>eating fats makes us feel more satiated than eating carbs</li>
      <li>all food always contains all groups of fats, saturated fatty acid SFA, monounsaturated fatty acids MUFA, and polyunsaturated fatty acids PUFA.
        <ul>
          <li>PUFA can be separated into omega-6 and omega-3 variants</li>
          <li>omega-3 can be subdivided in to marine (EPA, DHA) and non-marine (ALA) sources.</li>
          <li>marine sources e.g. are salmon and seafood</li>
          <li>non-marine sources e.g. are flaxseeds and nuts</li>
        </ul>
      </li>
      <li>some food like olive oil contains more MUFA, but it still contains other fat groups</li>
      <li>recommended fats consumption
        <ul>
          <li>boost MUFA consumption to 50-55% of total fats</li>
          <li>SFA cut down to 15-20%</li>
          <li>make up the remaining with PUFA</li>
        </ul>
      </li>
      <li>in practice
        <ul>
          <li>eat more olive oil, avocado, nuts</li>
          <li>cut back on butter and lard</li>
          <li>reduce omega-6 rich oil like corn, soybean, sunflower oil</li>
          <li>increase high omega-3 marine PUFAs from seafood (almost always requires supplement for EPA and DHA)</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="best-diet">Best Diet</h3>

<ul>
  <li>it is the one you can sustain</li>
  <li>aim to lower calorie intake</li>
  <li>increase protein intake and build muscle mass</li>
  <li>control lipid levels</li>
  <li>while also reducing blood glucose level</li>
</ul>

<h2 id="sleep">Sleep</h2>

<ul>
  <li>poor sleep affects metabolic health, cognitive function, immune system, and emotional well-being</li>
</ul>

<h3 id="how-to-improve-sleep">How to improve sleep</h3>

<ul>
  <li>don’t drink alcohol</li>
  <li>don’t eat anything less than 3 hours before bedtime (ideally longer). it is ok to sleep with just a little bit of hunger</li>
  <li>abstain from stimulating electronic, beginning two hours before bed</li>
  <li>at least one hour before bed (if not, more) avoid doing anything anxiety-producing or stimulating</li>
  <li>hot shower or sauna or hot tub prior to bed. hitting a cool bed and lowering body temperature signals your brain the time to sleep</li>
  <li>room should be cool. cool mattress can help. mid-60s F (or 17-19 C)</li>
  <li>darken room completely. eye shade can help.</li>
  <li>provide ample time to prepare for bed</li>
  <li>fix wake-up time, even on weekend</li>
  <li>don’t obsess over our sleep. clock-watching makes it harder to fall asleep. anxiety over our sleep metrics also affects our sleep.</li>
</ul>

<h2 id="emotional-health">Emotional Health</h2>

<ul>
  <li>seek help from therapist</li>
  <li>we might be struggling with some trauma that we are not aware of</li>
  <li>it affects our relationships with others</li>
  <li>we can be fit and healthy mechanically, but having miserable relationships with those around us, which contradicts our focus on healthspan</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="health" /><summary type="html"><![CDATA[[!info] title: Outlive: The Science &amp; Art of Longevity author: Peter Attia published: 2023 edition: 1 ISBN: 978-0593236604]]></summary></entry><entry><title type="html">Notes for: Efficient Go</title><link href="https://twydev.github.io/notes/efficient-go/" rel="alternate" type="text/html" title="Notes for: Efficient Go" /><published>2025-04-27T00:00:00+00:00</published><updated>2025-04-27T00:00:00+00:00</updated><id>https://twydev.github.io/notes/efficient-go</id><content type="html" xml:base="https://twydev.github.io/notes/efficient-go/"><![CDATA[<blockquote>
  <p>[!info]
title: Efficient Go: Data-Driven Performance Optimization
author: Bartlomiej Plotka
published: 2022
edition: 1
ISBN: 978-1098105716</p>
</blockquote>

<h1 id="what-is-efficiency">What is Efficiency</h1>

<ul>
  <li>the word performance has a broad meaning and typically covers these elements
    <ul>
      <li>accuracy. how many errors the system produces</li>
      <li>speed. how fast the system completes the required task</li>
      <li>efficiency. how much extra resources are used (wasted) to complete the task</li>
    </ul>
  </li>
  <li>common efficiency misconceptions
    <ul>
      <li>Optimised code is not readable. this only applies to extreme scenario, low level implementation, which is not required for most use cases. typically, inefficient code are less readable.</li>
      <li>YAGNI rule should not be an excuse to skip simple and convenient efficient optimisation</li>
      <li>Hardware is getting faster. Yes, but. Bad practices that causes inefficiencies tends to fill up all available hardware resources.</li>
      <li>We can always scale horizontally to solve our problems. Yes, but horizontal scaling in distributed systems is order of magnitude more complex, more difficult, and more expensive, to get things right and to maintain, as compared to efficiency optimisation within one system.</li>
      <li>Time to market is more important. Yes, but. It is also more tragic to go to market with a non-performant system. The reputation damage may not be recoverable.</li>
    </ul>
  </li>
  <li>why should we try to be efficient?
    <ul>
      <li>it is harder to make efficient software slow</li>
      <li>optimising for speed is fragile (due to other factors like network). often, being efficient with given resources is the only thing developers can control</li>
      <li>speed is less portable (again, due to the different environments)</li>
    </ul>
  </li>
</ul>

<h2 id="efficiency-optimisation">Efficiency Optimisation</h2>

<ul>
  <li>the goal of efficiency optimisation, is to
    <ul>
      <li>modify code without changing functionality</li>
      <li>so that overall execution is more efficient</li>
      <li>or at least more efficient in categories we care about (which trades off by being worst in some other categories)</li>
    </ul>
  </li>
  <li>reasonable optimisations
    <ul>
      <li>cutting away obvious wastes in our system</li>
      <li>these could be legacies left behind by hasty implementations, or refactors, and are now obsolete but results in wasted resources</li>
      <li>these are typically easy to optimise on, and provides significant immediate benefits, without affecting functionality</li>
    </ul>
  </li>
  <li>deliberate optimisations
    <ul>
      <li>these are optimisations that are not obvious</li>
      <li>it will require us to improve on the efficiency of one category/resource, at the sacrifice of another category/resource</li>
      <li>this is a zero-sum game</li>
      <li>but we should still pursue such optimisations if it makes sense for our use cases</li>
    </ul>
  </li>
  <li>why is optimisation hard
    <ul>
      <li>we are bad at estimating which part of the system has performance problem</li>
      <li>we are bad at estimating exact resource consumption</li>
      <li>maintaining efficiency over time is hard</li>
      <li>reliable verification of current performance is difficult (environment is complex)</li>
      <li>optimising can impact other software qualities (tradeoffs)</li>
      <li>for Golang, we don’t have strict control over memory management</li>
      <li>we did not define what is efficient enough</li>
    </ul>
  </li>
</ul>

<h2 id="efficiency-requirements">Efficiency Requirements</h2>

<ul>
  <li>form a practice of stating efficiency requirements up front in a project</li>
  <li>encouraged to use Resource-Aware Efficiency Requirements (RAER)
    <ul>
      <li>state what is the operation we are interested in</li>
      <li>the scope of input (size, shape of data etc.)</li>
      <li>maximum latency of operation</li>
      <li>resource consumption budget for this operation in this scope</li>
    </ul>
  </li>
  <li>to be more pragmatic, just focus on the most critical operations, and focus on the boundaries (best and worst case scenarios)</li>
  <li>instead of absolute resource and latency measure, we can define them in relation to the input scope (e.g. use memory 2x the size of input data)</li>
  <li>we can always use a rough estimate to start, by applying napkin math calculation, and refine our requirements later</li>
  <li>tips to handling efficiency issues reported in a production environment
    <ul>
      <li>if there is already a workaround, direct users to that solution</li>
      <li>if the user is using the system outside of functional scope, kindly explain to the user</li>
      <li>if the issue reported is outside of RAER specs, kindly explain to the user</li>
      <li>if the issue reported is within RAER specs, then the team should potentially try to work on it</li>
      <li>but note, as usage and system evolves, efficiency requirements can change over time</li>
    </ul>
  </li>
</ul>

<h2 id="latencies-for-napkin-math-reference">Latencies for Napkin Math Reference</h2>

<p>Some notes</p>
<ul>
  <li>KB refers to kilobyte, which is 1000 bytes per KB</li>
  <li>KiB refers to kibibyte, which is 1024 bytes per KiB, which is more precise and uses binary system</li>
  <li>numbers below are estimated based on year 2021, using x86 CPU from the Xeon family</li>
  <li>but most CPU independent numbers have been stable since 2005</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>Latency</th>
      <th>Throughput</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>3 Ghz CPU clock cycle</td>
      <td>0.3 ns</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>CPU register access</td>
      <td>0.3 ns (1 CPU cycle)</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>CPU L1 cache access</td>
      <td>0.9 ns (3 CPU cycles)</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>CPU L2 cache access</td>
      <td>3 ns</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>Sequential memory R/W (64 Bytes)</td>
      <td>5 ns</td>
      <td>10 GBps</td>
    </tr>
    <tr>
      <td>CPU L3 cache access</td>
      <td>20 ns</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>Hashing, not crypto-safe (64 Bytes)</td>
      <td>25 ns</td>
      <td>2 GBps</td>
    </tr>
    <tr>
      <td>RAM R/W (64 Bytes)</td>
      <td>50 ns</td>
      <td>1 GBps</td>
    </tr>
    <tr>
      <td>Mutex lock/unlock</td>
      <td>17 ns</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>System call</td>
      <td>500 ns</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>Hashing, crypto-safe (64 Bytes)</td>
      <td>500 ns</td>
      <td>200 MBps</td>
    </tr>
    <tr>
      <td>Sequential SSD read (8 KB)</td>
      <td>1 us</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>Context switch</td>
      <td>10 us</td>
      <td>NA</td>
    </tr>
    <tr>
      <td>Sequential SSD write, -fsync (8 KB)</td>
      <td>10 us</td>
      <td>1 GBps</td>
    </tr>
    <tr>
      <td>TCP echo server (32 KiB)</td>
      <td>10 us</td>
      <td>4 GBps</td>
    </tr>
    <tr>
      <td>Sequential SSD write, +fsync (8 KB)</td>
      <td>1 ms</td>
      <td>10 MBps</td>
    </tr>
    <tr>
      <td>Sorting (64-bit integers)</td>
      <td>NA</td>
      <td>200 MBps</td>
    </tr>
    <tr>
      <td>Random SSD seek (8 KiB)</td>
      <td>100 us</td>
      <td>70 MBps</td>
    </tr>
    <tr>
      <td>Compression</td>
      <td>NA</td>
      <td>100 MBps</td>
    </tr>
    <tr>
      <td>Decompression</td>
      <td>NA</td>
      <td>200 MBps</td>
    </tr>
    <tr>
      <td>Proxy: Envoy/ProxySQL/NGINX/HAProxy</td>
      <td>50 us</td>
      <td>?</td>
    </tr>
    <tr>
      <td>Network within same region</td>
      <td>250 us</td>
      <td>100 MBps</td>
    </tr>
    <tr>
      <td>MySQL, memcached, Redis query</td>
      <td>500 us</td>
      <td>?</td>
    </tr>
    <tr>
      <td>Random HDD Seek (8 KB)</td>
      <td>10 ms</td>
      <td>0.7 MBps</td>
    </tr>
    <tr>
      <td>Network NA East &lt;-&gt; West</td>
      <td>60 ms</td>
      <td>25 MBps</td>
    </tr>
    <tr>
      <td>Network EU West &lt;-&gt; NA East</td>
      <td>80 ms</td>
      <td>25 MBps</td>
    </tr>
    <tr>
      <td>Network NA West &lt;-&gt; SG</td>
      <td>180 ms</td>
      <td>25 MBps</td>
    </tr>
    <tr>
      <td>Network EU West &lt;-&gt; SG</td>
      <td>160 ms</td>
      <td>25 MBps</td>
    </tr>
  </tbody>
</table>

<h2 id="optimisation-technology-levels">Optimisation Technology Levels</h2>

<p>The different parts that forms software execution:</p>
<ol>
  <li>system - The highest level of abstraction. It is made up of multiple processes, which can be distributed or not. Each process will be using modules</li>
  <li>module - Encapsulates certain functionality behind an API. Modules are implemented using data structures and algorithms</li>
  <li>code implementation - The same data structure and algorithm can be implemented differently at the code level. Also, depending on the language, we also depend on the compiler and runtime.</li>
  <li>operating system - the interface that runs our software, and communicate with underlying machine</li>
  <li>hardware - the actual machine and various components like CPU, memory, hard disk, network</li>
</ol>

<p>recognising the levels matters</p>
<ul>
  <li>optimisation within a level can achieve speedups with factors of 10 to 20</li>
  <li>to achieve better results beyond that, you will need to work on other levels</li>
  <li>our teams may be biased to only optimise within the level we are comfortable with, which may not be the easiest solution</li>
</ul>

<h2 id="efficiency-aware-development-flow">Efficiency-Aware Development Flow</h2>

<p>Here is a recommended Test-Fix-Benchmark-Optimise workflow (TFBO):</p>
<ol>
  <li>at the start of the project, set functional and efficiency requirements</li>
  <li>after development, test and assess that all functional requirements are met (top priority)</li>
  <li>if not, we must fix functional issues first, then go back to step 2.</li>
  <li>once functional test passed, benchmark and assess that efficiency requirements are also passing</li>
  <li>if not, analyse and identify bottlenecks</li>
  <li>choose a technology level to optimise, and implement reasonable/deliberate optimisation</li>
  <li>go back to step 2.</li>
  <li>only after both functional and efficiency tests have passed, then we release the feature.</li>
</ol>

<h1 id="cpu-usage">CPU Usage</h1>

<ul>
  <li>assembly instructions
    <ul>
      <li>we can use Go tooling to disassemble our compiled code into assembly dialects (depends on our target architecture)</li>
      <li>assembly instructions are sequential</li>
      <li>typically moving data in/out of registers</li>
      <li>computing data from input registers and sending to output registers</li>
    </ul>
  </li>
  <li>Go compiler
    <ul>
      <li>tokenises source code, builds Abstract Syntax Tree (AST)</li>
      <li>optimises by removing dead code</li>
      <li>performs escape analysis to determine what variables can allocate on the stack/heap</li>
      <li>performs function inlining (which avoids heap memory usage)</li>
      <li>AST is converted to Static Single Assignment form (SSA) for further machine-independent optimisation</li>
      <li>SSA is then converted to target machine code for specific Instruction Set Architecture (ISA) and OS</li>
      <li>apply further ISA and OS specific optimisation</li>
      <li>finally package all the machine code and debug info into a single object file</li>
      <li>debug info can be omitted from build, to shrink the size of output if necessary</li>
      <li>we can also choose to build with compiler options that shows the optimisations made by the compiler</li>
    </ul>
  </li>
</ul>

<h2 id="cpu-internals">CPU internals</h2>

<ul>
  <li>How CPU works
    <ul>
      <li>a CPU interacts with the main memory RAM and other I/O devices</li>
      <li>a CPU consists of multiple physical core</li>
      <li>each core contains an Arithmetic Logic Unit (ALU), registers, and a hierarchy of caches (L1, L2, L3)</li>
      <li>registers are the fastest but smallest local storage and are only used for short-term variables or for internal working of the CPU</li>
      <li>the caches are on-chip SRAM, which are closer and faster for ALU to access than the main memory RAM</li>
      <li>the core runs in cycles, it can only perform one instruction per cycle (Single Instruction Single Data SISD)
        <ul>
          <li>some CPU can perform Single Instruction Multiple Data (SIMD)</li>
          <li>parallel processing of the same instruction but on different data</li>
        </ul>
      </li>
      <li>but the bottleneck in modern computing is memory, not CPU cycles</li>
    </ul>
  </li>
  <li>The “memory wall” problem
    <ul>
      <li>reading data from main memory to the CPU is much slower compared to the number of instructions modern CPU can process in the same period of time</li>
      <li>therefore, the CPU is often waiting for data to arrive, before it can process</li>
    </ul>
  </li>
  <li>Hierarchical Cache System
    <ul>
      <li>data is read from main memory in contiguous blocks, beyond what is requested</li>
      <li>these data are stored in the CPU L-caches</li>
      <li>CPU will attempt to read the required data for the next cycle from the L-cache first</li>
      <li>therefore, if data structure used in our program are stored sequentially in memory, the CPU will have a higher chance of a cache hit</li>
    </ul>
  </li>
  <li>CPU Pipelining
    <ul>
      <li>executing one assembly instruction may actually take more than one cycle</li>
      <li>there are a couple of stages to the instruction, e.g. fetching instruction, decoding instruction, executing, and then storing results, which takes 4 cycles</li>
      <li>but each stage is physically handled by a different physical component of the CPU, and all physical components can be running within the same cycle</li>
      <li>with pipelining, the CPU is smart enough to overlap the instructions, and process a different stage for a different instruction within one cycle</li>
      <li>e.g. instruction one is currently in decoding stage, so the CPU can also perform the fetch stage for the second instruction</li>
      <li>this improves throughput, which is why we said modern CPUs can process “one instruction per cycle”</li>
    </ul>
  </li>
  <li>Out-of-Order execution
    <ul>
      <li>the CPU is also smart enough to use availability of input data to schedule some instructions before others when possible, with the help of an internal queue</li>
      <li>the objective is to make the CPU doing as much work as possible with least amount of delay waiting for data</li>
    </ul>
  </li>
  <li>Speculative Execution
    <ul>
      <li>some CPU also has the feature to guess the most likely branch of execution and perform that ahead of time</li>
    </ul>
  </li>
  <li>Hyper-Threading
    <ul>
      <li>is Intel’s proprietary name for Simultaneous Multithreading which other CPU makers also implements</li>
      <li>allowing one physical CPU core to operate in a mode visible to the OS as multiple separate logical cores</li>
      <li>the CPU core internally will help schedule instructions headed for the multiple logical cores</li>
      <li>objective is to keep the core as busy as possible, as we know the main bottleneck is waiting for data</li>
      <li>the CPU core will handle context switching internally</li>
    </ul>
  </li>
</ul>

<h2 id="thread-scheduling">Thread Scheduling</h2>

<ul>
  <li>Linux OS scheduler (Completely Fair Scheduler CFS)
    <ul>
      <li>each process has a dedicated memory space and unique pid</li>
      <li>a process can have multiple threads, which are the smallest scheduling unit. threads do not share machine code sequences, but they share context with the parent process.</li>
      <li>the Linux scheduler performs preemptive thread scheduling (freeze thread execution at any time)</li>
      <li>context needs to be switched when running a different process on the CPU</li>
      <li>CPU time is cut into slices, and allocated to each thread fairly</li>
      <li>if a thread is waiting for external events which may be slow (I/O reads) it may voluntarily yield its CPU time</li>
      <li>the more threads there are, the less time each thread will be allocated</li>
      <li>for strict realtime requirements (threads that never wants to be preempted), your system might need a different OS with realtime scheduler</li>
    </ul>
  </li>
  <li>Go Runtime Scheduler
    <ul>
      <li>Go runtime will make full use of allocated CPU time but switching between goroutines</li>
      <li>from the perspective of OS scheduler, the Go program is a single process</li>
      <li>this avoids expensive context switching by the actual OS scheduler</li>
      <li>all goroutine scheduling happens at the Go runtime application level</li>
      <li>goroutine have a flat hierarchy therefore all of them share the same memory context</li>
      <li>Go runtime will multiplex goroutines onto any allocated OS threads</li>
      <li>when an OS thread is preempted, Go runtime saves the goroutine’s execution state, and is able to schedule that same goroutine to run on a potentially different OS thread in a multi-threaded OS environment</li>
    </ul>
  </li>
</ul>

<h2 id="impact-on-our-code">Impact on our code</h2>

<ul>
  <li>if our code is too dynamic and does not allow the compiler to check some memory allocation statically, then the program will depend on runtime checks, which will have performance impact</li>
  <li>contiguous memory data structures are preferred as it empowers existing CPU cache hits and optimisations</li>
  <li>if our code has less conditional branching logic, the CPU is also better able to pipeline and optimise the program execution</li>
  <li>goroutines and concurrency makes our program more complex to maintain and debug and measure performance benchmark. it should be considered as a last deliberate optimisation option. most optimisation problems can be resolved without using concurrency.</li>
</ul>

<h1 id="memory-usage">Memory Usage</h1>

<ul>
  <li>symptoms of memory issues
    <ul>
      <li>process crashes due to OOM errors</li>
      <li>program running slower than usual while memory usage is higher than average (could be due to memory pressure from trashing or swapping)</li>
      <li>program is running slower than usual, with high spikes in CPU utilisation (could be caused by excessive memory allocation and release)</li>
    </ul>
  </li>
  <li>physical memory
    <ul>
      <li>usually DRAM chips, powered continuously, using memory cells (transistors) to store 1 bit</li>
      <li>DRAM is cheaper and easier to produce than SRAM, but is slower</li>
      <li>the most popular DRAM is from the SDRAM family, 5th gen called DDR4</li>
      <li>the memory is byte addressable, which means each byte has an address represented by an integer (therefore on a 32-bit system, largest integer is 2^32, and these systems typically cannot handle RAM with more capacity than 4 GB)</li>
      <li>since each byte is addressable, the memory supports random access (hence called RAM)</li>
      <li>but the industry has been focusing on other improvements, so random access remains a slow operation
        <ul>
          <li>focuses on higher capacity to store more data</li>
          <li>more bandwidth and lower latency for reading and writing large chunks of contiguous memory</li>
          <li>lower voltage since all components in a machine are competing for power. lower power also leads to lower heat and better battery life for devices</li>
          <li>production cost of RAM must be kept low since they are produced in large quantities and used in all systems</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="os-memory-management">OS memory management</h2>

<ul>
  <li>challenges for memory management from OS perspective
    <ul>
      <li>each process needs their dedicated memory space</li>
      <li>external fragmentation of memory can occur, since many processes are dynamically reserving memory, which can leads to pockets of empty and unusable space over time</li>
      <li>memory isolation and memory safety, to prevent processes from accessing other spaces or unauthorised spaces</li>
      <li>memory usage needs to be efficient as processes do not use all the memory they request for, so the memory needs to be dynamically managed</li>
    </ul>
  </li>
  <li>virtual memory
    <ul>
      <li>each process is given a simplified view of the RAM</li>
      <li>the kernel will take care of actual physical memory operations, like coordinating with other processes for space, bin packing, defragmentation, security, limits and swaps etc.</li>
      <li>memory is represented as fixed-size chunks called pages (for virtual memory) and frames (for physical memory)</li>
      <li>pages are typically 4 KB, but can be changed to larger sizes to match specific CPU operations</li>
      <li>the kernel maps pages to frames</li>
      <li>details about a page (mapping, state, permissions, metadata) are stored as an entry in many hierarchical page tables</li>
      <li>Memory Management Unit (MMU) uses a Translation Lookaside Buffer (TLB) which caches page tables. MMU sits inside the CPU, and it helps to translate CPU memory address (virtual) to physical memory address, to avoid expensive look up for the page tables in the RAM itself.</li>
      <li>more virtual memory can be allocated to processes, than what is available in physical memory (memory overcommitment)</li>
      <li>physical memory is only allocated when the process tries to access those virtual memory spaces (on-demand paging)</li>
      <li>we can inspect the memory usage using <code class="language-plaintext highlighter-rouge">proc</code>, to look at <code class="language-plaintext highlighter-rouge">VSS</code> stats (virtual) and <code class="language-plaintext highlighter-rouge">RSS</code> stats (physical)</li>
      <li>when the process first tries to access some of those unallocated physical memories, the MMU will trigger a hardware interrupt, and a couple of operations may happen
        <ul>
          <li>OS may allocate more RAM frames, if they are free and available</li>
          <li>OS may de-allocate unused RAM frames (trashing)</li>
          <li>if the OS is still OOM, it can use a swap disk partition to back up virtual memory pages (swapping)</li>
          <li>the OS can also crash and reboot</li>
          <li>it can also terminate lower priority processes</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="go-memory-management">Go memory management</h2>

<ul>
  <li>in the allocated memory space, it holds the following regions
    <ul>
      <li>program code (.text)</li>
      <li>initialised data (.data)</li>
      <li>space for uninitialised data (.bss)</li>
      <li>heap (which can grow dynamically)</li>
      <li>shared libraries</li>
      <li>other explicit memory mappings (depending on whether we made syscall like <code class="language-plaintext highlighter-rouge">mmap</code> in our code)</li>
      <li>stack (LIFO structure)</li>
    </ul>
  </li>
  <li>goroutines will each have their own stack frames, but they all sit inside the stack region of the parent process</li>
  <li>Go is value oriented, which means each variable is allocated at a memory address in the stack frame, and at that memory location, it directly stores the value of that variable.
    <ul>
      <li>therefore simple assignment like <code class="language-plaintext highlighter-rouge">var1 = var2</code> will copy the value at memory address of <code class="language-plaintext highlighter-rouge">var2</code> to the location of <code class="language-plaintext highlighter-rouge">var1</code></li>
    </ul>
  </li>
  <li>during compilation, escape analysis will be run to determine if a variable can be allocated on the stack or on the heap</li>
  <li>during runtime, Go allocator will be invoked to allocate space on the heap, and dynamically manage the heap (bin packing, request for more pages from OS, avoid locking and prevents fragmentation)</li>
  <li>the Go garbage collector offers two configurable options
    <ul>
      <li><code class="language-plaintext highlighter-rouge">GOGC</code> represents GC percentage, by default is 100</li>
      <li>GC will be run when heap size expands to 100% of the size it gas at the end of the last GC cycle (GC will estimate the time to run based on heap growth rate)</li>
      <li>the <code class="language-plaintext highlighter-rouge">GOMEMLIMIT</code> option, disabled by default, will run the GC when heap memory usage reaches the limit.</li>
      <li>This may lead to disruptive frequent triggers of GC if your program uses memory above the limit.</li>
      <li>you can also manually trigger GC in the code. This is usually used for testing/benchmarking.</li>
      <li>GC too often will be too disruptive to the program</li>
      <li>GC not frequent enough will cause more memory to be used as old memory are not reclaimed</li>
    </ul>
  </li>
  <li>the current Go GC implementation is a concurrent, non-generational, tricolour mark and sweep collector
    <ul>
      <li>first a stop the world event (STW) is triggered to inject a write barrier to all goroutines</li>
      <li>then use 25% of CPU capacity given to the process to concurrently mark all objects in the heap that are still in use</li>
      <li>finally remove write barriers on goroutines using another STW event</li>
    </ul>
  </li>
  <li>when Go allocator wants to allocate new heap memory to a goroutine
    <ul>
      <li>it will sweep the heap to find unmarked regions that can be used</li>
      <li>this sweeping task contributes to memory allocation latency, even though it is a functionality of garbage collection</li>
    </ul>
  </li>
  <li>heap memory is managed using bucketed object pooling
    <ul>
      <li>maintaining buckets of memory of different sizes</li>
      <li>buckets from a resource pool, that can be used by different goroutines</li>
    </ul>
  </li>
</ul>

<h2 id="impact-on-our-code-1">Impact on our code</h2>

<ul>
  <li>observing virtual memory size is not useful, due to on-demand paging by the OS</li>
  <li>it is impossible to tell how much memory a process has used precisely in a given time</li>
  <li>OS memory usage expands to use all available RAM, due to the “lazy” approach used by memory management, even if your program did not use so much memory</li>
  <li>tail latency of our program memory access can be much slower than DRAM latency, if we are hit with unfortunate cache misses, MMU misses, and page swaps/trashing</li>
  <li>slow program could be simply caused by high RAM usage, and CPU is mostly under-utilised</li>
  <li>for Go programs, looking at our heap size is usually a good start to optimise memory</li>
  <li>usually there is no need to explicitly assign <code class="language-plaintext highlighter-rouge">nil</code> to a variable to free memory, but if your goroutine is long running and the variable remains unused for a long time before next assignment, then early <code class="language-plaintext highlighter-rouge">nil</code> assignment can indicate to GC to reclaim the memory</li>
  <li>having more objects on heap to GC, means that CPU load will be higher during GC and the collection will be slower</li>
  <li>GC is also destructive to the hierarchical cache system, causing more cache misses that slows down the program overall</li>
  <li>if GC is not fast enough to catch up to the allocation, then it can result in “memory leaks” (memory usage grows)</li>
  <li>the best practice is simply to allocate less memory</li>
</ul>

<h1 id="observability">Observability</h1>

<ul>
  <li>auto or manual instrumentation
    <ul>
      <li>tools automatically generate metrics outside of your process</li>
      <li>or you need to manually add calls to the tools within your process</li>
    </ul>
  </li>
  <li>raw events or aggregated data
    <ul>
      <li>e.g. full HTTP requests responses</li>
      <li>or count of success/failure HTTP requests</li>
    </ul>
  </li>
  <li>pulled or pushed from process
    <ul>
      <li>a centralised remote process periodically collects data from your application process</li>
      <li>or your process must periodically sends data to a centralised tool</li>
    </ul>
  </li>
</ul>

<h2 id="common-tools">Common Tools</h2>

<ul>
  <li>Logging
    <ul>
      <li>simplest tool we can use to get primitive latency of a operation, by measuring duration between start and end within our own process and logging it out</li>
      <li>sometimes it is more valuable to aggregate the total latency and total number of operations to find the average latency</li>
      <li>Go benchmark tool from standard library helps to measure that average latency (helpful for very fast operation)</li>
      <li>at a micro-benchmark level (focusing on our process), logs can provide aggregated stats</li>
      <li>at a macro-benchmark level (distributed system), logs providing raw events are better for further analysis across the system</li>
      <li>can consider using third party API or <code class="language-plaintext highlighter-rouge">log/slog</code> for structured logging</li>
    </ul>
  </li>
  <li>Tracing
    <ul>
      <li>will need to depend on third party libraries, like OpenTelemetry</li>
      <li>uses a root span to trace an entire transaction, and children spans for different parts of the transaction</li>
      <li>the value comes from context propagation, even across different processes</li>
      <li>but it is harder to maintain, has risk of vendor lock-in, may be expensive, and it is still challenging to observe very fast/short duration transactions within a process</li>
    </ul>
  </li>
  <li>Metrics
    <ul>
      <li>will need to depend on third party libraries</li>
      <li>aggregated numerical values for some stats of our system</li>
      <li>this signal is useful for most efficiency analysis, as we can compare numbers before and after optimisations</li>
      <li>aim to have low metric cardinality to keep the overall instrumentation maintainable</li>
    </ul>
  </li>
</ul>

<h2 id="metrics-semantics">Metrics Semantics</h2>

<ul>
  <li>metric can be defined by 2 things
    <ul>
      <li>semantics. what does the number mean, what do we measure, what unit, how do we call it</li>
      <li>granularity. how details is the information, per operation or per goroutine, or over a time period</li>
    </ul>
  </li>
  <li>latency measures
    <ul>
      <li>Go <code class="language-plaintext highlighter-rouge">time</code> uses nanoseconds, but it may make sense to standardise your metrics to seconds to compare across different tools and processes</li>
      <li>short and very fast latencies should be measured using average latency (like using Go benchmark)</li>
      <li>take note, Go runtime is prone to leap second problem (adding extra second to be in sync with the Earth’s rotation) and virtual machine suspension (process goes to sleep) which leads to wrong measurement</li>
      <li>we can measure latency at different levels of granularity
        <ul>
          <li>end-to-end from client side</li>
          <li>HTTP server end-to-end</li>
          <li>only application logic</li>
          <li>only specific function in the process</li>
        </ul>
      </li>
      <li>each level of granularity has its value and helps identify different bottlenecks, so it may be helpful to measure a couple of them</li>
      <li>using percentiles are helpful, as averages may be misleading</li>
    </ul>
  </li>
  <li>CPU usage
    <ul>
      <li>we can use <code class="language-plaintext highlighter-rouge">proc</code> on linux to view the stats</li>
      <li>CPU cycles. total clock cycles used to execute our program</li>
      <li>CPU instructions. total instructions executed for our program.</li>
      <li>these measures are good, as they are independent of thread scheduling, and latency of memory fetches</li>
      <li>CPU time divided by CPU capacity = CPU usage.
        <ul>
          <li>CPU time is split into user time and kernel time</li>
        </ul>
      </li>
      <li>low CPU time could mean a lot of waiting for other slower events like I/O</li>
      <li>CPU usage can tell you if the program is reaching CPU saturation, which may be an issue if the program cannot handle additional spike in usage depending on your use case</li>
    </ul>
  </li>
  <li>memory usage
    <ul>
      <li>use Go <code class="language-plaintext highlighter-rouge">runtime/metrics</code> to collect insights about GC, memory allocation, heap etc.</li>
      <li>use <code class="language-plaintext highlighter-rouge">proc</code> to look at OS thread memory (but be skeptical due to the complexity and laziness of OS memory management)
        <ul>
          <li>VSS stands for virtual set size, number of pages allocated to process</li>
          <li>RSS is residential set size, is number of pages resident in RAM</li>
          <li>PSS is proportional set size, is number of shared memory pages divided equally among all users</li>
          <li>WSS is working set size, estimating number of pages currently used to perform work by our process</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="efficiency-assessment">Efficiency Assessment</h1>

<ul>
  <li>complexity analysis can be a preliminary estimate of our function’s behaviour
    <ul>
      <li>time and space asymptotic complexity as a function of some N input</li>
      <li>upper bounds, average, and lower bounds are useful to help us estimate the limits of our function</li>
      <li>we can easily have a quick estimate of how much resources is needed for any given input size N</li>
      <li>we can also easily figure out which is the critical bottleneck of our algorithm</li>
      <li>if requirements are simple but complexity is high, it is an indication that our algorithm might be bad</li>
      <li>if complexity scales faster than inputs, then we might have scaling problems in the future</li>
      <li>if measured and observed resource usage deviates significantly from complexity analysis, then we might be having efficiency issues (memory leaks, other poor implementations)</li>
    </ul>
  </li>
  <li>benchmarking is made up of 4 components (also called stress or load testing)
    <ul>
      <li>number of iterations</li>
      <li>experiment on the system for each iteration</li>
      <li>measure and observe resource stats for each iteration</li>
      <li>finally compare results from all iterations</li>
    </ul>
  </li>
  <li>benchmarking is harder than functional testing
    <ul>
      <li>we need to have many different test cases and test data</li>
      <li>the performance is often nondeterministic (due to the complex environment)</li>
      <li>it is expensive to maintain the tests</li>
      <li>it is difficult to assert what is correct or good enough, and requirements evolve over time</li>
    </ul>
  </li>
  <li>common challenges to reliable benchmark tests
    <ul>
      <li>human errors
        <ul>
          <li>keep our SOP simple</li>
          <li>track what version of system and benchmarking tools we are running.</li>
          <li>keep our work well documented and organised</li>
          <li>be skeptical about results that appears too good to be true</li>
        </ul>
      </li>
      <li>reproducing production
        <ul>
          <li>it is difficult to reproduce production conditions and workload, due to cost concerns and feasibility</li>
          <li>we can aim to only simulate key characteristics from production in our benchmark tests</li>
          <li>we can also focus only on the use cases that our users truly care about</li>
        </ul>
      </li>
      <li>performance nondeterminism
        <ul>
          <li>the complex environment we are operating in can often affect our measures</li>
          <li>if variance of our results is low, perhaps this is not such a big issue</li>
          <li>ensure stable state of machine we are benchmarking on (no background threads, thermal scaling, power management)</li>
          <li>if running on share infra, be skeptical of noisy neighbours</li>
          <li>cheap CI cloud platform runners are usually intended to run functional tests, not to produce reliable benchmarks</li>
          <li>benchmark machines may be having different limits from production</li>
          <li>run experiments longer to amortise the impact of overheads and noise</li>
          <li>expire older benchmark results, as system conditions is evolving over time</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>benchmarking levels
    <ul>
      <li>benchmarking in production
        <ul>
          <li>the most accurate way but it is challenging</li>
          <li>we must not impact real users</li>
          <li>feedback loop is long (fully deploy the system before we can test it, and then make changes to deploy again)</li>
        </ul>
      </li>
      <li>macro-benchmark (or testing in a sandbox/staging/UAT environment that is quite similar to production)
        <ul>
          <li>can be reliable and effective, and doesn’t impact production</li>
          <li>this may be expensive and difficult to maintain to keep in sync with production</li>
          <li>feedback loop is also long</li>
        </ul>
      </li>
      <li>micro-benchmark (testing an isolated part of our process)
        <ul>
          <li>fastest feedback loop</li>
          <li>easy and cheap to implement, like a unit test</li>
          <li>may not be able to identify all bottlenecks</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>it is recommended to start small with micro-benchmark, and move to higher level tests if necessary for your requirements</li>
</ul>

<h1 id="benchmarking">Benchmarking</h1>

<h2 id="micro-benchmark">Micro-benchmark</h2>

<ul>
  <li>should be kept micro. consider other levels if you violates some of the assumptions
    <ul>
      <li>testing single functionality</li>
      <li>short and fast</li>
      <li>not too many goroutines are created</li>
      <li>uses resources that are within the limits of development machine</li>
    </ul>
  </li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// filename must end with _test.go suffix</span>

<span class="c">// test function must start with Benchmark prefix</span>
<span class="c">// test function must accept (b *testing.B) as the only parameter</span>
<span class="k">func</span> <span class="n">BenchmarkOurFn</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">b</span><span class="o">.</span><span class="n">ReportAllocs</span><span class="p">()</span> <span class="c">// trace memory allocations, equivalent to -benchmem</span>
	
	<span class="n">InitOurFn</span><span class="p">()</span>
	
	<span class="n">b</span><span class="o">.</span><span class="n">ResetTimer</span><span class="p">()</span> <span class="c">// reset timer to ignore above overhead init</span>
	
	<span class="c">// b.N is an optimal number that benchmark tool will decide</span>
	<span class="c">// therefore, we should avoid using variable i within our loop</span>
	<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span> 
		<span class="n">OurFn</span><span class="p">()</span>
	<span class="p">}</span>
	
	<span class="c">// can use b.ReportMetric to emit custom metrics</span>
<span class="p">}</span>

<span class="c">// results from test table can also be analysed by benchstat</span>
<span class="k">func</span> <span class="n">BenchmarkOurFnTestTable</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">tcase</span> <span class="o">:=</span> <span class="k">range</span> <span class="p">[]</span><span class="k">struct</span> <span class="p">{</span> <span class="n">input</span> <span class="kt">int</span> <span class="p">}</span> <span class="p">{</span>
		<span class="p">{</span> <span class="n">input</span><span class="o">:</span> <span class="m">1</span> <span class="p">},</span>
		<span class="p">{</span> <span class="n">input</span><span class="o">:</span> <span class="m">99</span> <span class="p">},</span>
		<span class="p">{</span> <span class="n">input</span><span class="o">:</span> <span class="m">2048</span> <span class="p">}</span>
	<span class="p">}</span> <span class="p">{</span>
		<span class="n">b</span><span class="o">.</span><span class="n">Run</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"input-%d"</span><span class="p">,</span> <span class="n">tcase</span><span class="o">.</span><span class="n">input</span><span class="p">),</span> <span class="k">func</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">))</span> <span class="p">{</span>
			<span class="n">InitOurFn</span><span class="p">(</span><span class="n">tcase</span><span class="o">.</span><span class="n">input</span><span class="p">)</span>
			<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span><span class="o">&lt;</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span> 
				<span class="n">OurFn</span><span class="p">()</span>
			<span class="p">}</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">ver</span><span class="o">=</span>v1
go <span class="nb">test</span> <span class="nt">-run</span> <span class="s1">'^$'</span> <span class="se">\ </span><span class="c"># regex matches nothing</span>
	<span class="nt">-bench</span> <span class="s1">'^BenchmarkOurFn$'</span> <span class="se">\ </span><span class="c"># targets my particular benchmark</span>
	<span class="nt">-benchtime</span> 10s <span class="se">\ </span><span class="c"># run the benchmark test for 10s</span>
	<span class="nt">-count</span> 5 <span class="se">\ </span><span class="c"># repeats the benchmark test 5 times</span>
	<span class="nt">-cpu</span> 4 <span class="se">\ </span><span class="c"># sets the GOMAXPROCS</span>
	<span class="nt">-benchmem</span> <span class="se">\ </span><span class="c"># trace memory allocations</span>
	<span class="nt">-memprofile</span><span class="o">=</span><span class="k">${</span><span class="nv">ver</span><span class="k">}</span>.mem.pprof <span class="se">\ </span><span class="c"># output mem profile</span>
	<span class="nt">-cpuprofile</span><span class="o">=</span><span class="k">${</span><span class="nv">ver</span><span class="k">}</span>.cpu.pprof <span class="se">\ </span><span class="c"># output CPU profile (may have impact on ultra fast tests)</span>
	| <span class="nb">tee</span> <span class="k">${</span><span class="nv">ver</span><span class="k">}</span>.txt <span class="c"># save to temporary file</span>

<span class="c"># recommended to use community library `benchstat` to further analyse the results</span>
benchstat v1.txt
benchstat v1.txt v2.txt <span class="c"># compares results</span>
</code></pre></div></div>

<ul>
  <li>relating back to TBFO workflow
    <ul>
      <li>execute micro-benchmark and save results for current performance</li>
      <li>analyse and figure out bottlenecks, implement improvements in a new branch</li>
      <li>make sure new branch passes functional tests</li>
      <li>re-run benchmark and compare results</li>
      <li>compile all notes and analysis in pull request (but discard raw results, as results should expire!)</li>
      <li>benchmark code can be committed</li>
      <li>provide code comment or document in the PR description, how to replicate the same conditions used to run the benchmark (so that someone else may repeat the experiment in the future)</li>
    </ul>
  </li>
  <li>micro-benchmark is not able to reveal insights about memory efficiency, particularly:
    <ul>
      <li>GC latency</li>
      <li>maximum memory usage</li>
    </ul>
  </li>
  <li>but it is good enough to let us know the number of memory allocations and we can already start improving on that</li>
  <li>compiler optimisation may lead to unexpected results for our benchmark
    <ul>
      <li>the same function is run in a loop, compiler may inline the function</li>
      <li>some inputs are constant and doesn’t change, the compiler may cache the results instead of performing the work at runtime</li>
      <li>the outputs from our function are often discarded in the benchmark, the compiler may deem this as unused and not even run the function at all</li>
    </ul>
  </li>
  <li>there are some tricks to trick the compiler to not be optimal or lazy
    <ul>
      <li>using a global exported variable to assign a constant value. the compiler must allocate memory, since the variable may change value (by a goroutine for example) and the compiler cannot predict that runtime dynamics</li>
      <li>consuming the global variable in our function as input parameter</li>
      <li>assigning output of our function to another global exported variable</li>
    </ul>
  </li>
  <li>but these tricks conflict with benchmark principle: to be as close to production as possible. it is a delicate balance.</li>
</ul>

<h2 id="macro-benchmark">Macro-benchmark</h2>

<ul>
  <li>testing our product in a deployed environment similar to production</li>
  <li>ways to handle dependencies (e.g. database)
    <ul>
      <li>use a realistic dependency, this is the best approach</li>
      <li>use a fake, this is hard to simulate and maintain</li>
      <li>use a substitute, like local storage. it is not as accurate but may be sufficient depending on your requirements</li>
    </ul>
  </li>
  <li>an observability tool, third party platform, will need to be used (no Go built-in tool for this scale)
    <ul>
      <li>collect metrics from load tester</li>
      <li>collect metrics from our product</li>
    </ul>
  </li>
  <li>we also need a load tester to send requests to our product
    <ul>
      <li>consider the k6 open source project</li>
    </ul>
  </li>
  <li>we also need a framework to help us orchestrate the test
    <ul>
      <li>the book recommends its own package https://github.com/efficientgo/e2e</li>
    </ul>
  </li>
  <li>metrics to look out for
    <ul>
      <li>server-side latency</li>
      <li>CPU time</li>
      <li>memory, whether there are memory leaks and impact of GC</li>
    </ul>
  </li>
  <li>common practices
    <ul>
      <li>load test the product at a target RPS and observe how much resources is required to maintain a certain level of p90 latency</li>
      <li>run load tester from a different location to simulate realistic client</li>
      <li>deploy to remote servers instead of running on local machine</li>
      <li>use realistic dependencies</li>
      <li>scale and test your product on multiple replica to observe if load balancing works</li>
    </ul>
  </li>
  <li>typical workflow
    <ul>
      <li>plan what components to use for macro-benchmarking</li>
      <li>commit all details of the set up in code repo (test framework is likely sitting on a different repo)</li>
      <li>clearly document experiment details, like environment conditions and software versions (use Google doc for example)</li>
      <li>perform benchmark test</li>
      <li>confirm there are no functional errors</li>
      <li>save load tester results to the same document</li>
      <li>compile insights from various metrics and stats</li>
      <li>compile profiling results</li>
      <li>analyse and discover bottleneck, and implement improvements in a new branch of the system</li>
      <li>run the workflow again, and compare benchmark results</li>
      <li>merge branch and release if results are good</li>
    </ul>
  </li>
  <li>since macro-benchmark are more expensive and intensive, use it when required.
    <ul>
      <li>recommended as an acceptance test against RAER specs of the entire system after a major feature/release</li>
      <li>when debugging and optimising regressions or incidents that trigger efficiency problems</li>
    </ul>
  </li>
</ul>

<h1 id="bottleneck-analysis">Bottleneck Analysis</h1>

<h2 id="profiling-in-go">Profiling in Go</h2>

<ul>
  <li>using <code class="language-plaintext highlighter-rouge">pprof</code> has the advantage of using common representation, file format, and visualisation for different resource profile data</li>
  <li><code class="language-plaintext highlighter-rouge">runtime/pprof</code> can be used out of the box to profile the runtime</li>
  <li><code class="language-plaintext highlighter-rouge">google/pprof</code> can be used to read the output profile
    <ul>
      <li>can also display the profiles in the web browser in graphical form</li>
    </ul>
  </li>
  <li>profiling can be as significant as the other 3 signals (log, trace, metric) when analysing efficiency</li>
  <li>we tend to capture profiles during benchmarking</li>
  <li>there are 3 main patterns to capture profiles
    <ul>
      <li>instrument directly in the program code, supports custom profile</li>
      <li>triggers profile output when running Go benchmark (limited by what Go provides, which are CPU and memory)</li>
      <li>using HTTP handlers (make your Go program accept a request for profile data)</li>
    </ul>
  </li>
  <li>the Go standard library <code class="language-plaintext highlighter-rouge">net/http/pprof</code> already provides HTTP handlers you can easily add to your server</li>
  <li>you can connect the <code class="language-plaintext highlighter-rouge">pprof</code> web browser tool to your server to view the profiles</li>
  <li>common profilers to note
    <ul>
      <li>heap profiler</li>
      <li>goroutine profiler</li>
      <li>CPU profiler (this is expensive to gather, so it needs to be explicitly started and stopped. expect latency when requesting from HTTP handler. and the data will be sampled)</li>
      <li>Off-CPU time (block profiler) shows time our program spent waiting without utilising the CPU</li>
    </ul>
  </li>
  <li>advanced tips
    <ul>
      <li>profiles can be exported and shared to other team members</li>
      <li>continuous profiling by having a separate platform to periodically capture profiles from your server using the HTTP handlers (this can be done in production, or only during macro-benchmark tests)</li>
      <li><code class="language-plaintext highlighter-rouge">pprof</code> format supports comparing and aggregating profiles
        <ul>
          <li>one profile (e.g. tested using input A) can be subtracted from another profile (tested using input A+B)</li>
          <li>shows delta in different data across two profiles</li>
          <li>profiles can also be merged (this is not natively supported by <code class="language-plaintext highlighter-rouge">go tool pprof</code>)</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="optimisation-tips">Optimisation Tips</h1>

<ul>
  <li>optimise and benchmark one improvement at a time</li>
  <li>standard functions from Go may not be designed for your use case, and it can be a source of inefficiency</li>
  <li>standard functions may generally work on multiple types, and you may want to reimplement them for only a specific type that is relevant to your use case to be more efficient</li>
  <li>you can potentially use <code class="language-plaintext highlighter-rouge">unsafe</code> to perform deliberate optimisation, but we must be careful and understand the tradeoff (memory safety) and whether it truly meets our efficiency requirements</li>
  <li>stream data and process instead of allocating the entire space in memory</li>
  <li>when using concurrency to distribute work to improve latency, take note and observe communication overhead
    <ul>
      <li>a lot of time may be spent sending data over channels and queuing for channels</li>
      <li>sometimes, a distributed algorithm that guarantees no conflict without using channel communication may be a better choice to distribute work</li>
    </ul>
  </li>
  <li>sometimes, we can simply think out of the box instead of performing hard optimisation (e.g. cache the results that are frequently requested. this amortises any initial resource costs over repeated operations)</li>
</ul>

<h1 id="common-patterns">Common Patterns</h1>

<ul>
  <li>Do less work
    <ul>
      <li>skip unnecessary logic (e.g. double validating the same property)</li>
      <li>do things once (e.g. reuse memory in place, instead of allocating new memory)</li>
      <li>leverage math to do less (e.g. distributed algorithm instead of communication over channel)</li>
      <li>use pre-computed information (e.g. request for stats in API instead of recalculating stats)</li>
      <li>but be strategic, if you are just lazy, then you may incur more work in the future with tech debts</li>
    </ul>
  </li>
  <li>Trading functionality for efficiency</li>
  <li>Trading space for time
    <ul>
      <li>precompute results in a look up table</li>
      <li>caching</li>
      <li>augment data structure to contain more stats and metadata</li>
      <li>decompressed data</li>
    </ul>
  </li>
  <li>Trading time for space
    <ul>
      <li>usually the exact opposite of the above patterns</li>
    </ul>
  </li>
  <li>The Three Rs Optimisation Method
    <ul>
      <li>Reduce Allocation (e.g. prevent escape to heap)</li>
      <li>Reuse Memory (e.g. use already allocated objects for loops)</li>
      <li>Recycle (this is handled by GC, but we can prompt it)
        <ul>
          <li>optimise allocated struct (with less internal pointers)</li>
          <li>GC tuning (using the two options to GC more often, but is very brittle as our program evolves)</li>
          <li>manually trigger GC (generally not encouraged)</li>
          <li>allocating memory off-heap (using syscall like <code class="language-plaintext highlighter-rouge">mmap</code>)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>avoid resource leaks
    <ul>
      <li>we use an unbounded resource for the same amount of load</li>
      <li>eventually the resource runs out</li>
      <li>this can easily happen with a slice with short length but large underlying array</li>
      <li>this can easily happen with forgotten long-lived goroutines holding reference to large data in the heap</li>
    </ul>
  </li>
  <li>control goroutine lifecycle to prevent resource leaks
    <ul>
      <li>know when and how the goroutine will terminate</li>
      <li>will the goroutine run indefinitely? in what scenarios?</li>
      <li>should caller function wait for all goroutines to finish? then use <code class="language-plaintext highlighter-rouge">sync.WaitGroup</code></li>
      <li>if you can’t answer some of these questions, then there is a potential for resource leak</li>
      <li>there are libraries like <code class="language-plaintext highlighter-rouge">goleak</code> to help test for resource leaks</li>
    </ul>
  </li>
  <li>reliably close resources
    <ul>
      <li>always read docs. if the resource needs to be closed, always close it. no linters to help with this.</li>
      <li>using <code class="language-plaintext highlighter-rouge">defer</code> to close resource may fail. we should be notified using some kind of error capture or logger.</li>
      <li>sometimes, we need to pass resource handlers around and we cannot <code class="language-plaintext highlighter-rouge">defer</code> to close them within the same function that created them. We can append any resource closers to a list on creation, and before program exits we can loop through the list and close each resource.</li>
      <li>some resources don’t have closer, but requires us to exhaust/drain the resource to release it</li>
    </ul>
  </li>
  <li>pre-allocate when possible
    <ul>
      <li>this allows our program to operate on bounded resources</li>
      <li>we may potentially avoid dynamic resize/memory allocation during the lifetime of the program</li>
      <li>even for implementing linked list, where each node points to the next (requires the use of pointers), we can still optimise it
        <ul>
          <li>for example, pre-allocating an array of to store the nodes, to act like a resource pool</li>
          <li>nodes can still be created to point to another node, but all nodes are assigned in the array</li>
          <li>there will not be any nodes floating in the heap, but we are bounded by the size of the array</li>
          <li>memory will be contiguous</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>overusing memory in arrays
    <ul>
      <li>pre-allocating memory can also lead to overuse, if the array is large but our slice only use a small part of the memory</li>
      <li>we may need to implement methods that helps to dynamically copy data to a smaller array</li>
    </ul>
  </li>
  <li>memory reuse and pooling
    <ul>
      <li>resource pooling can help avoid repeated allocations (which improves allocation latency)</li>
      <li><code class="language-plaintext highlighter-rouge">sync.Pool</code> provides a form of memory pooling that is thread-safe</li>
      <li>it is expected that all values in the pool are functionally identical. therefore a memory pool is not a cache where values are different</li>
      <li>however, the <code class="language-plaintext highlighter-rouge">sync.Pool</code> is intended for short duration usage, and GC may potentially wiped out all unused resources in the pool, and requesting from the pool will result in new allocations.</li>
    </ul>
  </li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="golang" /><summary type="html"><![CDATA[[!info] title: Efficient Go: Data-Driven Performance Optimization author: Bartlomiej Plotka published: 2022 edition: 1 ISBN: 978-1098105716]]></summary></entry><entry><title type="html">Notes for: The Way to Go</title><link href="https://twydev.github.io/notes/the-way-to-go/" rel="alternate" type="text/html" title="Notes for: The Way to Go" /><published>2025-04-26T00:00:00+00:00</published><updated>2025-04-26T00:00:00+00:00</updated><id>https://twydev.github.io/notes/the-way-to-go</id><content type="html" xml:base="https://twydev.github.io/notes/the-way-to-go/"><![CDATA[<blockquote>
  <p>[!info]
title: The Way to Go: A Thorough Introduction to the Go Programming Language
author: Ivo Balbaert
published: 2012
edition: 1
ISBN: 978-1469769175</p>
</blockquote>

<p>I already have some experiences working on Golang codebase, so I will only highlight the important points from this book.</p>

<h1 id="design-objectives-of-golang">Design objectives of Golang</h1>

<ul>
  <li>it aims to be the “C for the 21st century”</li>
  <li>software needs to be built quickly (for developer productivity)
    <ul>
      <li>a rigid, clean, fast dependency analysis allows fast compilation</li>
      <li>explicit dependencies</li>
    </ul>
  </li>
  <li>language should run well on modern multi-core hardware</li>
  <li>language should work well in a networked environment</li>
  <li>language should be a pleasure to use</li>
  <li>efficacy, speed and safety (type and memory safe), strongly and statically compiled, ease of programming like a dynamic language</li>
  <li>garbage collected to avoid memory problems (therefore not suitable for real-time software)</li>
  <li>for backward-compatibility, it is able to run C code as well.</li>
</ul>

<p>what was removed from usual languages to maintain simplicity:</p>

<ul>
  <li>no function or operator overloading</li>
  <li>no implicit conversions</li>
  <li>no classes and type inheritance</li>
  <li>no variant types</li>
  <li>no dynamic code loading</li>
  <li>no dynamic libraries</li>
  <li>no generics (back then)</li>
  <li>no exceptions (but has panic and recover)</li>
  <li>no assertions (runtime assertions that throws error. not the same as type assertions)</li>
  <li>no immutable variables</li>
</ul>

<p>The spirit of Golang is to follow convention and idioms when writing code, so that it is easy to navigate between code bases.</p>

<h1 id="install-and-runtime-environment">Install and Runtime Environment</h1>

<ul>
  <li>Golang provides compiler for different OS and architecture (32/64 bits)
    <ul>
      <li>a word may be 32-bits (4 bytes) or 64-bits (8 bytes) depending on the architecture</li>
    </ul>
  </li>
  <li>Go compiler supports cross-compiling, developing a go program on a certain host architecture but compile it for a different target architecture</li>
  <li>with source code, you can also just compile the code in your own OS and architecture, as long as the OS and architecture is supported, your Go program will build correctly</li>
  <li>Go compiler generates native executable code.
    <ul>
      <li>but it will link a Go runtime code to every Go package</li>
      <li>the Go runtime is somewhat comparable to VM used by Java/.Net languages</li>
      <li>Go runtime is responsible for handling memory allocation, garbage collection, stack handling, goroutines, channels, and other managing memory reference data types.</li>
      <li>therefore Go compiled code will be much bigger than source code, due to the runtime embedded but deployment is much easier since there is no need to link any external files</li>
    </ul>
  </li>
</ul>

<h1 id="best-practices-and-conventions">Best Practices and Conventions</h1>

<ul>
  <li>naming should be short, concise, evocative, simplicity
    <ul>
      <li>package name should be lowercase, act as namespace</li>
      <li>functions/methods/variables don’t need to contain indication of the package name</li>
      <li>getters can be named as a noun e.g. <code class="language-plaintext highlighter-rouge">person.Name()</code> instead of <code class="language-plaintext highlighter-rouge">person.GetName()</code></li>
      <li>setters can be prefixed with verb e.g. <code class="language-plaintext highlighter-rouge">person.SetName()</code></li>
    </ul>
  </li>
  <li>there are some universal method names used in Go standard library, we are encouraged to adopt those names when implement similar functionality for our own programs e.g. <code class="language-plaintext highlighter-rouge">Open(), Read(), Write(), String()</code></li>
  <li>names of interfaces should have <code class="language-plaintext highlighter-rouge">-er</code> suffix, e.g. <code class="language-plaintext highlighter-rouge">Reader, Writer</code>
    <ul>
      <li>interfaces are short, usually max 3 methods</li>
    </ul>
  </li>
  <li><strong>always recover panic in your own package</strong>. panics should never cross package boundary, for a good developer experience for someone using your package.</li>
  <li>return errors as error value to the callers of your package</li>
  <li>documentation comments should be provided at the start of the package (the main entry, one of the file). this will be extracted by godoc.</li>
  <li><strong>Golang has no tail recursion optimization (TCO)</strong>
    <ul>
      <li>a function is tail recursive, if the recursive call is the final line in the function with no further computation</li>
      <li>this means the current recursion can be cleaned from the stack</li>
      <li>compilers that implements TCO will be able to optimize stack memory</li>
      <li>however Golang encourage you to use loop instead of recursion</li>
      <li>this keeps compiler and runtime implementation simple. it also generally leads to more readable code (arguable?)</li>
    </ul>
  </li>
</ul>

<h1 id="golang-constructs-basics">Golang Constructs Basics</h1>

<ul>
  <li>only 25 keywords. this allows you to keep all required knowledge in your mind while working with the language</li>
  <li>building a Go program
    <ul>
      <li>it must contain one package called <code class="language-plaintext highlighter-rouge">main</code></li>
      <li>main package can depend on other packages (by convention one directory per package)</li>
      <li>each package is compiled as a unit</li>
      <li>every piece of code is compiled only once</li>
      <li>if package A depends on B depends on C:
        <ul>
          <li>C is compiled first into C object file</li>
          <li>B is compiled, compiler will link to C object file</li>
          <li>A is compiled, but compiler will only link to B object file (this speeds up build at a large scale)</li>
        </ul>
      </li>
      <li>dependencies are explicitly declared by package imports</li>
    </ul>
  </li>
  <li>structure of a Go program/package (common convention)
    <ul>
      <li>import statements</li>
      <li>declare constants, variables, types</li>
      <li>define <code class="language-plaintext highlighter-rouge">init()</code> function, which executes set up when this package is imported</li>
      <li>define <code class="language-plaintext highlighter-rouge">main()</code> function (only if this is the <code class="language-plaintext highlighter-rouge">main</code> package)</li>
      <li>define methods on types</li>
      <li>define functions</li>
      <li>methods and functions can be defined in the order they have been used. or alphabetical order if large numbers of functions exist</li>
    </ul>
  </li>
  <li>execution of a Go program/package
    <ul>
      <li>packages in <code class="language-plaintext highlighter-rouge">main</code> are imported in the order as indicated, and recursively importing in every package (but each unique package is only imported once)</li>
      <li>for every package (in reverse order) all constants and variables are evaluated, and then <code class="language-plaintext highlighter-rouge">init()</code> function are executed (LIFO)
        <ul>
          <li>initialization is always single threaded, to guarantee correct execution order</li>
        </ul>
      </li>
      <li>finally the same initialization happens for <code class="language-plaintext highlighter-rouge">main</code> package</li>
      <li>then finally the <code class="language-plaintext highlighter-rouge">main()</code> function executes</li>
    </ul>
  </li>
</ul>

<h2 id="types">Types</h2>

<table>
  <thead>
    <tr>
      <th>type</th>
      <th>memory rep</th>
      <th>mutability</th>
      <th>zero val</th>
      <th>alloc/init mem</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>int</td>
      <td>value</td>
      <td>immutable</td>
      <td>0</td>
      <td>new()</td>
    </tr>
    <tr>
      <td>float</td>
      <td>value</td>
      <td>immutable</td>
      <td>0.0</td>
      <td>new()</td>
    </tr>
    <tr>
      <td>bool</td>
      <td>value</td>
      <td>immutable</td>
      <td>false</td>
      <td>new()</td>
    </tr>
    <tr>
      <td>string</td>
      <td>value</td>
      <td>immutable</td>
      <td>””</td>
      <td>new()</td>
    </tr>
    <tr>
      <td>array e.g. <code class="language-plaintext highlighter-rouge">[3]int</code></td>
      <td>value</td>
      <td><strong>mutable</strong></td>
      <td><code class="language-plaintext highlighter-rouge">[0,0,0]</code></td>
      <td>new()</td>
    </tr>
    <tr>
      <td>struct</td>
      <td>value</td>
      <td><strong>mutable</strong></td>
      <td>all fields zero value</td>
      <td>new()</td>
    </tr>
    <tr>
      <td>slice e.g. <code class="language-plaintext highlighter-rouge">[]int</code></td>
      <td>reference</td>
      <td>mutable</td>
      <td>nil</td>
      <td>make()</td>
    </tr>
    <tr>
      <td>map</td>
      <td>reference</td>
      <td>mutable</td>
      <td>nil</td>
      <td>make()</td>
    </tr>
    <tr>
      <td>channels</td>
      <td>reference</td>
      <td>mutable</td>
      <td>nil</td>
      <td>make()</td>
    </tr>
    <tr>
      <td>interface</td>
      <td>reference</td>
      <td>mutable</td>
      <td>nil</td>
      <td>N.A.</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>when a <strong>variable is declared, all memory is initialized, to the zero value for the corresponding type</strong>
    <ul>
      <li>for value types, the variable contains actual value data</li>
      <li>for reference types, the variable only contains a memory address to another location that contains the actual data structure</li>
      <li>reference types are therefore very similar to pointers</li>
      <li>and the zero value for pointers and reference types are <code class="language-plaintext highlighter-rouge">nil</code></li>
      <li>you are therefore discouraged by the IDE to not use a pointer of a reference type. just work directly with the reference type variable.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">new()</code> allocates memory for value types</li>
  <li><code class="language-plaintext highlighter-rouge">make()</code> is only for reference types, and it initializes the memory (or data structure) for that reference type so that it is usable
    <ul>
      <li>it typically accepts optional params, to initialize maps/slices of certain sizes</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">int</code>, <code class="language-plaintext highlighter-rouge">uint</code>, <code class="language-plaintext highlighter-rouge">uintptr</code> are architecture dependent, defaults to 32-bit or 64-bits</li>
  <li>strings are a sequence of UTF-8 characters with variable-width per character (will use 1 byte ASCII-code when possible, and expand the bytes used depending on the character) which allows Golang string to occupy less space as compared to C++ or Java.
    <ul>
      <li>using <code class="language-plaintext highlighter-rouge">for</code> loop over each byte will therefore produce undesirable results</li>
      <li>using <code class="language-plaintext highlighter-rouge">for range</code> loop over a string will allow the correct parsing of each UTF-8 character</li>
    </ul>
  </li>
</ul>

<h2 id="slice-in-memory">Slice in memory</h2>

<ul>
  <li>memory structure has 3 fields
    <ul>
      <li>has a pointer to underlying array, at the starting index of the slice</li>
      <li>has a length field</li>
      <li>has a capacity field</li>
    </ul>
  </li>
  <li>slice can be resliced up to the capacity limit (which is determined by the underlying array)
    <ul>
      <li>if a slice is shortened from the starting index, then there is no way to expand it back to the starting index, even if the underlying array still exists</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">append()</code> adds an element to the slice (and hence the underlying array is mutated)
    <ul>
      <li>if underlying array do not have sufficient capacity, a new slice (with new array) is allocated and returned. <strong>The original slice and array will remain intact.</strong></li>
    </ul>
  </li>
  <li>when slices are assigned to a new variable e.g. <code class="language-plaintext highlighter-rouge">slice2 := slice1</code>, the <strong>memory structure of slice1 is copied to slice2</strong>
    <ul>
      <li>so slice1 and slice2 points to the same underlying array</li>
      <li>but slice1 and slice2 length field can now mutate independently of each other</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">copy(dst, src []T) int</code> copies elements from source slice to destination slice
    <ul>
      <li>it returns number of elements copied</li>
      <li>it is limited by the minimum length between source and destination slices</li>
    </ul>
  </li>
  <li><strong>slices can potentially reference a very big array with high impact on memory</strong>
    <ul>
      <li>you can copy the relevant data to a new slice and return from your function</li>
      <li>the new slice will be using a shorter array with only relevant data</li>
      <li>this allows the underlying large array to be GC-ed</li>
    </ul>
  </li>
</ul>

<h2 id="map-in-memory">Map in memory</h2>

<ul>
  <li>maps are good for key-value store and retrieval
    <ul>
      <li>but it is still 100x slower than direct indexing in an array or a slice</li>
      <li>so take note for ultra high performance requirements</li>
    </ul>
  </li>
  <li>map capacity grows by only 1, whenever an additional element needs to be added beyond the current capacity
    <ul>
      <li><strong>it is better for performance to set the capacity for large maps or map that keeps growing to reduce memory operations during runtime</strong></li>
    </ul>
  </li>
</ul>

<h2 id="structs-in-memory">Structs in memory</h2>

<ul>
  <li>All data in a struct, even if it contains another struct, are allocated in a continuous block of memory
    <ul>
      <li>this gives huge memory performance boosts</li>
      <li>fields must therefore be defined in struct literal in the same order as their struct definition</li>
      <li>and since fields are sequentially defined, anonymous fields are supported, as long as you define the values in sequence</li>
      <li>anonymous fields will use the type as field name e.g. <code class="language-plaintext highlighter-rouge">myStruct.int, myStruct.float32</code></li>
      <li>therefore each struct can only have one anonymous field of the same type</li>
    </ul>
  </li>
  <li><strong>if struct contains reference types, those are allocated with zero values</strong>
    <ul>
      <li>those reference types must be initialized separately before they can be used</li>
      <li>it may be a good idea to use a factory pattern to create a new struct</li>
    </ul>
  </li>
  <li>Methods are functions that accept a certain struct type as receiver
    <ul>
      <li><strong>the receiver type and method implementations must be declared within the same package</strong></li>
      <li>you can define your method with either a pointer or value receiver
        <ul>
          <li>pointer receiver allows you to mutate the struct. it is also efficient to pass large struct data</li>
          <li><strong>value receiver is a copy of struct data, therefore modifying value type fields will not work.</strong></li>
          <li><strong>but a value receive will allow you to mutate reference type fields</strong>, since the receiver is a copy of the reference type containing a pointer to the underlying memory data.</li>
        </ul>
      </li>
      <li><strong>Golang runtime is smart enough to allow pointer/value receiver methods to interoperate on either pointer or value inputs</strong>
        <ul>
          <li>Golang will perform reference/dereference before calling the method</li>
          <li>So if you method expect a value receiver, it will still receive a value receiver, regardless of a pointer being used at the calling line of code</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="struct-inheritance">Struct Inheritance</h3>

<ul>
  <li>inheritance of fields and methods can achieved using embedding
    <ul>
      <li>“parent” is the inner struct</li>
      <li>“subtype” is the outer struct</li>
      <li>inner struct can be anonymous, so outer struct will inherit the fields and methods</li>
    </ul>
  </li>
  <li><strong>(gotcha) however if the inner struct method accesses a struct field as part of its implementation, the inner struct field will be used even if you called the method on the outer struct</strong>
    <ul>
      <li>the only way to achieve the desired override behavior is either for the outer struct to either define its own implementation of the same method</li>
      <li>or update the inner struct with a new field value</li>
      <li>this is expected, since methods are defined in the same package that defined the corresponding struct
        <ul>
          <li>if you subtype a third party package struct and expect that your outer struct shadow fields will override inner struct methods</li>
          <li>then the compiler and Go runtime will incur a lot more complexity and overhead</li>
        </ul>
      </li>
      <li>this explicit behavior also made the language simpler</li>
    </ul>
  </li>
  <li>embedding more inner structs allows multiple inheritance</li>
</ul>

<h2 id="interfaces">Interfaces</h2>

<ul>
  <li>interface can define a method set without implementation of methods (abstract)</li>
  <li>Go allows us to define a variable with the interface type (typescript also allows for this)</li>
  <li>the interface variable has the following structure in memory
    <ul>
      <li>the zero value of interface variable is <code class="language-plaintext highlighter-rouge">nil</code> (similar to pointer)</li>
      <li>it contains a method table pointer field, which points to the actual implementation of methods matching this interface, for a provided concrete type.</li>
      <li>it contains a receiver field, which holds the data of a concrete type variable
        <ul>
          <li>the receiver field can be assigned with a value, or a pointer</li>
          <li>e.g. if a struct is assigned to the interface variable, then it is a copy of a struct (similar to using value receiver for a struct method)</li>
          <li>e.g. if a struct pointer is assigned to the interface variable, then this field stores the memory address (similar to using pointer receiver for a struct method)</li>
          <li>e.g. if any other data types are assigned, the behavior will depend on whether they are primitive (values) or reference types</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>an interface can embed another interface to achieve inheritance, which is the same as explicitly enumerating all methods of the embedded interface</li>
  <li>type assertions
    <ul>
      <li>interface variable can be cast to concrete type using type assertion at runtime e.g. <code class="language-plaintext highlighter-rouge">t, ok := interfaceVar.(T)</code></li>
      <li>this can be used for type switches</li>
      <li>a concrete value can also be tested to see if it implements an interface e.g. <code class="language-plaintext highlighter-rouge">ivar, ok := var.(MyInterface)</code></li>
    </ul>
  </li>
  <li><strong>when calling methods on an interface variable, the reference/dereference receiver behaviors are different from struct methods</strong>
    <ul>
      <li>when interface variable contains a pointer
        <ul>
          <li>it can be called with pointer receiver methods</li>
          <li>it can be called with value receiver methods (automatic dereference)</li>
        </ul>
      </li>
      <li>when interface variable contains a value
        <ul>
          <li>it can be called with value receiver methods</li>
          <li>it <strong>cannot</strong> be called with pointer receiver methods</li>
        </ul>
      </li>
      <li>reason behind this behavior
        <ul>
          <li>when assigning to an interface, the method table pointer is determined upfront</li>
          <li>when you assign only a value to the interface, the method table pointer will only point to the set of value receiver methods</li>
          <li>when you assign a pointer to the interface, then the method table pointer will point to both value/pointer receiver methods</li>
          <li>since interface variable by design needs to be dynamic, it cannot perform the same automatic reference that a concrete struct type can perform</li>
          <li>this explicit behavior allows the code writer to be very intentional with which method set he is trying to assign when using the interface variable (he can choose to provide a value or a pointer)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>therefore when embedding inner interface as a pointer in an outer struct, it will allow the outer struct to be called with all methods (value/pointer receiver) of that interface. You will still need to implement the methods though, since interfaces are abstract.</li>
</ul>

<h3 id="empty-interface">Empty Interface</h3>

<ul>
  <li>an empty interface <code class="language-plaintext highlighter-rouge">type Any interface{}</code> can be used to represent an “any” type since it has no method set</li>
  <li>(gotcha) the code snippet below produces an error:</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">dataslice</span> <span class="p">[]</span><span class="n">myType</span> <span class="o">=</span> <span class="c">//..some data..//</span>
<span class="k">var</span> <span class="n">anyslice</span> <span class="p">[]</span><span class="k">interface</span><span class="p">{}</span> <span class="o">=</span> <span class="n">dataslice</span>

<span class="c">// error: cannot use dataslice (type []myType) as type []interface{} in assigment</span>
</code></pre></div></div>

<ul>
  <li>because the two variables <code class="language-plaintext highlighter-rouge">dataslice</code> and <code class="language-plaintext highlighter-rouge">anyslice</code> are two different concrete arrays, even though <code class="language-plaintext highlighter-rouge">anyslice</code> is an array containing “any” type of elements</li>
  <li>to overcome this problem, you can assign the elements one by one.</li>
</ul>

<h2 id="reflection">Reflection</h2>

<ul>
  <li>reflection first cast the input as an empty interface, then retrieves the various attributes of the input
    <ul>
      <li><code class="language-plaintext highlighter-rouge">reflect.TypeOf(x)</code> gives the type of the input</li>
      <li><code class="language-plaintext highlighter-rouge">reflect.ValueOf(x)</code> gives the raw value of the input</li>
      <li><code class="language-plaintext highlighter-rouge">reflect.ValueOf(x).Kind()</code> gives the type, based on the raw value</li>
      <li><code class="language-plaintext highlighter-rouge">reflect.ValueOf(x).Interface()</code> gives the interface value, based on the raw value</li>
    </ul>
  </li>
  <li>reflection allows you to change the value of the input
    <ul>
      <li><code class="language-plaintext highlighter-rouge">reflect.ValueOf(&amp;x)</code> must be provided with a pointer (<strong>this is a recurring design of Golang. use pointer to allow mutable value</strong>)</li>
      <li><code class="language-plaintext highlighter-rouge">v := reflect.ValueOf(&amp;x).Elem()</code> indirects through the pointer and returns the underlying element</li>
      <li><code class="language-plaintext highlighter-rouge">v.CanSet()</code> will be true</li>
      <li><code class="language-plaintext highlighter-rouge">v.SetFloat(1.234)</code> will succeed</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Printf(format string, args ... interface{})</code> uses reflection extensively to figure out what are the inputs and what format strings are supported.</li>
  <li>I have once encountered a function in production that attempts to mutate all string fields in an input struct
    <ul>
      <li>the function made heavy use of reflection</li>
      <li>one developer used the function but provided the in-built Error type instead of struct to the function call</li>
      <li>the Error was mutated, but it resulted in memory corruption</li>
      <li>later attempts by the program to log that Error object crashed the entire Go program (it was not even a panic that can be recovered. it was a fatal crash)</li>
      <li>my hypothesis of what could have happened:
        <ul>
          <li>the function uses reflection and recursively mutated any string fields in a struct (and any nested structs)</li>
          <li>however, Error contains an un-exported struct that contains the string message and a length field</li>
          <li>the function mutated the Error string (making the string shorter), but it failed to update the length field</li>
          <li>this violated the memory alignment assumptions</li>
          <li>when the Go runtime later tries to access the Error object in memory, it reads the unmodified length, and reads that segment of memory, which includes addresses that are already invalid, this resulted in a segmentation fault.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h1 id="standard-library">Standard Library</h1>

<p>just some notes on standard library</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">net/rpc</code> provides utility for RPC</li>
  <li><code class="language-plaintext highlighter-rouge">encoding/gob</code> (stands for Go Binary) provides de/serialization to binary format. it can be considered as an alternative for data representation (to JSON which is popular). But it can only be used by Go processes since it uses Go reflection.</li>
  <li><code class="language-plaintext highlighter-rouge">netchan</code> (stands for network channel) has been moved out of Golang v1</li>
  <li><code class="language-plaintext highlighter-rouge">websocket</code> has been moved out of Golang v1</li>
</ul>

<p>I think standard library must be studied by all users of Go, to avoid reinventing the wheel.</p>

<h1 id="golang-oop-equivalence">Golang OOP Equivalence</h1>

<ul>
  <li>Encapsulation
    <ul>
      <li>package scope: objects defined within their own package with a name that starts with lowercase</li>
      <li>exported: objects defined within their own package with a name that starts with uppercase</li>
      <li>a type can only have methods defined in its own package</li>
    </ul>
  </li>
  <li>Inheritance (by composition)
    <ul>
      <li>embedding types with desired behaviors</li>
      <li>supports multiple inheritance through multiple embedded types</li>
    </ul>
  </li>
  <li>Polymorphism
    <ul>
      <li>a variable of a type can be assigned to a variable of an interface, if type implements the interface</li>
      <li>types and interfaces are loosely coupled</li>
      <li>multiple inheritance is supported by types implementing multiple interfaces</li>
    </ul>
  </li>
</ul>

<h1 id="json-handling">JSON Handling</h1>

<ul>
  <li>JSON handling will be common for web application development</li>
  <li>Golang to JSON data type conversions
    <ul>
      <li>bool marshals to JSON booleans</li>
      <li>float64 marshals to JSON numbers</li>
      <li>string marshals to JSON strings</li>
      <li>nil marshals to JSON null</li>
      <li>maps can be marshalled to JSON if keys of the maps are string</li>
      <li>channel, complex, function types cannot be marshalled</li>
      <li>cyclical data structure cannot be marshalled as it will cause the <code class="language-plaintext highlighter-rouge">Marshal()</code> function call to loop infinitely</li>
      <li>pointers are marshalled as the values they are pointing to</li>
      <li>only exported fields of a struct will be marshalled</li>
    </ul>
  </li>
</ul>

<h1 id="error-handling">Error Handling</h1>

<ul>
  <li>you can create a new error object with custom message using <code class="language-plaintext highlighter-rouge">errors.New()</code></li>
  <li>you can also create a new error object with format string message using <code class="language-plaintext highlighter-rouge">fmt.Errorf()</code></li>
  <li>you can also implement your own custom error by having a type implement the built-in error interface
    <ul>
      <li>typically used if you need to load more data into the error object or you need to implement custom methods on the error object</li>
    </ul>
  </li>
  <li>if a runtime problem occurs, a panic will be triggered (value of interface type <code class="language-plaintext highlighter-rouge">runtime.Error</code>)</li>
  <li>flow of control when panic occurs in a nested function call (also called panicking)
    <ul>
      <li>current function execution stops immediately</li>
      <li><code class="language-plaintext highlighter-rouge">defer</code> functions executes in LIFO order</li>
      <li>control is passed back to function caller</li>
      <li>caller either handles the panic, or continue execution of defer functions and bubble panic upwards</li>
      <li>at top level, program crashes and error condition is reported to the CLI using the value in the panic</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">recover</code> can only be used inside a deferred function
    <ul>
      <li>retrieves error value passed through the call of panic</li>
      <li>some standard libraries uses recover so that errors are explicitly returned to caller, instead of causing a panic to bubble from the API</li>
    </ul>
  </li>
  <li>(gotcha) <code class="language-plaintext highlighter-rouge">defer</code> expression are evaluated at the line they are called, but the function will be executed later in the deferred phase
    <ul>
      <li>e.g. <code class="language-plaintext highlighter-rouge">defer myFn(420+69)</code> the summation will be evaluated on the spot</li>
    </ul>
  </li>
</ul>

<h1 id="testing">Testing</h1>

<ul>
  <li>standard library <code class="language-plaintext highlighter-rouge">testing</code> provides automated testing, logging, error reporting, benchmarking</li>
  <li>test programs must sit in the same package as source programs</li>
  <li>test programs must be named <code class="language-plaintext highlighter-rouge">*_test.go</code></li>
  <li>test programs are not compiled by the normal Go compiler (so they are not deployed). only <code class="language-plaintext highlighter-rouge">gotest</code> compiles all programs</li>
  <li>test functions should be named with the Test prefix and have a particular header form <code class="language-plaintext highlighter-rouge">TestXXX(t *testing.T)</code></li>
  <li>the options <code class="language-plaintext highlighter-rouge">-cpuprofile</code> and <code class="language-plaintext highlighter-rouge">-memprofile</code> can output the respective resources profile into a file</li>
  <li><code class="language-plaintext highlighter-rouge">pprof</code> is a runtime library that needs to be imported into your source program to output more profiling data for analysis</li>
</ul>

<h1 id="concurrency">Concurrency</h1>

<h2 id="goroutines">Goroutines</h2>

<ul>
  <li>Golang provides goroutines and channels for concurrency, which requires support from the language, compiler and runtime.</li>
  <li>The spirit of Go concurrency: <strong>Do not communicate by sharing memory. Share memory by communicating</strong>.</li>
  <li>Communication forces coordination</li>
  <li>concurrency and parallelism
    <ul>
      <li>a single process on a machine run in its own address space</li>
      <li>it can be made up of multiple OS threads, running concurrently on 1 processor core (multi-threaded)</li>
      <li>only if the same application process runs on multiple core, then it is considered parallelized</li>
    </ul>
  </li>
  <li>the classic concurrency model in other programing languages
    <ul>
      <li>synchronize different threads by locking data</li>
      <li>this is complex and error-prone</li>
      <li>race conditions lead to unpredictable results</li>
    </ul>
  </li>
  <li>Go uses the Communicating Sequential Processes or Message Passing Model
    <ul>
      <li>Go provides goroutines to run concurrent computations</li>
      <li>goroutines do not correspond one-to-one to OS threads</li>
      <li>the Go runtime component, goroutine-scheduler, manages the multiplexing of goroutine to OS threads</li>
      <li>goroutines can run on multiple threads or within one thread only, and these are abstracted from us and well managed by Go runtime</li>
      <li>goroutines share memory address space, but synchronizing data is discouraged (but provided by <code class="language-plaintext highlighter-rouge">sync</code> standard library).</li>
      <li>channels should be used to communicate between goroutines</li>
    </ul>
  </li>
  <li>goroutine footprint
    <ul>
      <li>it is lightweight since it is a virtual abstraction on top of actual OS threads</li>
      <li>they are created with a 4K memory stack-space on the heap</li>
      <li>segmented stack can grow or shrink dynamically according to usage, all managed by Go runtime</li>
      <li>therefore a large number of goroutines can be created on the fly (~100K in the same address space)</li>
      <li>memory are freed when goroutines exit (no need for GC). goroutines exit silently without notifying the caller.</li>
      <li>similarly, <strong>when <code class="language-plaintext highlighter-rouge">main()</code> returns or exits, the Go program terminates. It doesn’t wait for goroutines to finish</strong>.
        <ul>
          <li><code class="language-plaintext highlighter-rouge">main()</code> can be designed to receive signals from goroutines</li>
          <li><code class="language-plaintext highlighter-rouge">main()</code> can attempt to wait for all goroutines to complete execution before exiting (can consider using <code class="language-plaintext highlighter-rouge">sync.WaitGroup</code>)</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">runtime.Gosched()</code> called within goroutine function will yield the processor (can be used if computation is intensive), but there is no need to explicitly or manually resume the goroutine, the Go runtime will handle that.</li>
    </ul>
  </li>
  <li><strong>by focusing on writing good concurrent programs, we let the underlying Go runtime decide if the goroutines will be run in parallel using multiple cores or not. because a concurrent program that runs well can also be parallelized well.</strong></li>
  <li>number of cores to use can be set using <code class="language-plaintext highlighter-rouge">runtime.GOMAXPROCS(n)</code>
    <ul>
      <li>experiments suggests best performance is obtained by setting n = one less than number of cores on the machine</li>
      <li>higher n does not necessarily means better performance due to communication overhead</li>
      <li>if n is set to 1 then only 1 core will be used, and there will be no parallel executions.</li>
      <li>in later versions of Golang, Go runtime by default uses all available core, if <code class="language-plaintext highlighter-rouge">GOMAXPROCS</code> is not set</li>
    </ul>
  </li>
  <li>goroutines may run during <code class="language-plaintext highlighter-rouge">init()</code> function of <code class="language-plaintext highlighter-rouge">main()</code></li>
</ul>

<h2 id="channels">Channels</h2>

<ul>
  <li>channel is like a conduit (pipe) through which goroutines can send data (typed values) between each other
    <ul>
      <li>it is like a message queue</li>
      <li>data follows a FIFO order</li>
    </ul>
  </li>
  <li><strong>only one goroutine has access to a data item at any given time, data races therefore cannot occur by design</strong></li>
  <li><strong>channel sending and receiving are atomic</strong>, they always complete without interruption</li>
  <li>Go compiler is able to detect some deadlock scenarios using static analysis, if it knows that some goroutines will be blocked due to missing send/receive implementation on channels</li>
  <li>by default, communication are synchronous and unbuffered
    <ul>
      <li>sender will be blocked until a receiver is ready to receive</li>
      <li>and similarly, receiver is blocked until a data is sent</li>
      <li>and if more sender or receiver tries to access the channel, they will be waiting behind the current blocked sender/receiver</li>
    </ul>
  </li>
  <li>asynchronous communication can be achieved through use of buffered channels <code class="language-plaintext highlighter-rouge">make(chan type, bufSize)</code>
    <ul>
      <li>this prevents blocking the sender (unless buffer is full) or receiver (unless buffer is empty)</li>
      <li>in a multiprocessor set up, using a buffered channel for producer-consumer pattern can result in goroutines that never block, since the producer and consumer may run in parallel</li>
      <li>however, the bigger the channel buffer, the more memory will be used. benchmarking can help us to figure out the optimal performance and memory tradeoff and setting the appropriate buffer size.</li>
    </ul>
  </li>
  <li>channel variables can also be declared to be omnidirectional
    <ul>
      <li><code class="language-plaintext highlighter-rouge">chan&lt;-</code> can only send data</li>
      <li><code class="language-plaintext highlighter-rouge">&lt;-chan</code> can only receive data</li>
      <li>misusing them after declaration will therefore cause compile errors</li>
      <li>and read-only channels cannot be closed. <strong>the convention is to have the sender close the channel to signal end of processing</strong>.</li>
    </ul>
  </li>
  <li>the convention is usually to close the channel using defer right after channel creation
    <ul>
      <li>receiver can test whether the receive failed because channel has been closed using <code class="language-plaintext highlighter-rouge">v, ok := &lt;-ch</code></li>
      <li>if receiver is using <code class="language-plaintext highlighter-rouge">for v := range ch {}</code> construct, the loop will automatically ends when channel has been closed</li>
    </ul>
  </li>
  <li>select statement (looks similar to switch-case) can be used to select between different channels to send/receive
    <ul>
      <li>if all channels are blocked, it waits until one channel can proceed</li>
      <li>if multiple channels can proceed, it chooses one at random</li>
      <li>therefore it is usually used in a loop, to implement a listener pattern. the loop will be broken by an explicit break statement when conditions are met</li>
      <li>if default clause is present, then it will be executed if no channels can proceed</li>
    </ul>
  </li>
</ul>

<h3 id="channel-usage-patterns">Channel Usage Patterns</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">time.Ticker</code> and <code class="language-plaintext highlighter-rouge">time.Tick()</code> returns a channel that sends a tick at specified time intervals, which you can use to implement time driven tasks (like periodic logging, or rate limit, since receiving will block until the next tick)</li>
  <li>select statement can be used to implement some interesting patterns to avoid blocking
    <ul>
      <li>listener
        <ul>
          <li>select between multiple different channels</li>
          <li>one of the channel can be dedicated to sending the quit signal</li>
          <li>if all channels are blocked, and quit signal is received, the loop can terminate and the goroutine can clean up and exit</li>
        </ul>
      </li>
      <li>timeout
        <ul>
          <li>select between receiving from a data channel, and a time based channel</li>
          <li>if data comes first, move on</li>
          <li>if time based channel receives first, then a timeout has occurred</li>
        </ul>
      </li>
      <li>fastest result (similar to JavaScript <code class="language-plaintext highlighter-rouge">Promise.race()</code>)
        <ul>
          <li>create a common buffered channel (this is a minor optimization, in case the main function is still spinning up goroutines, but one of the goroutine has already completed execution. having buffered channel allows sending data without waiting for a receiver)</li>
          <li>main function spins up goroutines</li>
          <li>the main function blocks to receive the result from the common channel</li>
          <li>each goroutine queries data from a different external source</li>
          <li>each goroutine implements a select statement with only one case: to send the queried data to the common channel</li>
          <li>but the select statement also has an empty default clause</li>
          <li>so all goroutine will query the data first before attempting to send the data to the channel</li>
          <li>when it is time to send data, the fastest goroutine will succeed first.</li>
          <li>the slower goroutine will be blocked by the channel.</li>
          <li>but since they are attempting to send data in a select case statement, they will fallback to execute the empty default clause</li>
          <li>and then the slower goroutine will exit, without blocking and without sending data.</li>
          <li>this ensure that we only receive the fastest result, and all other goroutines will automatically clean up</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>implement semaphores
    <ul>
      <li>a buffered channel with n capacity</li>
      <li>it can hold n number of placeholder data which represents semaphores</li>
      <li>goroutines “check out” the semaphore from the channel before accessing shared memory</li>
      <li>goroutines “check in” the semaphore back to the channel after they are done</li>
    </ul>
  </li>
  <li>implement lazy evaluation generators
    <ul>
      <li>since sender will block unless receivers read the data</li>
      <li>and if sender is in a loop</li>
      <li>then the sender only computes the next result in each loop after sending succeeds</li>
    </ul>
  </li>
  <li>implement futures pattern (similar to JavaScript <code class="language-plaintext highlighter-rouge">Promise</code>)
    <ul>
      <li>the concept is similar to lazy evaluation</li>
      <li>you can pass the reference for future around, while sending a separate goroutine to compute the results</li>
      <li>you can then “await” for the future when you finally need the result</li>
    </ul>
  </li>
  <li>exclusive data access via “background task”
    <ul>
      <li>set up a single goroutine to execute functions sent over a channel (reference to a function can be passed)</li>
      <li>only that goroutine can execute those functions, which exclusively access shared data in memory</li>
      <li>other goroutines/functions will dispatch functions to the channel</li>
      <li>this is simply another implementation of semaphores</li>
    </ul>
  </li>
  <li>concurrent computation of sequential tasks
    <ul>
      <li>a typical single-threaded approach could be to loop over the data, and in each iteration, perform tasks A, B, C in sequence</li>
      <li>we can re-write this by having task A communicating output to task B, and then to task C, using channels, forming a pipeline</li>
      <li>each step in the pipeline (A, B, C) can be run on a different goroutine, since these goroutines now communicate exclusively using channels</li>
      <li>the only loop we need is to iterate over the data, and sending the data into the pipeline</li>
      <li>we delegated the scheduling of concurrent computation to the Go runtime (this can also be parallelized potentially, depending on architecture and set up of your Go runtime, but it is abstracted from us)</li>
    </ul>
  </li>
</ul>

<h3 id="my-understanding-on-channels">My understanding on Channels</h3>

<ul>
  <li>Golang essentially offered a queue-like data structure with exclusive access for goroutines, to achieve the same synchronization that we typically use with locks and shared memory</li>
  <li>exclusive access is the key feature here, as it essentially removes deadlock from poor implementation occurred when manually managing locks</li>
  <li>the same channel behavior can definitely be implemented using traditional locks and share memory, nothing special here. so Golang is basically having the channel API, and encouraging us to use this API and adopt a different paradigm to manage our concurrency</li>
</ul>

<h1 id="common-pitfalls">Common Pitfalls</h1>

<ul>
  <li>never use <code class="language-plaintext highlighter-rouge">goto</code> with a preceding label (usage of <code class="language-plaintext highlighter-rouge">goto</code> is intended to skip forward)</li>
  <li>when implementing <code class="language-plaintext highlighter-rouge">String()</code> method, don’t use other functions like <code class="language-plaintext highlighter-rouge">fmt.Print</code> that itself depends on <code class="language-plaintext highlighter-rouge">String()</code> (this causes infinite recursive loop)</li>
  <li>always use <code class="language-plaintext highlighter-rouge">Flush()</code> to terminate buffered writing</li>
  <li>never change the <code class="language-plaintext highlighter-rouge">for</code> loop counter variable inside the loop itself. use the language construct.</li>
  <li>use factory pattern to create your structs (if it contains reference types that needs to be initialized)</li>
  <li>use pointer receiver if you need to mutate the receiver</li>
  <li>reference types do not need to be passed explicitly as pointers to functions (interface type is also a reference type!)</li>
  <li>do not misuse <code class="language-plaintext highlighter-rouge">defer</code> inside a loop to clean up resources
    <ul>
      <li><code class="language-plaintext highlighter-rouge">defer</code> will finally execute in LIFO order after function returns, NOT at the end of the loop</li>
      <li>it may lead to a large number of unclosed resources if the function is large and does not return</li>
      <li>directly close the resources in each loop iteration is simpler</li>
    </ul>
  </li>
  <li><strong>prefer to pass value to functions when it is small (cheap to copy). pass pointer if mutability or size dictates</strong>
    <ul>
      <li>passing by value to the function, means the value lives on the stack</li>
      <li>stack allocation is fast (only bumping the pointer)</li>
      <li>stack is auto-cleaned when function pops from the stack</li>
      <li>stack is cache friendly (CPU cache reads from contiguous stack memory faster)</li>
      <li>does not use the heap, which leads to less memory fragmentation in the heap over time</li>
      <li>heap memory requires GC</li>
      <li>Go compiler uses <strong>escape analysis</strong> to decide if a variable can live on the stack or the heap</li>
    </ul>
  </li>
  <li><strong>always start with a simple single-threaded implementation. only use goroutines and channels when concurrency becomes a concern</strong>
    <ul>
      <li>it is more important to get the program right</li>
    </ul>
  </li>
</ul>

<h2 id="misusing-closures-and-goroutines">Misusing Closures and Goroutines</h2>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c">// version A</span>
	<span class="k">for</span> <span class="n">ix</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">values</span> <span class="p">{</span>
		<span class="k">go</span> <span class="k">func</span><span class="p">(){</span>
			<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">ix</span><span class="p">)</span>
		<span class="p">}()</span>
	<span class="p">}</span>

	<span class="c">// version B</span>
	<span class="k">for</span> <span class="n">ix</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">values</span> <span class="p">{</span>
		<span class="k">go</span> <span class="k">func</span><span class="p">(</span><span class="n">ix</span> <span class="k">interface</span><span class="p">{}){</span>
			<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">ix</span><span class="p">)</span>
		<span class="p">}(</span><span class="n">ix</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="c">// version C</span>
	<span class="k">for</span> <span class="n">ix</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">values</span> <span class="p">{</span>
		<span class="n">val</span> <span class="o">:=</span> <span class="n">values</span><span class="p">[</span><span class="n">ix</span><span class="p">]</span>
		<span class="k">go</span> <span class="k">func</span><span class="p">(){</span>
			<span class="n">fmt</span><span class="o">.</span><span class="n">Print</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
		<span class="p">}()</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>version A will not work as intended
    <ul>
      <li><code class="language-plaintext highlighter-rouge">ix</code> is a variable in the <code class="language-plaintext highlighter-rouge">main()</code> function scope</li>
      <li>all goroutines have shared access to a single <code class="language-plaintext highlighter-rouge">ix</code> variable</li>
      <li>the goroutine executions are scheduled by the runtime, and may only begin at the end of the main loop</li>
      <li>there is a chance that all goroutine is reading the same value for <code class="language-plaintext highlighter-rouge">ix</code></li>
    </ul>
  </li>
  <li>version B works
    <ul>
      <li>because the <code class="language-plaintext highlighter-rouge">ix</code> value is copied to goroutine stack on invocation</li>
      <li>in each loop iteration, a unique value is copied</li>
      <li>even if goroutines are scheduled to run at the end of the loop, all goroutines have received a different value in their function argument</li>
    </ul>
  </li>
  <li>version C also works
    <ul>
      <li>because <code class="language-plaintext highlighter-rouge">val</code> is a variable that only exists in the scope of each iteration (not shared between iteration)</li>
      <li>each goroutine is invoked with a closure over a unique <code class="language-plaintext highlighter-rouge">val</code> variable</li>
    </ul>
  </li>
</ul>

<h1 id="common-language-specific-patterns">Common Language Specific Patterns</h1>

<ul>
  <li>comma, ok pattern</li>
  <li>defer pattern (to close resources, to recover from panic)</li>
  <li>visibility pattern (exported objects)</li>
  <li>factory pattern (to initialize un-exported fields)
    <ul>
      <li>naturally you will make a struct private, but export the factory function from your package to create the struct correctly</li>
    </ul>
  </li>
  <li>operator pattern
    <ul>
      <li>implement operators as methods</li>
      <li>the methods are internally responsible to switch between types and operate on the values correctly</li>
      <li>this pattern allows chaining of operations</li>
      <li>an interface can be used to describe this polymorphism</li>
    </ul>
  </li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">x</span> <span class="o">*</span><span class="n">SubTypeA</span><span class="p">)</span> <span class="n">Add</span> <span class="p">(</span><span class="n">y</span> <span class="n">MainType</span><span class="p">)</span> <span class="n">MainType</span>

<span class="k">func</span> <span class="p">(</span><span class="n">x</span> <span class="o">*</span><span class="n">SubTypeB</span><span class="p">)</span> <span class="n">Add</span> <span class="p">(</span><span class="n">y</span> <span class="n">MainType</span><span class="p">)</span> <span class="n">MainType</span>

<span class="n">subtypeA</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">subtypeB</span><span class="p">)</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">subtypeC</span><span class="p">)</span>

<span class="c">// each subtype implements the same interface</span>
<span class="k">type</span> <span class="n">Operable</span> <span class="k">interface</span> <span class="p">{</span>
	<span class="n">Add</span><span class="p">(</span><span class="n">x</span> <span class="n">MainType</span><span class="p">)</span> <span class="n">MainType</span>
	<span class="n">Subtract</span><span class="p">(</span><span class="n">x</span> <span class="n">MainType</span><span class="p">)</span> <span class="n">MainType</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="performance-advice">Performance Advice</h2>

<ul>
  <li>convert string to byte slice to manipulate characters in a string</li>
  <li>use <code class="language-plaintext highlighter-rouge">utf8.RuneCountInString(str)</code> to count number of characters correctly (because characters uses variable number of bytes in Golang)</li>
  <li>use <code class="language-plaintext highlighter-rouge">bytes.Buffer</code> instead of string concatenation to accumulate string content (fastest and most memory efficient)</li>
  <li>when using goroutine for concurrent computation
    <ul>
      <li><strong>work done inside the goroutine must be much higher than the overhead of creating goroutine and sending data through channels to gain performance benefit</strong></li>
      <li>buffered channels can help increase throughput (at the cost of memory)</li>
      <li>channels can become bottlenecks, consider passing references to shared memory instead (e.g. pass pointers to array, and unpack the values in the receiver instead)</li>
    </ul>
  </li>
  <li>use slices when possible instead of arrays</li>
  <li>use <code class="language-plaintext highlighter-rouge">for-range</code> loop over a slice if you only need the value, not the index, of the elements (this is slightly faster as it does not perform individual look up for elements by index)</li>
  <li>using a <code class="language-plaintext highlighter-rouge">map</code> instead of a sparse <code class="language-plaintext highlighter-rouge">array</code> can reduce memory footprint</li>
  <li>specify initial capacity for maps</li>
  <li>use pointer receiver for methods of structs</li>
  <li>using constants or flags to extract constant values from the code (hardcoded literal built into the compiled code, no memory allocation is required at runtime)</li>
  <li>use caching when large amount of memory are being allocated</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="golang" /><summary type="html"><![CDATA[[!info] title: The Way to Go: A Thorough Introduction to the Go Programming Language author: Ivo Balbaert published: 2012 edition: 1 ISBN: 978-1469769175]]></summary></entry><entry><title type="html">Notes for: PostgreSQL Query Optimization: The ultimate guide to building efficient queries</title><link href="https://twydev.github.io/notes/postgresql-query-optimization/" rel="alternate" type="text/html" title="Notes for: PostgreSQL Query Optimization: The ultimate guide to building efficient queries" /><published>2025-04-25T00:00:00+00:00</published><updated>2025-04-25T00:00:00+00:00</updated><id>https://twydev.github.io/notes/postgresql-query-optimization</id><content type="html" xml:base="https://twydev.github.io/notes/postgresql-query-optimization/"><![CDATA[<blockquote>
  <p>[!info]
title: PostgreSQL Query Optimization: The ultimate guide to building efficient queries
author: Dombrovskaya H., Novikov B. and Bailliekova A.
published: 2021
edition: 1
ISBN: 978-1484268841</p>
</blockquote>

<h1 id="why-optimise">Why Optimise</h1>

<ul>
  <li>Optimisation starts with gathering requirements about the use case the system is serving.
    <ul>
      <li>normalise or denormalise tables, to serve use cases that might require searching a smaller set of data.</li>
      <li>unique and primary identifier concerns.</li>
      <li>frequency of access.</li>
      <li>acceptable response time (mission critical apps vs report generation has different tolerance).</li>
      <li>resource consumption</li>
      <li>throughput required</li>
      <li>think about optimised query as early as possible in the application development</li>
      <li>questioning the business intent of queries, may help us find elegant solutions that avoid complex queries all together.</li>
    </ul>
  </li>
  <li>Two key concepts to think like a database:
    <ul>
      <li>how a database engine processes a query</li>
      <li>how a query planner decides what execution path to choose</li>
    </ul>
  </li>
  <li>Even though SQL is declarative, we may construct queries in imperative fashion and end up having hard-coded execution path, which can never be optimised by the database engine.</li>
</ul>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">--imperative</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">student</span>
<span class="k">WHERE</span> <span class="n">class_id</span> <span class="k">in</span>
      <span class="p">(</span><span class="k">SELECT</span> <span class="n">class_id</span> <span class="k">FROM</span> <span class="k">class</span> <span class="k">WHERE</span> <span class="n">cohort_year</span> <span class="o">=</span> <span class="s1">'2021'</span><span class="p">);</span>

<span class="c1">-- declarative</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">student</span> <span class="n">st</span>
         <span class="k">join</span> <span class="k">class</span> <span class="n">cl</span> <span class="k">ON</span> <span class="n">st</span><span class="p">.</span><span class="n">class_id</span> <span class="o">=</span> <span class="n">cl</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="n">cl</span><span class="p">.</span><span class="n">cohort_year</span> <span class="o">=</span> <span class="s1">'2021'</span><span class="p">;</span>
</code></pre></div></div>

<ul>
  <li>Two main classes of systems:
    <ul>
      <li>OLTP (Online Transaction Processing) systems, usually optimised for short queries.</li>
      <li>OLAP (Online Analytical Processing) systems, usually optimised for both long and short queries.</li>
    </ul>
  </li>
  <li>Observe performance trends.
    <ul>
      <li>Query may degrade over time as volume of data increase or distribution of data changes.</li>
      <li>We may also need to update queries for new PostgreSQL release and features.</li>
    </ul>
  </li>
</ul>

<h2 id="postgresql-quirks">PostgreSQL quirks</h2>

<ul>
  <li>PostgreSQL has different implementation compared to other databases.</li>
  <li>Optimisation techniques specific to other databases may not be applicable to PostgreSQL.</li>
  <li>It is important to be aware of new features and releases and consider how they can be used for our apps.</li>
  <li>For example, PostgreSQL by design does not accept hints from users.
    <ul>
      <li>The query planner will choose the best path independently.</li>
      <li>Users simply focus on writing declarative SQL queries.</li>
    </ul>
  </li>
</ul>

<h1 id="processing-and-operations">Processing and Operations</h1>

<h2 id="processing-overview">Processing Overview</h2>

<ul>
  <li><strong>1. Compilation</strong>
    <ul>
      <li>Different DB servers can interpret the same SQL query differently.</li>
      <li>Compiled SQL queries are declarative high-level logical operations (logical plan), which does not determine final execution.</li>
    </ul>
  </li>
  <li><strong>2. Optimization</strong>
    <ul>
      <li>Optimiser replaces logical operations with their execution algorithms by choosing the best out of possible algorithms.</li>
      <li>Optimiser may change the logical expression structure by changing the order in which logical operations will be executed.</li>
      <li>Optimiser seeks to minimise physical resources used (including execution time) by choosing the best physical operations (execution plan) for the given logical plan.</li>
    </ul>
  </li>
  <li><strong>3. Execution</strong>
    <ul>
      <li>The executor simply runs the plan and return the results.</li>
    </ul>
  </li>
</ul>

<h2 id="relational-operations">Relational Operations</h2>

<ul>
  <li>Theoretical.</li>
  <li>We can simplify relational theory by assuming a <em>relation</em> is a table.</li>
  <li>Any relational operations take one of more relations as input, and produces another relation as output. Relational operations can therefore be chained.</li>
  <li><strong>filter</strong>: returns all rows from the input relation that satisfy filter condition.</li>
  <li><strong>project</strong>: returns the input relation with some attributes removed and rows de-duplicated.</li>
  <li><strong>product</strong>: (a.k.a. Cartesian product) returns all pairs of rows from two input relations.</li>
  <li>The SQL <code class="language-plaintext highlighter-rouge">JOIN</code> is simply a product followed by a filter operation.</li>
  <li>Other operations include <strong>grouping, union, intersection</strong>, and <strong>set difference</strong></li>
  <li>All operations satisfy the equivalence rules:
    <ul>
      <li>Commutativity: JOIN(R,S) = JOIN (S,R)</li>
      <li>Associativity: JOIN(R, JOIN(S,T)) = JOIN(JOIN(R,S), T)</li>
      <li>Distributivity: JOIN(R, UNION(S,T)) = UNION(JOIN(R,S), JOIN(R, T))</li>
    </ul>
  </li>
</ul>

<h2 id="logical-operations">Logical Operations</h2>

<ul>
  <li>Logical operations extends the capabilities of relational operations to support SQL constructs.</li>
  <li>Operations also obeys equivalence rules. Equivalence rules is the key to allowing the optimiser to use different expressions to produce the same output.</li>
  <li>Chaining those operations works like pure functions (no side effects), which is optimal for database query performance.</li>
  <li>It is easier for humans to think about operations working on a set (or a table) than to think about iterating over individual rows of data, to write better declarative queries.</li>
</ul>

<h1 id="algorithms">Algorithms</h1>

<ul>
  <li>Logical operations are implemented as algorithms (sometimes with a few alternative implementation per operation)</li>
  <li>Query planner will choose the best algorithm to use to optimise resource consumption.</li>
  <li>Multiple algorithms may need to work in sequence (data access, followed by transformation) to support a single logical operation.</li>
</ul>

<h2 id="algorithm-cost-models">Algorithm Cost Models</h2>

<ul>
  <li>The primary metrics are <strong>CPU cycles</strong> and <strong>I/O accesses</strong>.</li>
  <li>Available memory, memory distribution, also affects the ratio of primary metrics (but this is server params, out-of-scope).</li>
  <li>Query optimiser uses a single composite of both metric to compare cost across different algorithms.</li>
  <li>The cost metric used to be dominated by I/O accesses (because hard drives rotation are more expensive) but for modern hardware (e.g. SSD) this may not be relevant.</li>
  <li>Optimiser should be tuned to consider modern hardware (again, this is server params, out-of-scope).</li>
  <li>Cost also depends on input table to the operation.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">R = table</code></li>
      <li><code class="language-plaintext highlighter-rouge">TR = number of rows</code></li>
      <li><code class="language-plaintext highlighter-rouge">BR = number of storage blocks occupied by table</code></li>
    </ul>
  </li>
</ul>

<h2 id="data-access-algorithms">Data Access Algorithms</h2>

<ul>
  <li>These operations are usually combined with subsequent operations. E.g. filtering rows after reading from table is less efficient than simply reading the required rows.</li>
  <li><strong>Selectivity</strong>: Ratio between total number of rows in table vs rows that will be retained after the operations</li>
  <li>Choice of read algorithm is influenced by selectivity of subsequent filter operations, that can be simultaneously applied.</li>
</ul>

<h3 id="storage-structures">Storage Structures</h3>

<ul>
  <li>Data are stored as files in hard drives.</li>
  <li>Files used for database objects (rows, tables, indexes …) are divided in blocks of the same length (PostgreSQL by default uses 8192 bytes or 8Kb per block)</li>
  <li>Several small items may reside in a single block, large items may span multiple blocks.</li>
  <li>The allocation of items to blocks also depends on the type of the database object (e.g. a table uses the heap data structure).</li>
  <li>A block is the unit transferred between hard drive and main memory.</li>
  <li>Therefore, the <strong>number of I/O operations = number of blocks transferred</strong> during a read/write.</li>
</ul>

<p>![[image-postgresql-harddisk-block.png]]</p>

<figure class=""><img src="/assets/images/image-postgresql-harddisk-block.png" alt="" /><figcaption>
      Block structure in PostgreSQL

    </figcaption></figure>

<h3 id="full-scan">Full Scan</h3>

<p>Cost of full scans with a filter condition is simply</p>

<ul>
  <li>cost of total blocks read from the table +</li>
  <li>cost of total rows processed in memory +</li>
  <li>cost of filter (determined by selectivity, annotated as <code class="language-plaintext highlighter-rouge">S</code>) multiplied by total number of rows</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cost = (c1 * BR) + (c2 * TR) + (c3 * S * TR)
</code></pre></div></div>

<p>where <code class="language-plaintext highlighter-rouge">cX</code> are hardware dependent constants.</p>

<h3 id="index-based-table-access">Index-Based Table Access</h3>

<ul>
  <li>Tables store rows in heap data structure, therefore rows are unordered.</li>
  <li>Indexes provide additional data access path, for us to determine where data is stored without scanning the table.</li>
  <li>Two physical operations to read retrieve rows via indexes:</li>
  <li>First, the <code class="language-plaintext highlighter-rouge">index scan</code>:
    <ul>
      <li>the algorithm scans index for pointers to all rows that satisfy condition.</li>
      <li>the entire block that contains the row will be read.</li>
      <li>as the table uses heap, multiple rows may be stored on the same block.</li>
      <li>the same block may be retrieved multiple times (inefficient)</li>
    </ul>
  </li>
  <li>Second, the <code class="language-plaintext highlighter-rouge">bitmap heap scan</code>:
    <ul>
      <li>a bitmap indicating blocks that contain needed rows are first generated.</li>
      <li>then all the rows in those blocks are filtered for those we need.</li>
      <li>very efficient to generate multiple bitmaps from multiple indexes, then use them all in a single query (bitmaps can be evaluated together using logical AND/OR)</li>
    </ul>
  </li>
  <li>The cost of such access is approximately:
    <ul>
      <li>for low selectivity, rows are sparsely distributed, fewer blocks will be retrieved, so cost is proportional to number of rows retrieved.</li>
      <li>for high selectivity, almost all blocks will be retrieved (perform worse than full scan due to additional steps to read indexes and generate bitmaps)</li>
    </ul>
  </li>
</ul>

<h3 id="index-only-scan">Index-Only Scan</h3>

<ul>
  <li>Index-only scan takes place when we combine project operation with data access, and all the columns needed in the projection are also in the index.</li>
  <li>The cost of such access is approximately:
    <ul>
      <li>proportional to number of rows retrieved (for all levels of selectivity)</li>
      <li>for high selectivity, it will still perform better than the above access types, because retrieval contains less data.</li>
    </ul>
  </li>
</ul>

<h2 id="comparing-data-access-algorithms">Comparing Data Access Algorithms</h2>

<p>In general:</p>

<ul>
  <li>index-only scan has the best performance and is preferred.</li>
  <li>index-based table access is better than full scan for low selectivity.</li>
  <li>full scan execution cost is almost constant, regardless of selectivity, and will be better than index-based table access for high selectivity use cases.</li>
</ul>

<p>Choosing the best algorithm then depends on use cases.</p>

<ul>
  <li>Any algorithm can become a winner under certain conditions.</li>
  <li>Decision also depends on storage structures and statistical properties of the data. The database maintains metadata (statistics) for tables, e.g. column cardinality, sparseness. Such statistics change over the lifecycle of the app.</li>
  <li>The declarative nature of SQL is essential, to allow query optimiser to use all the above information to select the best execution plan for the same SQL query.</li>
</ul>

<h2 id="index-structures">Index Structures</h2>

<p>Instead of using structural properties, we will define an index based on its usage. It must be:</p>
<ul>
  <li>a redundant data structure (in relation to the table).</li>
  <li>transparent to user/apps.</li>
  <li>designed to improve query performance.</li>
</ul>

<p>Index update when rows are updated introduces some overhead, but modern RDBMS has algorithm to optimise such updates.</p>

<blockquote>
  <p>[!tip]
Unlike other databases, PostgreSQL does not have clustered indexes. All tables are heap tables, all indexes are non-clustered. (Clustered index defines the order in which data is physically stored in a table)</p>
</blockquote>

<h3 id="b-tree-indexes">B-Tree Indexes</h3>

<p>(Using a <code class="language-plaintext highlighter-rouge">student</code> table as example, with PK <code class="language-plaintext highlighter-rouge">student_id</code> we want to index by <code class="language-plaintext highlighter-rouge">name</code>)</p>

<ul>
  <li>B-Tree is also known as self-balancing tree. Nodes can have more than 2 children.</li>
  <li>Nodes are associated with blocks on the disk.</li>
  <li>Leaf nodes contain actual index records (the indexed key, maps to table row id e.g. <code class="language-plaintext highlighter-rouge">name</code> maps to <code class="language-plaintext highlighter-rouge">student_id</code>).</li>
  <li>Non-leaf nodes contain child node locations (smallest indexed key in child node maps to block address e.g. <code class="language-plaintext highlighter-rouge">name</code> map to <code class="language-plaintext highlighter-rouge">block_addr</code>).</li>
  <li>Number of nodes to traverse in a search = height of tree.</li>
  <li>At least half of each block’s capacity will be used.</li>
  <li>Records in all nodes are ordered by index key (<code class="language-plaintext highlighter-rouge">name</code>).</li>
  <li>Range queries <code class="language-plaintext highlighter-rouge">BETWEEN</code> are supported by finding the smallest indexed key in the range, then sequentially scanning records in the leaf nodes.</li>
  <li>Records also need to be scanned if one index key (<code class="language-plaintext highlighter-rouge">name</code>) maps to a few rows (<code class="language-plaintext highlighter-rouge">student_id</code>) (so a few students have the same name).</li>
  <li>Height of tree = Logarithm of <code class="language-plaintext highlighter-rouge">order of tree (or branching/degree of a node)</code> / Logarithm of <code class="language-plaintext highlighter-rouge">total number of records</code></li>
</ul>

<p>![[image-postgresql-b-tree.png]]</p>

<figure class=""><img src="/assets/images/image-postgresql-b-tree.png" alt="" /><figcaption>
      B-tree in PostgreSQL

    </figcaption></figure>

<p><strong>Why B-Tree is used so often</strong></p>

<ul>
  <li>Update performance of B-Tree is better than binary tree or ordered list, even though searching a binary tree/ordered list is slightly faster.
    <ul>
      <li>An insert only affects a specific block in the tree. If block is full, it will be split, and update will propagate up the tree to parent nodes.</li>
      <li>Maximum number of blocks affected by an insert = height of tree.</li>
    </ul>
  </li>
  <li>Within a block, searching for a record will use binary search (fast).</li>
  <li>Since nodes can have multiple children, a tree with a few levels can index massive amount of rows.</li>
  <li>B-tree works for any index key with ordinal data type (values that can be compared greater/less than)</li>
</ul>

<h3 id="bitmaps">Bitmaps</h3>

<ul>
  <li>Used internally by PostgreSQL to facilitate access to other data structures containing data blocks.</li>
  <li>A bit is used to indicate if a block contains relevant data.</li>
  <li>Multiple indexes can generate bitmaps and be evaluated together.</li>
  <li>A bitmap signals to the engine which block may contain records that satisfy both index conditions, but does not guarantee that any single row in that block matches both conditions. This helps to limit number of blocks accessed.</li>
  <li>To speed up bitmaps, PostgreSQL builds hierarchical structure for bitmaps to avoid accessing irrelevant portions of the map.</li>
</ul>

<p>![[image-postgresql-bitmaps.png]]</p>

<figure class=""><img src="/assets/images/image-postgresql-bitmaps.png" alt="" /><figcaption>
      Bitmaps in PostgreSQL

    </figcaption></figure>

<h3 id="other-kinds-of-indexes">Other kinds of indexes</h3>

<p><strong>Hash Indexes</strong></p>

<ul>
  <li>Use hashing to calculate address of an index block containing an index key.</li>
  <li>Much faster than B-Tree, for equality search.</li>
  <li>Useless for range search.</li>
</ul>

<p><strong>R-Tree Indexes</strong></p>

<ul>
  <li>Like a B-Tree, but for spatial data.</li>
  <li>Index key represents a rectangle in multi-dimensional space.</li>
  <li>Search to return all objects that intersects with query rectangle.</li>
</ul>

<p>There are many other indexes, e.g. for full text search, or large tables, that are out-of-scope here.</p>

<h2 id="combining-relations">Combining Relations</h2>

<ul>
  <li>A set of identical algorithms can be used to support multiple operations like Cartesian product, joins, union, intersection, and even grouping.</li>
</ul>

<h3 id="nested-loops">Nested Loops</h3>

<ul>
  <li>To find product of two tables <code class="language-plaintext highlighter-rouge">R</code> and <code class="language-plaintext highlighter-rouge">S</code>.</li>
  <li>Loop, for each row in <code class="language-plaintext highlighter-rouge">R</code>, iterate each row in <code class="language-plaintext highlighter-rouge">S</code>, apply filter conditions (if we are evaluating JOIN).</li>
  <li>Theoretically, performance cost is proportional to the size of <code class="language-plaintext highlighter-rouge">R</code> and <code class="language-plaintext highlighter-rouge">S</code> and we can hardly do better.</li>
  <li>JOIN operation is the same as applying a product, followed by a filter. Cost remains the same, since all records will be iterated.</li>
  <li>Nested loops can be combined with data access algorithms and techniques to create variations, that may improve performance in specific cases. Some e.g.
    <ul>
      <li>using index scans before nested loops.</li>
      <li>loading multiple blocks of <code class="language-plaintext highlighter-rouge">R</code> in memory to consolidate and perform a single pass over <code class="language-plaintext highlighter-rouge">S</code>.</li>
    </ul>
  </li>
</ul>

<h3 id="hash-based-algorithms">Hash-based Algorithms</h3>

<ul>
  <li>Natural joins (means condition of <code class="language-plaintext highlighter-rouge">R</code> join <code class="language-plaintext highlighter-rouge">S</code> is equality).</li>
  <li>If attributes are equal, the hash of those attributes will also be equal.</li>
  <li>Hash-join algorithm has two phase:
    <ul>
      <li>build phase, all tuples of <code class="language-plaintext highlighter-rouge">R</code> are stored in buckets according to their hashed value.</li>
      <li>probe phase, all rows in <code class="language-plaintext highlighter-rouge">S</code> are sent to matching bucket, and matched with rows from <code class="language-plaintext highlighter-rouge">R</code> to produce output.</li>
      <li>these are shown as two physical steps in execution plans.</li>
    </ul>
  </li>
  <li>PostgreSQL uses a more efficient matching algorithm based on Bloom filtering.</li>
  <li>Cost is approximately:
    <ul>
      <li>size(<code class="language-plaintext highlighter-rouge">R</code>) +</li>
      <li>size(<code class="language-plaintext highlighter-rouge">S</code>) +</li>
      <li>(size(<code class="language-plaintext highlighter-rouge">R</code>) + size(<code class="language-plaintext highlighter-rouge">S</code>))/(number of different values of join attribute)</li>
    </ul>
  </li>
  <li>This performs better than simple nested loops for large tables and large number of different values of join attribute.</li>
  <li>If buckets produced in build phase cannot fit into main memory, will need to use hybrid hash join algorithm, that only loads a partition of the tables into main memory, and processing takes place by partition.</li>
</ul>

<h3 id="sort-merge-algorithm">Sort-Merge Algorithm</h3>

<ul>
  <li>Sort-Merge for natural joins works in two phase:
    <ul>
      <li>Sort phase sorts both tables by the join attributes in ascending order.</li>
      <li>Merge phase scans both tables once, for matching join attribute generate Cartesian product of rows.</li>
    </ul>
  </li>
  <li>Cost is approximately:
    <ul>
      <li>Sort cost =
        <ul>
          <li>size(<code class="language-plaintext highlighter-rouge">R</code>) * log size(<code class="language-plaintext highlighter-rouge">R</code>) +</li>
          <li>size(<code class="language-plaintext highlighter-rouge">S</code>) * log size(<code class="language-plaintext highlighter-rouge">S</code>)</li>
        </ul>
      </li>
      <li>Merge cost = same as hash join, but without the cost of build phase.</li>
    </ul>
  </li>
  <li>This algorithm is especially efficient if inputs are already sorted, e.g. in a series of join of the same join attribute.</li>
</ul>

<h2 id="comparing-join-algorithms">Comparing Join Algorithms</h2>

<ul>
  <li>Depends on use case and situations.</li>
  <li>Nested loop is more universal, good for small index-based joins.</li>
  <li>Sort-Merge and hash joins are good for larger tables.</li>
</ul>

<h1 id="understanding-execution-plans">Understanding Execution Plans</h1>

<ul>
  <li>Use the <code class="language-plaintext highlighter-rouge">EXPLAIN</code> command to generate an execution plan from an SQL query.</li>
  <li>For the query planner, choosing execution plan is a nondeterministic process.</li>
  <li>Even when the plans are identical, execution times may vary with differences in hardware and configuration.</li>
</ul>

<h2 id="reading-execution-plans">Reading Execution Plans</h2>

<p>![[image-postgresql-execution-plan.png]]</p>

<figure class=""><img src="/assets/images/image-postgresql-execution-plan.png" alt="" /><figcaption>
      Query Plan in PostgreSQL

    </figcaption></figure>

<ul>
  <li>Each row contains:
    <ul>
      <li>An operation that will be performed</li>
      <li>Estimated cost, which is accumulated from all previous operations before this current operation. There are two values in the cost:
        <ul>
          <li>cost to get the first row</li>
          <li>cost to get all results</li>
        </ul>
      </li>
      <li>Estimated number of rows of output (based on database statistics)</li>
      <li>Expected average width of a row</li>
    </ul>
  </li>
  <li>These are estimates, and errors grow as more operations are applied.</li>
  <li><code class="language-plaintext highlighter-rouge">pgAdmin</code> provides graphical tool to display the plan as a tree.</li>
  <li>Operation at the rightmost offset will be executed first.
    <ul>
      <li>Independent leaf nodes can be executed in parallel.</li>
      <li>There is no need to store intermediate results between operations. A row will be pushed to the next operation once generated.</li>
    </ul>
  </li>
  <li>Execution starts from leaf nodes and ends at the root.</li>
</ul>

<h2 id="how-was-the-plan-optimised">How was the plan optimised</h2>

<p>Recap, during optimisation, the optimiser will:</p>

<ol>
  <li>Determine the possible orders of operations</li>
  <li>Determine the possible execution algorithms for each operation</li>
  <li>Compare the costs of different plans</li>
  <li>Select the optimal execution plan</li>
</ol>

<p>Therefore, plans of the same SQL query may vary in:</p>
<ul>
  <li>Order of operations</li>
  <li>Algorithms used for joins and other operations (e.g., nested loops, hash join)</li>
  <li>Data retrieval methods (e.g., indexes usage, full scan)</li>
</ul>

<p>The optimiser does not search through all plans in the search space. It relies on optimality principle:</p>

<ul>
  <li>Start with the smallest sub-plan and find the optimal.</li>
  <li>Move up the plan tree to search for optimal sub-plan, with descendent nodes already optimised.</li>
  <li>Heuristics also helps to reduce search space.</li>
</ul>

<h2 id="how-are-costs-calculated">How are costs calculated</h2>

<p>The cost of each execution plan depends on:</p>

<ul>
  <li>Cost formulas of algorithms used in the plan</li>
  <li>Statistical data on tables and indexes, including distribution of values (this affects selectivity)</li>
  <li>System settings (parameters and preferences), such as <code class="language-plaintext highlighter-rouge">join_collapse_limit</code> or <code class="language-plaintext highlighter-rouge">cpu_index_tuple_cost</code></li>
</ul>

<p>There is no best plan, since the factors affecting cost will change according to database usage. Example from the book:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">flight_id</span><span class="p">,</span> <span class="n">scheduled_departure</span>
<span class="k">FROM</span> <span class="n">flight</span> <span class="n">f</span>
         <span class="k">JOIN</span> <span class="n">airport</span> <span class="n">a</span>
              <span class="k">ON</span> <span class="n">departure_airport</span> <span class="o">=</span> <span class="n">airport_code</span>
                  <span class="k">AND</span> <span class="n">iso_country</span> <span class="o">=</span> <span class="s1">'US'</span><span class="p">;</span>
</code></pre></div></div>

<ul>
  <li>Because selectivity of this filter condition <code class="language-plaintext highlighter-rouge">iso_country = 'US'</code> is high, the optimised plan will use a full scan.</li>
  <li>However, if we change this to a low selectivity condition like <code class="language-plaintext highlighter-rouge">iso_country = 'CZ'</code>, then the optimised plan will use a bitmap heap scan instead.</li>
</ul>

<h2 id="optimisers-are-not-perfect">Optimisers are not perfect</h2>

<ul>
  <li>The optimisers estimate cost using formulas that assumes uniform distribution of data, which only provides an imperfect approximation of real use cases.</li>
  <li>The optimisers use database statistics which are accurate for stored tables, but has limited effectiveness on intermediate results of an execution.</li>
  <li>The optimisers may miss the optimal plan in their search, because of heuristics, or because query is too complex.</li>
</ul>

<p>In all these cases, human intervention might be required to fix the situation.</p>

<h1 id="short-queries-and-indexes">Short Queries and Indexes</h1>

<p>A query is short when the number of rows needed to compute its output is small, no matter how large the involved tables are.</p>

<ul>
  <li>Not just the initial input rows need to be small, the intermediate results between operations should also be small.</li>
  <li>Short queries may read every row from small tables but read only a small percentage of rows from large tables.</li>
  <li>The definition of “small percentage” depends on system parameters, application specifics, actual table sizes, and possibly other factors.</li>
</ul>

<p>The optimisation goal of short query is therefore to reduce the size of the result set as early as possible. If the most restrictive selection criterion is applied in the first steps of query execution, further sorting, grouping, and even joins will be less expensive. To achieve this, we need indexes.</p>

<h2 id="details-about-indexes">Details about Indexes</h2>

<h3 id="index-selectivity">Index Selectivity</h3>

<ul>
  <li>The lower the index selectivity, the better the performance for short queries. (e.g. it doesn’t make sense to create an index on population data using gender as the index key, because the selectivity is very high)</li>
  <li>We should ensure that search criteria of a query will use indexes.</li>
  <li>Among all search criteria, having the most restrictive (the lowest selectivity) criteria satisfied by an index will produce the best performance.</li>
  <li><code class="language-plaintext highlighter-rouge">UNIQUE</code> index = for each indexed value, there is only one matching row in the table. Unique indexes has the best selectivity performance.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">PRIMARY KEY</code> is simply a short-hand for <code class="language-plaintext highlighter-rouge">UNIQUE</code> and <code class="language-plaintext highlighter-rouge">NOT NULL</code> constraints. Primary keys can be composite attributes.</li>
      <li><code class="language-plaintext highlighter-rouge">UNIQUE</code> constraint is nullable.</li>
      <li>the unique index can be created separately from the table instead of being defined as a constraint. However, upon creation all rows in the table will be validated. Index will not be created if validation fails. After creation, subsequent insert/update to the table will be constrained.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">FOREIGN KEY</code> is a referential integrity constraint, which does not create an index by default.
    <ul>
      <li>joining and searching foreign key relations will still be slow unless indexes are explicitly created.</li>
    </ul>
  </li>
  <li><strong>Best Practice</strong>: Creating index on foreign key column/join column, will not always improve performance. We should only create index is number of distinct values is large enough (low selectivity).</li>
</ul>

<h3 id="column-transformation">Column Transformation</h3>

<ul>
  <li>A column transformation occurs when the search criteria are on some modifications of the values in a column. E.g. casting string value to timestamp type before filtering.</li>
  <li>The index on original column attribute cannot be used for search that requires transformation.</li>
  <li>A functional index may be required to serve the use case
    <ul>
      <li>create index by applying a function to a column attribute, and storing the transformed value as index key.</li>
    </ul>
  </li>
  <li>Column transformation may be subtle in your search, so your execution plan may be running full sequential scan, when you assume that an index is used.</li>
  <li>Some queries may be re-written to produce the same results without transformation, which is one option to optimising.</li>
  <li>E.g. when searching for all records created today,
    <ul>
      <li>instead of casting the <code class="language-plaintext highlighter-rouge">created_at</code> timestamp column to date type</li>
      <li>to perform an equality comparison to today’s date,</li>
      <li>we can decompose the search condition into a range search on the existing <code class="language-plaintext highlighter-rouge">created_at</code> timestamp column,</li>
      <li>range between timestamp at the start of today and timestamp at the end of today.</li>
    </ul>
  </li>
</ul>

<h3 id="like-operator">LIKE Operator</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">LIKE</code> search criteria is not supported by B-tree, so a full scan will likely be executed.</li>
  <li><code class="language-plaintext highlighter-rouge">LIKE</code> search can be re-written as a range search:
    <ul>
      <li>e.g. <code class="language-plaintext highlighter-rouge">WHERE last_name like 'johns%'</code> can be decomposed to a range</li>
      <li><code class="language-plaintext highlighter-rouge">WHERE last_name &gt;= 'johns' AND last_name &gt;= 'johnt'</code></li>
    </ul>
  </li>
  <li><strong>Best Practice</strong>: create a pattern search index to support such use cases.
    <ul>
      <li>e.g. <code class="language-plaintext highlighter-rouge">text_pattern_ops</code>, <code class="language-plaintext highlighter-rouge">varchar_pattern_ops</code>, and <code class="language-plaintext highlighter-rouge">bpchar_pattern_ops</code></li>
      <li>e.g. creation <code class="language-plaintext highlighter-rouge">CREATE INDEX test_index ON test_table (col varchar_pattern_ops);</code></li>
      <li>comparison of text values depends on the locale, which determines character ordering, formatting, which varies by languages/countries.</li>
      <li>use <code class="language-plaintext highlighter-rouge">SHOW LC_COLLATE;</code> to see the locale setting in the database.</li>
      <li><code class="language-plaintext highlighter-rouge">LIKE</code> operator on an attribute column with pattern search index will automatically use the index to optimise execution.</li>
    </ul>
  </li>
</ul>

<h3 id="using-multiple-indexes">Using Multiple Indexes</h3>

<ul>
  <li>PostgreSQL will create in-memory bitmaps of multiple indexes to reduce the blocks that will be accessed.</li>
</ul>

<h3 id="compound-indexes">Compound Indexes</h3>

<ul>
  <li>Indexes built from multiple columns.</li>
  <li>In general, an index on (X,Y,Z) will be used for:
    <ul>
      <li>searches on X,</li>
      <li>searches on XY,</li>
      <li>searches on XYZ,</li>
      <li>and even searches on XZ</li>
      <li>But index on (X,Y,Z) will never be used on searches on Y alone or on YZ.</li>
    </ul>
  </li>
  <li><strong>Best Practice</strong>: When creating compound indexes, deciding the columns to include, and the sequence of columns are equally important, and should consider the use cases.</li>
  <li>Compound indexes may perform better than using multiple individual indexes when:
    <ul>
      <li>the compound index significantly lower selectivity. (uniqueness only surface from a combination of columns..</li>
      <li>the compound index contains sufficient columns to satisfy the <code class="language-plaintext highlighter-rouge">SELECT</code> query (index-only scan is sufficient without table access).</li>
    </ul>
  </li>
</ul>

<h3 id="covering-indexes">Covering Indexes</h3>

<ul>
  <li>Introduced in PostgreSQL 11.</li>
  <li>A covering index is specifically designed to include the columns needed by a particular type of query that you run frequently.</li>
</ul>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">test_index</span>
    <span class="k">ON</span> <span class="n">test_table</span>
        <span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">Z</span><span class="p">)</span> <span class="n">INCLUDE</span> <span class="p">(</span><span class="n">W</span><span class="p">);</span>
</code></pre></div></div>

<ul>
  <li>The column <code class="language-plaintext highlighter-rouge">W</code> is included in the covering index to avoid accessing the table, but it is not used as search criteria.</li>
  <li>For small columns, it may not matter if the extra column is indexed for search.</li>
  <li>However, for wider columns, the use of covering index helps keep the index key compact.</li>
</ul>

<h3 id="excessive-selection-criteria">Excessive Selection Criteria</h3>

<ul>
  <li>This refers to the practice of providing additional, redundant filters to prompt the database engine to use specific indexes or reduce the size of join arguments.</li>
  <li>Some SQL queries simply cannot be optimised if the search conditions depends on result values from multiple tables.
    <ul>
      <li>Analysing business requirements may reveal that it is not necessary to search through the entire space from data since the dawn of time.</li>
      <li>Excessive selection criteria may be applied to limit the search space of large tables, which the query optimiser can use to re-order queries and optimise execution.</li>
    </ul>
  </li>
</ul>

<h3 id="partial-indexes">Partial Indexes</h3>

<ul>
  <li>Refers to indexes built from only a subset of rows in a table.
    <ul>
      <li>e.g. flights scheduled do not have actual_departure, until the flight takes place.</li>
      <li>create a partial index for only flights with a non-null actual_departure value will help improve search for flights that already took place.</li>
    </ul>
  </li>
  <li>Another use case: searching for retail orders with status = ‘REJECTED’ will automatically use this partial index to narrow search space.</li>
</ul>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- possible status values are NEW, ACCEPTED, REJECTED, CANCELLED</span>
<span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">retail_order_rejected</span>
<span class="k">ON</span> <span class="n">retail_order</span> <span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="k">WHERE</span> <span class="n">status</span><span class="o">=</span><span class="s1">'REJECTED'</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="indexes-and-join-order">Indexes and Join Order</h3>

<ul>
  <li>Applying most restrictive selection criteria first with indexes helps reduce intermediate result size.</li>
  <li>Intermediate result size can also be minimised by performing semi-joins where one side of the argument significantly capped the size of join results.</li>
  <li>Most of the time query planner is able to choose the most optimal plan, unless it was forced by SQL query to select a bad ordering.</li>
</ul>

<h2 id="manipulating-index-usage">Manipulating Index Usage</h2>

<h3 id="avoiding-index-usage">Avoiding Index Usage</h3>

<ul>
  <li>We want to avoid using indexes when:
    <ul>
      <li>A small table is completely read into main memory.</li>
      <li>We need a large proportion of the rows of a table to execute a query.</li>
    </ul>
  </li>
  <li>Most of the time query planner is smart enough to detect that indexes should not be used if it yields poorer performance.</li>
  <li>To manually force this, we can apply inconsequential column transformation (e.g. adding zero to numeric values).</li>
</ul>

<h3 id="forcing-index-usage">Forcing Index Usage</h3>

<ul>
  <li>If the query planner is not using your index, it is likely because using the index will yield poorer performance. (PostgreSQL optimizer is smart.)</li>
  <li><strong>Best Practice</strong>: Let PostgreSQL do its job.</li>
</ul>

<h2 id="how-to-build-the-right-indexes">How to Build the Right Indexes</h2>

<ul>
  <li>(Traditional) reasons against building too many indexes:
    <ul>
      <li>take up extra space in database.</li>
      <li>slows down insert/update rows with overhead. (Prevailing guidance is to drop indexes before bulk loading, then create them again.)</li>
    </ul>
  </li>
  <li>Times have changed:
    <ul>
      <li>disk storage is cheaper and faster. Fast response is more critical than saving space.</li>
      <li>even if total size of indexes exceeds table size for OLTP systems, it is acceptable.</li>
    </ul>
  </li>
  <li><strong>Best Practices</strong>:
    <ul>
      <li>create partial indexes whenever it makes sense. These reduces search space, allows working data to fit in main memory.</li>
      <li>create covering indexes whenever it makes sense. These reduces round trips to the table.</li>
      <li>keep an eye on all indexes, to detect slowness for inserts/updates. Poor performance are usually caused by:
        <ul>
          <li>unique/primary keys.</li>
          <li>foreign key references to unique/primary keys in other tables.</li>
          <li>SQL triggers on inserts/updates.</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">pg_stat_all_indexes</code> shows index usage. We can consider removing unused indexes.</li>
    </ul>
  </li>
</ul>

<h2 id="maintaining-indexes-for-scalability">Maintaining Indexes for Scalability</h2>

<ul>
  <li>As data size grows, index performance may worsen due to:
    <ul>
      <li>index becoming too large to fit into main memory.</li>
      <li>index gets pushed out by indexes for competing queries.</li>
    </ul>
  </li>
  <li>When we design queries and indexes, we aim for them to scale well to a reasonable extent.
    <ul>
      <li><strong>No optimised set up lasts forever</strong>.</li>
      <li>keep an eye on data volume growth.</li>
      <li>keep an eye on data distribution.</li>
    </ul>
  </li>
</ul>

<h1 id="long-queries">Long Queries</h1>

<p>A query is long if:</p>

<ul>
  <li>selectivity is high for at least one of the large tables. That is, almost all rows contribute to the output (aggregation), even when the output size is small.</li>
  <li>or, anything that is not a short query is a long query (LOL)</li>
</ul>

<p>Overall strategy to optimise long queries is to:</p>

<ul>
  <li>Avoid multiple table scans.</li>
  <li>Reduce the size of the result at the earliest possible stage.</li>
</ul>

<h2 id="scans-and-joins">Scans and Joins</h2>

<h3 id="full-scans">Full Scans</h3>

<ul>
  <li>Full scans are more desirable for long queries.</li>
  <li>Index will use more I/O access than full scan due to high selectivity so should be avoided.</li>
  <li>However, actual measure of whether full scan is required is decided by PostgreSQL, and is also dependent on hardware specs.</li>
</ul>

<h3 id="hash-joins">Hash Joins</h3>

<ul>
  <li>Hash joins are always more preferable to nested loops as the cost is lower (refer to formula in earlier sections)</li>
</ul>

<h3 id="rule-of-thumb">Rule of thumb</h3>

<ul>
  <li>Index access works well with nested loop.</li>
  <li>Sequential scan works well with hash join.</li>
  <li>We should look for these indicators to see that query plans meet expectation (our job is to verify the plans).</li>
  <li>We should remember to always let PostgreSQL optimizer do its planning job.</li>
</ul>

<h3 id="order-of-joins">Order of Joins</h3>

<ul>
  <li>We should always perform most restrictive joins first, even for large tables.</li>
</ul>

<h3 id="semi-joins">Semi Joins</h3>

<ul>
  <li>These are joins that satisfy two conditions:
    <ul>
      <li>Only columns from the first table appear in the result set.</li>
      <li>Rows from first table are not duplicated even when there is more than one match in the second table (removes duplicate).</li>
    </ul>
  </li>
  <li>Queries may not be using the <code class="language-plaintext highlighter-rouge">JOIN</code> keyword, but the plan will indicate a semi join.
    <ul>
      <li>e.g. selecting from a table using a column selected from another table. (using <code class="language-plaintext highlighter-rouge">EXISTS</code> or <code class="language-plaintext highlighter-rouge">IN</code> keywords)</li>
      <li>only <code class="language-plaintext highlighter-rouge">EXISTS</code> keyword guarantee a semi join plan.</li>
    </ul>
  </li>
  <li>The optimiser may also choose a regular join, depending on database statistics.</li>
  <li>Semi joins by definition cannot increase the size of result set, and is often the most restrictive join in the plan.</li>
  <li>Depending on filter conditions of query, semi joins may be most restrictive for large range, but index access may become more restrictive at small ranges.</li>
</ul>

<h3 id="anti-join">Anti-Join</h3>

<ul>
  <li>A join that returns all rows from first table with no match in second table.</li>
  <li>Uses operators <code class="language-plaintext highlighter-rouge">NOT EXISTS</code> or <code class="language-plaintext highlighter-rouge">NOT IN</code> to define queries.
    <ul>
      <li>only <code class="language-plaintext highlighter-rouge">NOT EXISTS</code> guarantees an anti-join</li>
      <li>using <code class="language-plaintext highlighter-rouge">OUTER JOIN</code> followed by a filter <code class="language-plaintext highlighter-rouge">NULL</code> may also lead to optimiser re-writing the operation as an anti-join.</li>
    </ul>
  </li>
</ul>

<h3 id="manually-specified-join-order">Manually Specified Join Order</h3>

<ul>
  <li>The manual join order of the query will only be respected when we hit the limit set by server param <code class="language-plaintext highlighter-rouge">join_collapse_limit</code> (default value is 8).</li>
  <li>Within this limit, the optimiser will still create candidate plans and select the best option. Beyond that, the planner will use the specified order.</li>
  <li>The higher the <code class="language-plaintext highlighter-rouge">join_collapse_limit</code> the longer it will take for planner to choose a plan.</li>
  <li>Therefore, setting this param to 1 will always force the join order.
    <ul>
      <li>Another way to force the order is to use common table expressions (CTE).</li>
    </ul>
  </li>
</ul>

<h2 id="grouping">Grouping</h2>

<h3 id="filter-first-group-last">Filter First, Group Last</h3>

<ul>
  <li>A common mistake is to figure out how to perform a grouping calculation (on the entire dataset), creating an inline view, then trying to filter for the data required.</li>
  <li>This forces suboptimal execution sequence.</li>
  <li>Ideally we should allow the data to be filtered as soon as possible as part of the innermost query, if any.</li>
</ul>

<h3 id="group-first-select-last">Group First, Select Last</h3>

<ul>
  <li>Sometimes, it may be more optimal to perform grouping first, if it reduces the size of intermediate results (before joins).</li>
  <li>The inline view can then be used for further grouping in the query.</li>
  <li>I think we need to compare query plans to evaluate if it is worthwhile to rewrite the query for such optimization (it may not be obvious to detect this)</li>
</ul>

<h2 id="reducing-sizecomputation">Reducing Size/Computation</h2>

<h3 id="set-operation">SET Operation</h3>

<ul>
  <li>Using SET operation may prompt the optimiser to select alternate algorithms and may end up with more optimal plans. E.g.
    <ul>
      <li>using <code class="language-plaintext highlighter-rouge">EXCEPT</code> instead of <code class="language-plaintext highlighter-rouge">NOT EXISTS</code> or <code class="language-plaintext highlighter-rouge">NOT IN</code></li>
      <li>using <code class="language-plaintext highlighter-rouge">INTERSECT</code> instead of <code class="language-plaintext highlighter-rouge">EXISTS</code> or <code class="language-plaintext highlighter-rouge">IN</code></li>
      <li>using <code class="language-plaintext highlighter-rouge">UNION</code> instead of complex selection criteria with <code class="language-plaintext highlighter-rouge">OR</code></li>
    </ul>
  </li>
  <li>On top of the potential performance improvement (not guaranteed), it also improves readability of code.</li>
  <li>Note that when using hash joins or SET operations, execution time increase significantly if any datasets cannot fit into the main memory.</li>
</ul>

<h3 id="avoiding-multiple-scans">Avoiding Multiple Scans</h3>

<ul>
  <li>Multiple scans is usually a result of poor schema design. We can only try to write better queries to salvage the imperfect design (if it is beyond our control)</li>
  <li>A common design pattern is to use Entity-Attribute-Value (EAV) to store arbitrary attributes for data that is added to serve new use cases only after a system is live in production. (The design is similar to DynamoDB table with entity-relations modelled in a single table).</li>
</ul>

<p><em>e.g. Other Table</em></p>

<table>
  <thead>
    <tr>
      <th>other_table_user_id</th>
      <th>other_table_user_name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0001</td>
      <td>John</td>
    </tr>
    <tr>
      <td>0002</td>
      <td>Mary</td>
    </tr>
    <tr>
      <td>0003</td>
      <td>William</td>
    </tr>
  </tbody>
</table>

<p>*e.g. EAV**</p>

<table>
  <thead>
    <tr>
      <th>eav_user_id</th>
      <th>eav_attribute_name</th>
      <th>eav_attribute_value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0001</td>
      <td>School</td>
      <td>Engineering</td>
    </tr>
    <tr>
      <td>0002</td>
      <td>School</td>
      <td>Arts</td>
    </tr>
    <tr>
      <td>0003</td>
      <td>School</td>
      <td>Law</td>
    </tr>
    <tr>
      <td>0001</td>
      <td>Year</td>
      <td>4</td>
    </tr>
    <tr>
      <td>0002</td>
      <td>Year</td>
      <td>5</td>
    </tr>
    <tr>
      <td>0003</td>
      <td>Year</td>
      <td>2</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>A query that wants to select the school and the year of each student may join the EAV table with the other table twice, generating two full scans.</li>
  <li>A way to optimise is to ensure that we join the table only once. Use <code class="language-plaintext highlighter-rouge">CASE</code> statements to <code class="language-plaintext highlighter-rouge">SELECT</code> the correct values into the view columns.</li>
</ul>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">user_id</span><span class="p">,</span>
<span class="k">max</span><span class="p">(</span>
	<span class="k">CASE</span> <span class="k">WHEN</span> <span class="n">eav_attribute_name</span> <span class="o">=</span> <span class="s1">'School'</span> <span class="k">THEN</span> <span class="n">eav_attribute_value</span>
	<span class="k">ELSE</span> <span class="k">NULL</span> <span class="k">END</span>
<span class="p">)</span> <span class="k">as</span> <span class="n">school</span><span class="p">,</span>
<span class="k">max</span><span class="p">(</span>
	<span class="k">CASE</span> <span class="k">WHEN</span> <span class="n">eav_attribute_name</span> <span class="o">=</span> <span class="s1">'Year'</span> <span class="k">THEN</span> <span class="n">eav_attribute_value</span>
	<span class="k">ELSE</span> <span class="k">NULL</span> <span class="k">END</span>
<span class="p">)</span> <span class="k">as</span> <span class="nb">year</span>
<span class="p">...</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">user_id</span><span class="p">;</span>
</code></pre></div></div>

<ul>
  <li>Aggregation functions like <code class="language-plaintext highlighter-rouge">max</code> can help to eliminate empty rows from joining the EAV only once.</li>
  <li>As additional optimisation (if applicable), we may filter/group values in the EAV before joining (please refer to the techniques mentioned above).</li>
</ul>

<h2 id="additional-techniques">Additional Techniques</h2>

<h3 id="temporary-tables">Temporary Tables</h3>

<ul>
  <li>Created using the command <code class="language-plaintext highlighter-rouge">CREATE TEMP TABLE table_name AS SELECT ...</code>.</li>
  <li>Will be dropped when session disconnects.</li>
  <li>Potential problems:
    <ul>
      <li>Abused by developers who write each intermediate steps of computation to a temp table.
        <ul>
          <li>Subsequent queries will not be able to use indexes from original source table. (Will need to build need indexes).</li>
          <li>Blocks query rewrites as optimizer do not have control over the entire query (it is forced to break the execution to persist intermediate results in temp tables).</li>
        </ul>
      </li>
      <li>New temp tables do not have statistics to assist the optimizer.</li>
      <li>Temp tables competes for storage resource used by certain operations like joins and groupings.</li>
      <li>Excessive I/O as temp tables are written to disk.</li>
    </ul>
  </li>
</ul>

<h3 id="common-table-expression-cte">Common Table Expression (CTE)</h3>

<ul>
  <li>Can be thought of as temp table for a single query.</li>
  <li>Each auxiliary statement (the CTE) in a <code class="language-plaintext highlighter-rouge">WITH</code> clause can be attached to a primary statement.</li>
  <li>Before PostgreSQL 12, CTEs are executed as temp table, materialised in main memory with disk failover. Therefore, there is no performance improvements over temp tables.</li>
  <li>The purpose of CTEs are to allow reuse of a same sub-query more than once in a statement.
    <ul>
      <li>This creates an optimisation fence, where CTEs will be planned separately by the optimiser.</li>
    </ul>
  </li>
  <li>From PostgreSQL 12 onwards, if CTE is used only once in a <code class="language-plaintext highlighter-rouge">SELECT</code> statement with no recursion, the optimisation fence will be removed. The CTE will be rewritten and inlined as part of the outer query.
    <ul>
      <li>This new feature can be ignored using the <code class="language-plaintext highlighter-rouge">MATERIALIZED</code> keyword. eg. <code class="language-plaintext highlighter-rouge">WITH cte AS MATERIALIZED ...</code>.</li>
    </ul>
  </li>
  <li>Therefore, CTEs can be used to improve readability, as long as it does not create unintended temp tables.</li>
</ul>

<h3 id="views">Views</h3>

<ul>
  <li>A view is a database object that stores a query that defines a virtual table.</li>
  <li>When selecting on top of a view, the original query stored in the view will be inlined as sub-queries by the optimiser.</li>
  <li>Potential problems of views:
    <ul>
      <li>May result in sub-optimal execution if the optimiser is unable to rewrite the statement, and the use of the view accidentally forced a sub-optimal execution.</li>
      <li>The view abstracts the details of the underlying database objects (good for usability but…)</li>
      <li>It may be used by developers to make inefficient queries without knowing the details. E.g. querying without the help of indexes on a view, when the required indexes do exist on the underlying tables.</li>
      <li>PostgreSQL internally creates rules, triggers, and automatic updates to make views behave like tables, making it an extremely sophisticated object.</li>
    </ul>
  </li>
  <li><strong>Views do not provide any performance benefits!</strong></li>
  <li>The best use case for views would be to act as a security layer, or to define a selection properly for reporting purposes.</li>
</ul>

<h3 id="materialized-views">Materialized Views</h3>

<ul>
  <li>A materialised view is a database object that combines both a query definition and a table to store the results of the query at the time it is run.</li>
  <li>Subsequent reading of the view will show stale data (from the table).</li>
  <li>There is no way to update the data, besides using <code class="language-plaintext highlighter-rouge">REFRESH</code> command to run the predefined query in the view.
    <ul>
      <li>Based on current PostgreSQL implementation, during a refresh, the table will be truncated, and all data reinserted.</li>
      <li>On error, the refresh will roll back to the last version.</li>
      <li>If refreshed with the <code class="language-plaintext highlighter-rouge">CONCURRENTLY</code> keyword, the view will not be locked (available for read during refresh). But the view must have a unique index.</li>
    </ul>
  </li>
  <li>Indexes can be created on materialised views.</li>
  <li>Materialised views cannot have primary/foreign keys.</li>
  <li>Material views may be good for the following use cases:
    <ul>
      <li>data that seldom changes</li>
      <li>it is not critical to have live data</li>
      <li>it will be used by many different queries, and many reads between refreshes.</li>
    </ul>
  </li>
</ul>

<h3 id="dependencies">Dependencies</h3>

<ul>
  <li>Creating a view/materialised view creates a dependency on the underlying tables (or underlying materialised views).</li>
  <li><code class="language-plaintext highlighter-rouge">ALTER</code> or <code class="language-plaintext highlighter-rouge">DROP</code> on the underlying database objects are not permitted unless using <code class="language-plaintext highlighter-rouge">CASCADE</code>.</li>
  <li>Even a simple modification on an underlying table, e.g. adding a column, that does not affect the views, will require the view to be dropped and rebuilt.</li>
  <li>This is the unfortunate limitation of PostgreSQL.</li>
  <li>If a web of dependencies has been created, altering a table may be extremely slow.</li>
</ul>

<h3 id="partitioning">Partitioning</h3>

<ul>
  <li>Partitioning is relatively new to PostgreSQL, starting from version 10.</li>
  <li>Range partitioning = all rows in a partition has attribute values within a certain range.</li>
  <li>Adding/Dropping a partition can be significantly faster than working on a single table with massive data.</li>
  <li>Partitioning may improve query performance.
    <ul>
      <li>Partitions may be distributed across different servers.</li>
      <li>Certain scans may only need to access a single partition.</li>
      <li>Indexes may also be built on the partition (within the partition), but will only benefit if query do not require multiple partition access.</li>
    </ul>
  </li>
</ul>

<h3 id="parallelism">Parallelism</h3>

<ul>
  <li>Parallel execution is relatively new to PostgreSQL, starting from version 10.</li>
  <li>Good for massive scans and hash joins where the work can be split up into multiple unit.</li>
  <li>Sometimes, optimiser may replace sequential execution (index-based) with parallel table scan, due to imprecise database statistics, which may perform worse than sequential execution.</li>
  <li>Use server settings to ensure that optimiser will consider parallel algorithms, and let the optimiser do its job.</li>
  <li>Parallel execution is often view as a silver bullet, but it cannot fix poor design.</li>
</ul>

<h1 id="optimise-data-modification">Optimise Data Modification</h1>

<ul>
  <li>Usually consists of optimising two parts
    <ul>
      <li>Optimising the selection of data to be updated (use strategy described above)</li>
      <li>Optimising the data update itself (use strategy in this new section)</li>
    </ul>
  </li>
</ul>

<h2 id="understanding-data-modification-concepts">Understanding Data Modification Concepts</h2>

<h3 id="low-level-inputoutput">Low-Level Input/Output</h3>

<ul>
  <li>Read operations requires all required database blocks to be fetched from disk to main memory to complete.</li>
  <li>Write operations however, do not require actual modified database blocks to be written to disk to complete.
    <ul>
      <li>This can happen in the background. Operation is deemed complete once changes are registered in main memory.</li>
      <li>So it will appear fast to users, but it still takes up system resources.</li>
      <li>Heavy load of data modification operations will also slow down incoming modification operations due to concurrency control.</li>
    </ul>
  </li>
</ul>

<h3 id="concurrency-control">Concurrency Control</h3>

<ul>
  <li>Lock waiting (transactions trying to update the same data) can slow down operations.</li>
  <li>Flushing Write-Ahead Logs (WAL) too frequently leads to disk I/O, slows down performance.
    <ul>
      <li>This happens when we do not use transaction control (by default every DML will be one transaction and gets committed immediately, therefore WAL gets flushed)</li>
    </ul>
  </li>
  <li>For PostgreSQL, new updated row/deleted row are not overwritten.
    <ul>
      <li>They are stored as dead row while a new row take its place.</li>
      <li>Dead rows are cleaned out by <code class="language-plaintext highlighter-rouge">VACUUM</code> operation.</li>
    </ul>
  </li>
  <li>PostgreSQL uses Multi-version Concurrency Control (MVCC), Snapshot Isolation (SI) model.
    <ul>
      <li>Transactions always read latest committed rows.</li>
      <li>Concurrent write to same row is not allowed and only one transaction will hold the lock.</li>
      <li>Lock is released if transaction aborts.</li>
      <li>If transaction commits, then behavior depends on the configured isolation level in the transaction.
        <ul>
          <li><code class="language-plaintext highlighter-rouge">READ COMMITTED</code> is the default, waiting transaction will read the new committed data and proceed.</li>
          <li><code class="language-plaintext highlighter-rouge">REPEATABLE READ</code>, the waiting transaction will be aborted since initially read data differs from current data.</li>
          <li>There are other levels not covered.</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="data-modification-and-indexes">Data Modification and Indexes</h2>

<ul>
  <li>Rule of thumb, one extra index increase data update/delete time by approximately 1%.</li>
  <li><code class="language-plaintext highlighter-rouge">CREATE INDEX</code> locks the entire while <code class="language-plaintext highlighter-rouge">CREATE INDEX CONCURRENTLY</code> does not, but takes longer.</li>
  <li>PostgreSQL uses the Heap-Only Tuples (HOT) technique to reduce index updates when new row is inserted.
    <ul>
      <li>if the same block has sufficient free space for new row data and</li>
      <li>no index columns need modification</li>
      <li>then no indexes will be modified.</li>
    </ul>
  </li>
</ul>

<h2 id="mass-updates">Mass Updates</h2>

<ul>
  <li>if massive updates create a lot of dead rows, that means blocks are not used efficiently.</li>
  <li>operations will read a lot of blocks, incurring disk I/O.</li>
  <li>however, PostgreSQL auto-vacuuming should still be good enough. Engineers simply need to tune the configurations appropriately.</li>
</ul>

<h2 id="frequent-updates">Frequent Updates</h2>

<ul>
  <li>very frequent updates also results in dead rows and therefore inefficient block usage.</li>
  <li>if auto-vacuum cannot keep up with the frequency, we can also tune the <code class="language-plaintext highlighter-rouge">fillfactor</code> storage parameter.
    <ul>
      <li>low <code class="language-plaintext highlighter-rouge">fillfactor</code> means a new block will only utilise a small amount of space.</li>
      <li>the free space will be reserved for update operations.</li>
      <li>since the block has free space to store new versioned rows, indexes do not need to be updated.</li>
      <li>however, the tradeoff is operations will need to fetch many blocks inefficiently (when updates didn’t happen).</li>
    </ul>
  </li>
</ul>

<h2 id="referential-integrity-and-triggers">Referential Integrity and Triggers</h2>

<ul>
  <li>Foreign key constraints will slow down inserts (on child table) due to additional checks.</li>
  <li>Foreign key constraints will also slow down updates and deletes (on parent table).</li>
  <li>Constraint checks are implemented internally as system triggers.</li>
  <li>Therefore, triggers also slow down performance.</li>
  <li>However, it also depends on situation and size of data.</li>
  <li>This also does not mean we should avoid constraints and triggers (they are important and valuable).</li>
</ul>

<h1 id="design">Design</h1>

<h2 id="alternatives-to-rdbms">Alternatives to RDBMS</h2>

<p>It is hard to replace RDBMS because fundamentally the query language (SQL) is based on boolean logic and does not specify the way to store data. Eventually all other types of databases chose to support SQL as one of their query language option.</p>

<h3 id="entity-attribute-value-eav">Entity-Attribute-Value (EAV)</h3>

<ul>
  <li>Covered in earlier section, the EAV contains 3 columns (entity id, attribute id, value).</li>
  <li>It is popular and offers flexibility when requirements are unclear.</li>
  <li>Performance is still slow compare to traditional relational models.</li>
  <li>Cannot easily enforce referential integrity, or data validation constraints.</li>
</ul>

<h3 id="key-value-model">Key-Value Model</h3>

<ul>
  <li>A single primary key, with other attributes stored as a complex object. (like DynamoDB).</li>
  <li>Limits the database engine from performing more complex operations on other attributes, without retrieving the full object.</li>
  <li>PostgreSQL new <code class="language-plaintext highlighter-rouge">JSONB</code> support provides a similar experience.</li>
  <li>Again like EAV it is hard to enforce referential integrity, or data validation constraints.</li>
</ul>

<h3 id="hierarchical-model">Hierarchical Model</h3>

<ul>
  <li>Typical document stores. (like MongoDB).</li>
  <li>Popular because it is intuitive to understand.</li>
  <li>Works great when data can be modelled in a single hierarchy.</li>
  <li>Gets more complex when data fits multiple hierarchy.</li>
</ul>

<h3 id="combining-the-best-of-all-worlds">Combining the best of all worlds</h3>

<ul>
  <li>PostgreSQL provides features (such as <code class="language-plaintext highlighter-rouge">JSONB</code> support) that allows us to gain the benefit of all the above databases without using different technologies.</li>
  <li>PostgreSQL also provide Foreign Data Wrappers (FDW) to abstract the access to other databases (DBMS and more) transparently.</li>
</ul>

<h2 id="flexibility-vs-efficiency--correctness-tradeoff">Flexibility vs Efficiency &amp; Correctness tradeoff</h2>

<ul>
  <li>Storing values as text, regardless of type, will sacrifice correctness.</li>
  <li>It will also be difficult to perform type-specific comparisons (e.g. date comparison) when data are stored as text.
    <ul>
      <li>And as a result, indexes will also not work so well for inequality searches.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">JSONB</code> best serves use cases that requires the retrieval of the entire object, instead of attributes within objects.</li>
</ul>

<h2 id="normalisation">Normalisation</h2>

<ul>
  <li>Normalisation is typically misunderstood and used/not used properly.</li>
  <li>In a nutshell, it is used to decompose data into multiple tables, depending only on the same primary key, to reduce repetition.</li>
  <li>Usually if Entity-Relation (ER) models are designed properly, the schemas are naturally normalized.</li>
  <li>But the importance of normalisation again depends on situation.
    <ul>
      <li><strong>The primary purpose should not be performance optimisation</strong>.</li>
      <li>Normalisation provides cleaner structure, supports referential integrity constraints.</li>
    </ul>
  </li>
  <li><strong>Ideally, a clean logical structure should be provided for the application based on a storage structure optimized for performance.</strong></li>
</ul>

<h2 id="surrogate-keys">Surrogate Keys</h2>

<ul>
  <li>Unique values generated to identify objects. (In PostgreSQL they are values selected from <code class="language-plaintext highlighter-rouge">sequence</code>).</li>
  <li>Defining a column as <code class="language-plaintext highlighter-rouge">serial</code> type naturally obtains the next generated value when a row is inserted.</li>
  <li>It is not necessary to have a blanket policy to create surrogate keys on every table.
    <ul>
      <li>It is detrimental if the key has no bearing to real world relations.</li>
      <li>It is even worst if the table also stores a unique identifier from the real world relations.</li>
    </ul>
  </li>
  <li>In some cases, it is useful to have an internal identifier, when the table have data with the following characteristics:
    <ul>
      <li>the real world identifier gets repeated after a short period of time and therefore is not unique.</li>
      <li>data defined by multiple source systems with different external identifier conventions.</li>
      <li>in the future, the data may lose their identifier or update their identifier due to system changes.</li>
      <li>data needs to be joined with other tables that may be extremely large using existing identifiers, and may be optimized by surrogate keys.</li>
    </ul>
  </li>
</ul>

<h1 id="app-development-and-performance">App Development and Performance</h1>

<p>This section focuses on optimising processes instead of queries. This aspect is often neglected but can bring about huge impact.</p>

<ul>
  <li>Response time is a huge non-functional requirement for all systems, regardless of industry and use cases.</li>
  <li>Application and Database can both be working perfectly (in terms of their own metrics)</li>
  <li>and yet, the interactions between the two systems slows down overall response time for the end user (which is the more important metric from the user’s perspective)</li>
</ul>

<h2 id="impedance-mismatch">Impedance Mismatch</h2>

<ul>
  <li>The power of the expressiveness and efficiency of database query languages (declarative) does not match the strengths of imperative programming languages.</li>
  <li>Even though both can have great strength, they might deliver less power than expected.</li>
</ul>

<h2 id="interface-between-application-and-database">Interface between Application and Database</h2>

<ul>
  <li>JDBC and ODBC are generic interfaces that simply cannot offer the full power of the database to the application.</li>
  <li>ORMs are worse.
    <ul>
      <li>They abstract all details from the application.</li>
      <li>ORMs often end up with N+1 queries because as a generic tool it cannot satisfy all use cases.</li>
    </ul>
  </li>
  <li>Trying to abstract storage/persistence implementation details from the business logic in a layered approached often results in some sacrifice.
    <ul>
      <li>objects have no knowledge of each other, resulting in inefficient query (kind of like N+1 queries problem) at the service layer to bring different objects together.</li>
    </ul>
  </li>
</ul>

<h2 id="a-better-approach">A Better Approach</h2>

<p>The above can be summarised as two closely related problems:</p>

<ol>
  <li>Inability to transfer all the data at once to the application (to think and operate in sets).</li>
  <li>Inability to transfer complex objects without disassembling them before the data transfer to/from the application.</li>
</ol>

<p>In theory PostgreSQL should be able solve the above by using a single query to return a set because:</p>

<ul>
  <li>it is an object-relational database (<code class="language-plaintext highlighter-rouge">JSONB</code> support)</li>
  <li>it allows the creation of custom types</li>
  <li>it has functions that can return sets, including sets of records</li>
</ul>

<h1 id="functions">Functions</h1>

<h2 id="user-defined-function">User-Defined Function</h2>

<ul>
  <li>Besides internal functions, we can create custom functions using:
    <ul>
      <li>SQL</li>
      <li>C</li>
      <li>Procedural Language (PL), which includes by default:
        <ul>
          <li>PL/pgSQL</li>
          <li>PL/Tcl</li>
          <li>PL/Perl</li>
          <li>PL/Python</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>The database engine only captures the following metadata about the function:
    <ul>
      <li>name</li>
      <li>list of params</li>
      <li>return type</li>
      <li>language of the function code</li>
    </ul>
  </li>
  <li>The function body itself is stored as string literal, and will be passed to language-specific special handlers to parse/execute at runtime.</li>
  <li>PostgreSQL didn’t support stored procedures in the past. Some outdated guide may recommend packaging multiple statements together using a function that returns void to emulate a stored procedure.</li>
  <li>PostgreSQL supports function overloading (polymorphic behavior).</li>
  <li>Supports exception handling using <code class="language-plaintext highlighter-rouge">EXCEPTION</code> and <code class="language-plaintext highlighter-rouge">WHEN</code> keywords. This prevents query from failing in a <code class="language-plaintext highlighter-rouge">SELECT</code> statement if the function can return a fallback value when it cannot process the input.</li>
  <li>Function can also work with DML to insert/update/delete data.</li>
  <li>Please refer to official documentation for full function definition syntax.</li>
</ul>

<h2 id="function-execution-internals-postgresql">Function Execution Internals (PostgreSQL)</h2>

<ul>
  <li>Functions are not compiled. The source code will be stored. Therefore, PostgreSQL is unable to detect syntax errors like wrong column name.</li>
  <li>Function code will be interpreted when called.
    <ul>
      <li>an instruction tree will be produced the first time function is called within a session.</li>
      <li>only when the execution path reaches any specific commands in this function’s instruction trees, then an actual SQL statement will be prepared.</li>
      <li>in other words, if the function contains conditional logic, and one of the conditional branches has syntax error, it may take a long time to detect if that branch was never executed.</li>
    </ul>
  </li>
  <li>Transactions cannot be initiated inside a function.</li>
  <li>Functions also do not generate execution plan, so the optimiser will not know the cost of a function (unless the function was defined using <code class="language-plaintext highlighter-rouge">COST</code> keyword and the engineer manually supplied an arbitrary value. This is quite dangerous if you do not know what you are doing).</li>
</ul>

<h2 id="functions-performance">Functions Performance</h2>

<ul>
  <li>Performance can be bad because to the optimiser, the function is a blackbox with no database statistics to help improve the plan.</li>
  <li>The function code may introduce inefficiencies to a query.</li>
  <li>Perhaps the best use of function is not to improve a single query performance, but to improve overall process.</li>
</ul>

<h2 id="user-defined-types">User Defined Types</h2>

<ul>
  <li>Can be <code class="language-plaintext highlighter-rouge">DOMAIN</code>, <code class="language-plaintext highlighter-rouge">ENUM</code>, <code class="language-plaintext highlighter-rouge">RANGE</code></li>
  <li>Can even be a composite type which represents a record
    <ul>
      <li>the definition consists of attribute names and data type of each attribute</li>
    </ul>
  </li>
  <li>Functions can return sets of composite types.</li>
  <li>Composite types can contain elements which are defined as other composite types.
    <ul>
      <li>this allows us to represent complex nested objects in our results.</li>
      <li>unfortunately, the details about the structure (nested attribute names and data types) will not be revealed in the result, which limits the use of this approach as a solution to nested object.</li>
    </ul>
  </li>
  <li>Functions that depends on user defined types must first be dropped and later recreated, if the types need to be modified.</li>
</ul>

<h2 id="function-security">Function Security</h2>

<ul>
  <li>Supplying the <code class="language-plaintext highlighter-rouge">SECURITY</code> parameter will change the access control behaviour of the function.</li>
  <li><code class="language-plaintext highlighter-rouge">SECURITY INVOKER</code> is the default. Function will run with permissions of the user calling it. User must have access to underlying database objects used in the function.</li>
  <li><code class="language-plaintext highlighter-rouge">SECURITY DEFINER</code> will use the function creator’s permission instead, even if the function caller has no permission.</li>
  <li>access to all functions are granted to <code class="language-plaintext highlighter-rouge">PUBLIC</code> by default (so everyone can be a function user).</li>
</ul>

<h2 id="business-logic">Business Logic</h2>

<ul>
  <li>When deciding what can go to the database and what has to stay in the application, <strong>a decisive factor is whether bringing the dependencies into the database would improve performance (facilitate joins or enable the use of indexes).</strong></li>
  <li>If so, the logic is moved into a function and considered “database logic”; otherwise, data is returned to the application for further processing of business logic.</li>
</ul>

<h2 id="functions-in-olap">Functions in OLAP</h2>

<ul>
  <li>Can be used to parameterise a view (by selecting records using functions)</li>
  <li>Provides an abstraction from underlying view/table</li>
</ul>

<h2 id="stored-procedures">Stored Procedures</h2>

<ul>
  <li>They are like functions that do not return any values.</li>
  <li>They also allow transactions to commit and rollback within the procedure body (functions do not allow this)</li>
</ul>

<h2 id="exception-handling">Exception Handling</h2>

<ul>
  <li>Exceptions can be handled by separate inner blocks of the function</li>
</ul>

<h2 id="dynamic-sql">Dynamic SQL</h2>

<ul>
  <li>Generating SQL code dynamically, to be executed by the database engine.</li>
  <li>Advantage specific to Postgres:
    <ul>
      <li>Postgres optimise execution for specific values, and at the last step of planning.</li>
      <li>Using dynamic SQL ensures that your queries are always optimised.</li>
    </ul>
  </li>
  <li>General approach is to generate dynamic SQL code (in text) within a function, and run it.
    <ul>
      <li>It is able to provide consistent performance, because the optimisation do not depend on database session cache.</li>
      <li>If we save the SQL code as a function instead, performance will be affected by session cache, and the cache may currently contain a sub-optimal plan.</li>
      <li>However, dynamic SQL is harder to debug and develop.</li>
    </ul>
  </li>
  <li>No worries about SQL injection, if input params from users are sanitised, and SQL code are generated from database functions (not from users directly).</li>
</ul>

<h3 id="flexibles-joins">Flexibles JOINS</h3>

<ul>
  <li>Since SQL code are constructed on the fly, certain JOINS can be omitted depending on input params.</li>
  <li>It can also force the optimiser to execute index joins over hash joins.</li>
  <li>This provides significant performance improvement.</li>
</ul>

<h1 id="avoiding-orms">Avoiding ORMs</h1>

<p>This is a golden quote from the book:</p>

<blockquote>
  <p>[!quote]
Often, new development methodologies require application developers to make significant changes to the development process, which inevitably leads to lower productivity. It is not unusual for potential performance gains to fail to justify the increase in development time. After all, developer time is the most expensive resource in any project.</p>
</blockquote>

<ul>
  <li>This is applicable to the entire software engineering industry in general.</li>
  <li>It is often not worth the effort to migrate to the next new, shiny, and trendy things on a whim, derailing the entire product development roadmap.</li>
</ul>

<h2 id="norm-as-an-alternative">NORM as an alternative</h2>

<ul>
  <li>An example framework is provided by the authors on <a href="https://github.com/hettie-d/NORM">GitHub</a></li>
  <li>Contract driven approach to develop the interface between database models and application models.</li>
  <li>ORM are generic tools, and therefore always causes N+1 query problem.</li>
  <li>Having customised and specific database functions to return result sets that can be serialized, and then deserialized by the application to JSON objects.
    <ul>
      <li>Can be optimised to run only one query in the database.</li>
      <li>Combining with dynamic SQL provides a powerful way to run complex queries (with CASE WHEN to selectively JOIN and search for values depending on input params).</li>
      <li>Application development only need to rely on the interface, that promises the return of deserialised JSON objects.</li>
    </ul>
  </li>
  <li>This approach does not store data directly as JSON because it will lead to duplicated data (typical of NoSQL document store).</li>
</ul>

<h1 id="complex-filtering-and-search">Complex Filtering and Search</h1>

<p>This chapter covers use cases that cannot be efficiently supported by B-trees.</p>

<h2 id="full-text-search">Full Text Search</h2>

<ul>
  <li>The search model used by Postgres is a simple Boolean model (internet search engines use more complex models).</li>
  <li>A document is a list of terms.
    <ul>
      <li>Words with the same meaning are mapped to the same term, using linguistic tools.</li>
      <li>Linguistic rules are defined in a configuration in Postgres, but is language dependent.</li>
      <li>Trigrams, converting text into a set of 3-character sequences, is a language independent processing.</li>
    </ul>
  </li>
  <li>Result of text processing is of <code class="language-plaintext highlighter-rouge">ts_vector</code> type (which is a list of terms).</li>
  <li>A query is represented as <code class="language-plaintext highlighter-rouge">ts_query</code> type (also a list of terms, with logical AND, OR, NOT connectors).</li>
  <li>Logical match will be performed, and the result is Boolean. The <code class="language-plaintext highlighter-rouge">ts_vector</code> either match the <code class="language-plaintext highlighter-rouge">ts_query</code> or not.</li>
  <li>Full text search in Postgres can work without indexes, but can also be optimised with special indexes. Use the <code class="language-plaintext highlighter-rouge">@@</code> command in the WHERE clause to query.</li>
</ul>

<h2 id="multidimensionalspatial-search">Multidimensional/Spatial Search</h2>

<ul>
  <li>Default multi-column indexes has search priority based on the order of columns, which cannot work for multidimensional data that requires all dimensions to be treated symmetrically.</li>
  <li>The default also cannot support range queries and nearest-neighbour queries for spatial data.</li>
  <li>There are specialised indexes in Postgres for these use cases.</li>
</ul>

<h2 id="generalised-index-types">Generalised Index Types</h2>

<ul>
  <li>Indexes created by B-tree by default, but we can specify the following specialised types: hash, GIST, spgist, GIN, and BRIN.</li>
</ul>

<h3 id="gist-indexes">GIST Indexes</h3>

<ul>
  <li>This is a family of index structures, specialised for different multidimensional data types.</li>
  <li>e.g. data can be represented as multidimensional point, and query is a multidimensional rectangle over the space.</li>
</ul>

<h3 id="indexes-for-full-text-search">Indexes for Full Text Search</h3>

<ul>
  <li>GIN (Generalised Inverted) can be used.
    <ul>
      <li>Document has a list of terms after processing (ts_vector)</li>
      <li>This index is created from each term in the list, and map the term to a list of documents containing the same term (hence, inverted)</li>
      <li>A search using this index can quickly find a relevant documents.</li>
      <li>GIN can be a functional index, that either persist the <code class="language-plaintext highlighter-rouge">ts_vector</code> of the document or not. If persisted, the search can be done without language specific configurations.</li>
      <li>GIN can also work for arrays of values, multivalued attributes.</li>
    </ul>
  </li>
  <li>GIST can also be used, but data will be indexed as bitmaps.
    <ul>
      <li>Each term in the document will be hashed, and the hash value is represented as a bit in the map.</li>
      <li>A bitmap of a document will indicate existence of all the terms (may have hash conflicts which is fine since it is an approximate result, and Postgres will recheck the  <code class="language-plaintext highlighter-rouge">ts_vector</code> before returning).</li>
      <li>Query will also be converted to bitmap.</li>
      <li>The search performs a simple logical matching of bitmaps.</li>
      <li>But if documents have a lot of terms, hash conflict will be high, and GIST will become less effective.</li>
    </ul>
  </li>
</ul>

<h3 id="brin-for-very-large-tables">BRIN for Very Large Tables</h3>

<ul>
  <li>Other databases support clustered indexes (data stored in the same order in the block as the index).
    <ul>
      <li>Clustered indexes are usually sparse indexes (index only need to store the first row in a block, and the block can be scanned)</li>
    </ul>
  </li>
  <li>Sparse index only stores a fraction of data of the column, but can still work effectively.</li>
  <li>Dense indexes need to store all data in the column it has indexed.</li>
  <li>Postgres do not support clustered indexes because it does not allow users to control how data is ordered in the block.
    <ul>
      <li>However, if the data in the tables are append-only, and always appended in order.</li>
      <li>And if we are interested in indexing their appended order e.g. the timestamp.</li>
      <li>We can use BRIN (Block Range Index)</li>
    </ul>
  </li>
  <li>BRIN works similar to how the typical clustered index works as described above.
    <ul>
      <li>However, it becomes ineffective if data appears in multiple blocks and gets appended out of the order.</li>
      <li>BRIN index a summary of the columns within a block range.</li>
      <li>The summary can be e.g. the min-max value of timestamp of rows within the block, or a multidimensional rectangle bounding the records of spatial data.</li>
      <li>Summarisation is expensive, so we can configure it to run on trigger (for small load), delay it to run with vacuum, or run it manually.</li>
    </ul>
  </li>
</ul>

<h3 id="indexing-json-and-jsonb">Indexing JSON and JSONB</h3>

<ul>
  <li>JSON type is stored as string, while JSONB is stored as binary.</li>
  <li>JSONB allows more performant indexes to be built and can satisfy more complex search use cases.</li>
  <li>Special indexes mentioned above can be built to search JSONB. However,
    <ul>
      <li>Performance will still not be as good as the usual B-tree indexes.</li>
      <li>Certain indexes like GIN do not support certain attribute values (like datetime) or complex searches. You may need to further enhance the data by persisting search terms alongside the record to specially facilitate full text search.</li>
      <li>Data in JSONB are denormalized and duplicated and may be out of date especially for foreign key relations, and will need to be refreshed periodically (perhaps through triggers).</li>
    </ul>
  </li>
</ul>

<h1 id="the-ultimate-optimization-algorithm">The Ultimate Optimization Algorithm</h1>

<p>![[image-postgresql-decision-steps.png]]</p>

<figure class=""><img src="/assets/images/image-postgresql-decision-steps.png" alt="" /><figcaption>
      Decision tree for optimisation

    </figcaption></figure>

<h2 id="other-tips">Other Tips</h2>

<ul>
  <li>introduce joins one table at a time</li>
  <li>observe the execution plan to verify if execution is ideal</li>
</ul>

<h2 id="other-considerations">Other Considerations</h2>

<ul>
  <li>For parameterised queries, different parameter values may change the most restrictive criteria of the query</li>
  <li>Dynamic SQL may also change the most restrictive criteria</li>
  <li>Functions may degrade performance but it is needed for dynamic SQL</li>
  <li>Changing database design instead of optimising the queries, if applicable, is also a good solution</li>
</ul>]]></content><author><name>wy</name></author><category term="notes" /><category term="postgresql" /><category term="databases" /><summary type="html"><![CDATA[[!info] title: PostgreSQL Query Optimization: The ultimate guide to building efficient queries author: Dombrovskaya H., Novikov B. and Bailliekova A. published: 2021 edition: 1 ISBN: 978-1484268841]]></summary></entry></feed>