<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rahul Kumar</title>
    <description>The latest articles on DEV Community by Rahul Kumar (@coderahul1).</description>
    <link>https://dev.to/coderahul1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4157616%2Fd07c9ea9-a025-45bd-ad44-8a499b776401.jpg</url>
      <title>DEV Community: Rahul Kumar</title>
      <link>https://dev.to/coderahul1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coderahul1"/>
    <language>en</language>
    <item>
      <title>How does a server understand "hii"? The underneath mechanism of HTTP, WebSockets and WebRTC</title>
      <dc:creator>Rahul Kumar</dc:creator>
      <pubDate>Tue, 06 Oct 2026 19:03:49 +0000</pubDate>
      <link>https://dev.to/coderahul1/how-does-a-server-understand-hii-the-underneath-mechanism-of-http-websockets-and-webrtc-32ik</link>
      <guid>https://dev.to/coderahul1/how-does-a-server-understand-hii-the-underneath-mechanism-of-http-websockets-and-webrtc-32ik</guid>
      <description>&lt;h1&gt;
  
  
  Why HTTP, WebSockets and WebRTC?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;a simple walk from how people talk to how machines talk&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with how we talk in real life
&lt;/h2&gt;

&lt;p&gt;Two people talking need three things. First, a &lt;strong&gt;medium&lt;/strong&gt; that carries the voice (air, or a phone line). Second, an &lt;strong&gt;address&lt;/strong&gt; to reach the right person (their phone number). Third, a &lt;strong&gt;common language&lt;/strong&gt;, so the words mean something.&lt;/p&gt;

&lt;p&gt;Take a phone call. We dial a number, the call goes through towers and cables, and the friend picks up. The medium and the address are working perfectly. But if we say "kaise ho?" and the friend doesn't know Hindi, nothing happens. The voice reached him, but he can't understand it. If we say "how r u?", he understands and replies "I'm good".&lt;/p&gt;

&lt;p&gt;The internet has exactly the same split, and almost everything in this topic comes from it. Some protocols &lt;strong&gt;carry&lt;/strong&gt; the data (medium and address), and some protocols give the data &lt;strong&gt;meaning&lt;/strong&gt; (language).&lt;/p&gt;

&lt;h2&gt;
  
  
  The carrying part: IP, TCP and UDP
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Physical layer.&lt;/strong&gt; Cables, wifi and 4G/5G towers turn bits into signals (radio waves, light, voltage) and move them. They don't care what the bits mean, just like the tower doesn't care what language we speak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IP (IPv4 and IPv6).&lt;/strong&gt; This is the phone number. Every machine gets an address like &lt;code&gt;142.250.77.46&lt;/code&gt; (IPv4). IPv6 is the longer version, created because IPv4 addresses were running out. IP only delivers on a best-effort basis. A packet can get lost, duplicated, or arrive out of order, and IP won't tell anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ports.&lt;/strong&gt; The IP address finds the machine, but one machine runs many apps. A port finds the right app (80 for HTTP, 443 for HTTPS), like an extension number after the main phone number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TCP and UDP.&lt;/strong&gt; Both sit on top of IP and use ports. They differ in how they deliver.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TCP&lt;/strong&gt; is like a proper phone call. First we confirm the other side is there (the 3-way handshake: SYN, SYN-ACK, ACK). Then every chunk is numbered, the receiver confirms each one (ACK), anything lost is sent again, and everything is handed over in the right order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UDP&lt;/strong&gt; is like shouting a message across the street. No call setup, no confirmation, no re-sending. Some words may not arrive, but it is fast and light.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important limit is this: &lt;strong&gt;TCP gives only a stream of bytes.&lt;/strong&gt; It has no idea where one message ends and the next begins, or whether the bytes are a name, a photo, or &lt;code&gt;hii&lt;/code&gt;. Just like the phone call, the connection is working, but the meaning is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The language part: why HTTP?
&lt;/h2&gt;

&lt;p&gt;When we send &lt;code&gt;hii&lt;/code&gt; to a server, the server receives bits. A server is just a program, and it can't "understand" anything by itself. It can only read data in a format it was written to expect, and both sides must agree on that format beforehand. That agreement is a protocol, and for the web it is &lt;strong&gt;HTTP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;HTTP is a text format. A request looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/myprofile&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp.com&lt;/span&gt;
&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bearer &amp;lt;token&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line has three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; what we want to do. &lt;code&gt;GET&lt;/code&gt; is "give me something", &lt;code&gt;POST&lt;/code&gt; is "create something" (like sending an email and password to make an account), &lt;code&gt;PUT&lt;/code&gt;/&lt;code&gt;PATCH&lt;/code&gt; update, and &lt;code&gt;DELETE&lt;/code&gt; removes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path:&lt;/strong&gt; what we want it done to (&lt;code&gt;/myprofile&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; which dialect of HTTP we speak.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The headers add details such as who we are and what formats we accept. The server reads this, knows "ohh, I have to send the profile", runs its code, and replies in the same agreed format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rahul"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;200&lt;/code&gt; is a status code that says how it went. &lt;code&gt;404&lt;/code&gt; means not found, &lt;code&gt;401&lt;/code&gt; means not logged in, and &lt;code&gt;500&lt;/code&gt; means the server crashed.&lt;/p&gt;

&lt;p&gt;That is the whole answer to "why HTTP": TCP made the call, and HTTP is the language that tells the server what the client wants. This is the same as "how r u" telling the friend what to answer. Two details are worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP is &lt;strong&gt;stateless&lt;/strong&gt;. Each request stands alone, and the server doesn't remember the previous one by itself. That is why cookies, sessions and tokens exist.&lt;/li&gt;
&lt;li&gt;HTTP/1.1 and HTTP/2 run on TCP. HTTP/3 runs on QUIC, which is built on UDP with reliability added on top. So "HTTP means TCP" is the common case, not a law.&lt;/li&gt;
&lt;li&gt;HTTPS is the same HTTP wrapped in TLS, which encrypts it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  HTTP only answers, it never starts the talk
&lt;/h2&gt;

&lt;p&gt;HTTP follows the &lt;strong&gt;client-server&lt;/strong&gt; model with a strict rule: &lt;strong&gt;request first, response after&lt;/strong&gt;. &lt;code&gt;GET /myprofile&lt;/code&gt; makes the server send the profile. &lt;code&gt;POST&lt;/code&gt; with an email and password makes the server create the account. The server never does anything on its own. It can't say "hey, new message!" unless a request is there to answer.&lt;/p&gt;

&lt;p&gt;For loading a profile or checking a balance, this is perfect. But what about a chat? We asked a friend something, and now we want to know whether he has replied. The server won't tell us, so we have to ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  Polling: asking again and again
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short polling&lt;/strong&gt; means sending a request every fixed interval, say every 2 seconds, with the question "any new message?". Each time, the server queries the database and replies, and most of the time the answer is "nothing". Why this hurts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each request carries full HTTP headers in both directions, often hundreds of bytes with cookies and tokens.&lt;/li&gt;
&lt;li&gt;Each one makes the server run its code and hit the database.&lt;/li&gt;
&lt;li&gt;The reply can still be up to 2 seconds late, because a message that arrives just after a check waits for the next one.&lt;/li&gt;
&lt;li&gt;At scale it is huge. One million users polling every 2 seconds is 500,000 requests per second, nearly all of them returning nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Throttling&lt;/strong&gt; is how servers protect themselves from this. The server (or a gateway in front of it) limits how many requests one client may send, for example 100 per minute. If we cross it, we get &lt;code&gt;429 Too Many Requests&lt;/code&gt;. So throttling is a deliberate limit on the client's requests, not the server slowing itself down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Long polling: let the server wait
&lt;/h2&gt;

&lt;p&gt;Instead of the server answering "nothing" immediately, what if it just &lt;strong&gt;holds our request open&lt;/strong&gt;? The client asks "any new message?". If there isn't one, the server &lt;strong&gt;doesn't reply yet&lt;/strong&gt;. It parks the request and replies only when a message arrives, or when a timeout (typically 20 to 60 seconds) runs out and it sends an empty reply. In both cases, the client sends the next request immediately, so there is nearly always one waiting request at the server.&lt;/p&gt;

&lt;p&gt;How does the server wait without wasting effort? It doesn't loop and keep checking the database, since that would only move the waste inside the server. It uses &lt;strong&gt;event-driven programming&lt;/strong&gt;: the handler registers "wake me when a message arrives for this user" and goes to sleep. When a message arrives, an event fires and the server completes the waiting request. This is why event-driven runtimes (Node's event loop, async I/O, &lt;code&gt;epoll&lt;/code&gt;) can hold thousands of parked connections cheaply.&lt;/p&gt;

&lt;p&gt;This fixes much of the waste and the delay, but it is only a partial fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every message still costs one full request and response, with all the headers.&lt;/li&gt;
&lt;li&gt;After every reply, the client has to reconnect.&lt;/li&gt;
&lt;li&gt;It is still effectively one-directional. While a request is parked, sending our own message needs a second request.&lt;/li&gt;
&lt;li&gt;Proxies and load balancers may cut long-held connections, and messages arriving between one reply and the next request must be remembered by the server so they aren't missed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  WebSockets: one connection, both directions
&lt;/h2&gt;

&lt;p&gt;The real fix is to stop making a new request for every message. Open &lt;strong&gt;one connection and keep it open&lt;/strong&gt; for the whole chat, so that either side can send whenever it wants. This is called &lt;strong&gt;full-duplex&lt;/strong&gt; communication, and it is what WebSockets give us.&lt;/p&gt;

&lt;p&gt;A WebSocket doesn't replace HTTP completely. It &lt;strong&gt;starts with HTTP&lt;/strong&gt;, once, to open the door. The client sends a normal HTTP request asking to switch protocols:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/chat&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp.com&lt;/span&gt;
&lt;span class="na"&gt;Upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;websocket&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt;
&lt;span class="na"&gt;Sec-WebSocket-Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dGhlIHNhbXBsZSBub25jZQ==&lt;/span&gt;
&lt;span class="na"&gt;Sec-WebSocket-Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the server agrees, it answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;101&lt;/span&gt; &lt;span class="ne"&gt;Switching Protocols&lt;/span&gt;
&lt;span class="na"&gt;Upgrade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;websocket&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt;
&lt;span class="na"&gt;Sec-WebSocket-Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3pPLMBiTxaQ9kYGzzhZRbK+xOo=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;101 Switching Protocols&lt;/code&gt; means "agreed". The &lt;code&gt;Sec-WebSocket-Accept&lt;/code&gt; value is calculated from the client's key, proving the server really understands WebSocket. Starting as HTTP also helps because the connection passes through the same ports (80 and 443) and proxies that already handle web traffic.&lt;/p&gt;

&lt;p&gt;After that exchange, &lt;strong&gt;the same TCP connection stops speaking HTTP&lt;/strong&gt;. Both sides now send small &lt;strong&gt;frames&lt;/strong&gt; (text, binary, ping, pong or close) with a header of only 2 to 14 bytes, instead of hundreds of bytes of HTTP headers on every message.&lt;/p&gt;

&lt;p&gt;What this gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The server can &lt;strong&gt;push&lt;/strong&gt;: "your friend replied" arrives the moment it exists, without any request.&lt;/li&gt;
&lt;li&gt;No repeated headers, no reconnecting, no empty replies.&lt;/li&gt;
&lt;li&gt;URLs are &lt;code&gt;ws://&lt;/code&gt; (plain) and &lt;code&gt;wss://&lt;/code&gt; (encrypted with TLS, the one used in production).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ping/pong&lt;/strong&gt; frames check the connection is alive, because silent connections get dropped by routers and proxies.&lt;/li&gt;
&lt;li&gt;The trade-off is that the server now holds state for every open connection. Scaling across several servers needs an extra layer (like Redis pub/sub) to deliver a message to whichever server holds the receiver's connection.&lt;/li&gt;
&lt;li&gt;If only the server needs to push (notifications, live scores), Server-Sent Events is a lighter one-way option over plain HTTP.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  WebRTC: when TCP's reliability becomes the problem
&lt;/h2&gt;

&lt;p&gt;WebSockets are great for chat because every message must arrive, and in order. That is exactly what TCP guarantees. But live voice and video are different.&lt;/p&gt;

&lt;p&gt;Suppose a packet is lost. TCP will not hand later packets to the app until the missing one is re-sent, even if they have already arrived. This is &lt;strong&gt;head-of-line blocking&lt;/strong&gt;. For a download, that is correct. For a live call it is a disaster, because by the time the re-sent packet arrives, that moment of the conversation is 300 ms old and useless. The call doesn't show an error. It &lt;strong&gt;freezes and lags&lt;/strong&gt;, then rushes to catch up.&lt;/p&gt;

&lt;p&gt;So for live media we want &lt;strong&gt;UDP&lt;/strong&gt;: if a few milliseconds of voice are lost, skip them and keep going. For a call, fresh and slightly broken is better than perfect and late.&lt;/p&gt;

&lt;p&gt;That is why &lt;strong&gt;WebRTC&lt;/strong&gt; exists. It isn't one protocol but a bundle that lets two browsers or apps talk &lt;strong&gt;peer-to-peer&lt;/strong&gt;, directly and not through our server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signaling:&lt;/strong&gt; before the call, the two sides must exchange who they are, which codecs they support (SDP offer and answer), and how to reach each other (ICE candidates). WebRTC doesn't define how to carry this, so apps usually use a &lt;strong&gt;WebSocket&lt;/strong&gt; or HTTP through a signaling server. That is why WebSockets and WebRTC often appear together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NAT traversal:&lt;/strong&gt; most devices sit behind a router with a private address, so they can't just dial each other. A &lt;strong&gt;STUN&lt;/strong&gt; server tells each peer its public address. If a direct path is impossible (strict firewalls), a &lt;strong&gt;TURN&lt;/strong&gt; server relays the media instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media:&lt;/strong&gt; audio and video travel as &lt;strong&gt;SRTP&lt;/strong&gt; (encrypted RTP) over UDP. Arbitrary data (chat, files, game state) travels over &lt;strong&gt;data channels&lt;/strong&gt; using SCTP over DTLS, also on UDP.&lt;/li&gt;
&lt;li&gt;Because UDP gives no help, WebRTC adds its own lightweight control: sequence numbers, a &lt;strong&gt;jitter buffer&lt;/strong&gt; to smooth uneven arrival, &lt;strong&gt;congestion control&lt;/strong&gt; to lower quality on a bad network, and optional re-sends only for important video frames.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why not WebSockets over UDP?
&lt;/h2&gt;

&lt;p&gt;This question comes up naturally, and the answer has four parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket is defined on top of TCP.&lt;/strong&gt; It begins with an HTTP upgrade, and its frame format assumes bytes arrive complete and in order. It can't survive packet loss, so a "WebSocket over UDP" would really be a different protocol.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browsers don't allow raw UDP sockets.&lt;/strong&gt; A page that could send arbitrary UDP packets could be used for attacks and network scanning. WebRTC is the controlled exception, with built-in encryption and a check that the other side agreed to receive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw UDP gives none of the extras.&lt;/strong&gt; NAT traversal, encryption, congestion control, jitter buffering and codecs would all have to be built from scratch. WebRTC ships them together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They are built for different jobs.&lt;/strong&gt; Chat, notifications and live dashboards need every message in order, so TCP's guarantees are a feature there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A newer option is &lt;strong&gt;WebTransport&lt;/strong&gt;, which runs over HTTP/3 (QUIC, built on UDP). It gives browsers both reliable streams and unreliable datagrams, a bit like a "WebSocket that can also skip reliability".&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole story in one list
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load a profile, create an account:&lt;/strong&gt; use &lt;strong&gt;HTTP&lt;/strong&gt;. One request, one response. Only the client can start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for news on a timer:&lt;/strong&gt; use &lt;strong&gt;short polling&lt;/strong&gt;. Repeated requests, mostly empty replies. Only the client can start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for news, with less waste:&lt;/strong&gt; use &lt;strong&gt;long polling&lt;/strong&gt;. The request is parked until data arrives. Only the client can start (the server replies late).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live chat, notifications, live scores:&lt;/strong&gt; use &lt;strong&gt;WebSocket&lt;/strong&gt;. One TCP connection kept open, data sent as frames. Both sides can start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live voice, video, games:&lt;/strong&gt; use &lt;strong&gt;WebRTC&lt;/strong&gt;. Peer-to-peer, mostly over UDP. Both sides can start.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  In one line each
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;IP, TCP and UDP are the &lt;strong&gt;phone line&lt;/strong&gt;: they carry the bits.&lt;/li&gt;
&lt;li&gt;HTTP is the &lt;strong&gt;language&lt;/strong&gt;: it gives the bits a meaning (&lt;code&gt;GET /myprofile&lt;/code&gt; = send my profile).&lt;/li&gt;
&lt;li&gt;Polling and long polling are &lt;strong&gt;workarounds&lt;/strong&gt; for HTTP only answering when asked.&lt;/li&gt;
&lt;li&gt;WebSocket &lt;strong&gt;keeps the line open&lt;/strong&gt; so both sides can speak anytime.&lt;/li&gt;
&lt;li&gt;WebRTC &lt;strong&gt;gives up TCP's guarantees on purpose&lt;/strong&gt;, because in a live call, a late word is worth less than a missing one.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>networking</category>
      <category>websockets</category>
      <category>http</category>
      <category>webrtc</category>
    </item>
    <item>
      <title>Fundamentals of redis(caching), why we use REDIS? or origin of it</title>
      <dc:creator>Rahul Kumar</dc:creator>
      <pubDate>Sat, 03 Oct 2026 17:00:44 +0000</pubDate>
      <link>https://dev.to/coderahul1/fundamentals-of-rediscaching-why-we-use-redis-or-origin-of-it-5cil</link>
      <guid>https://dev.to/coderahul1/fundamentals-of-rediscaching-why-we-use-redis-or-origin-of-it-5cil</guid>
      <description>&lt;p&gt;Part 1: Computer basics&lt;br&gt;
CPU (Central Processing Unit)&lt;br&gt;
Definition: The chip that executes instructions (calculations, comparisons, running your code).&lt;br&gt;
Simple meaning: The brain that does the work.&lt;br&gt;
Example: Parsing a request, running a loop, or computing a sum is CPU work.&lt;br&gt;
RAM (Random Access Memory)&lt;br&gt;
Definition: Fast, temporary memory where a running program keeps the data it is using right now.&lt;br&gt;
Simple meaning: The desk where you keep the papers you're working on.&lt;br&gt;
Example: Variables, arrays and maps in your code live in RAM.&lt;br&gt;
SSD (Solid State Drive)&lt;br&gt;
Definition: Permanent storage with no moving parts. Data stays when power is off.&lt;br&gt;
Simple meaning: The cupboard where you store papers for the long term.&lt;br&gt;
Example: Your files, installed apps, and database files.&lt;br&gt;
Volatile vs Persistent&lt;br&gt;
Volatile: data is lost when power is off or the process restarts (RAM).&lt;br&gt;
Persistent: data survives restarts and power loss (SSD).&lt;br&gt;
Latency&lt;br&gt;
Definition: The time one operation takes from start to finish.&lt;br&gt;
Simple meaning: How long you wait for one answer.&lt;br&gt;
Operation   Rough latency&lt;br&gt;
Read from RAM   ~100 nanoseconds&lt;br&gt;
Read from SSD   ~100 microseconds&lt;br&gt;
Network call to Redis   ~0.5 to 1 millisecond&lt;br&gt;
Database query  ~5 to 50 milliseconds (varies a lot)&lt;/p&gt;

&lt;p&gt;(1 millisecond = 1,000 microseconds = 1,000,000 nanoseconds)&lt;/p&gt;

&lt;p&gt;Yes, your laptop, EC2 server and database server all have CPU, RAM and SSD.&lt;/p&gt;

&lt;p&gt;Part 2: Why a local map works on your laptop but breaks in production&lt;br&gt;
Hash map (map / dictionary)&lt;br&gt;
Definition: A data structure that stores data as key → value pairs and finds a value by its key very quickly (usually O(1)).&lt;br&gt;
Simple meaning: A phonebook: look up the name (key), get the number (value).&lt;br&gt;
Example: cache["user:42"] = {name: "Ravi"}&lt;br&gt;
Cache&lt;br&gt;
Definition: A fast storage layer that keeps copies of frequently used data so you don't have to fetch it from the slow source again.&lt;br&gt;
Simple meaning: A shortcut. Keep answers you need often in RAM instead of asking the database every time.&lt;br&gt;
Cache hit and cache miss&lt;br&gt;
Cache hit: the data is found in the cache (fast).&lt;br&gt;
Cache miss: the data is not in the cache, so you go to the database (slow), then usually save it to the cache.&lt;br&gt;
Hit ratio: hits ÷ total requests. A higher ratio means a better cache.&lt;br&gt;
Synchronous vs Asynchronous&lt;br&gt;
Synchronous: one task finishes before the next starts.&lt;br&gt;
Asynchronous: a task is started, and the program continues doing other things while waiting.&lt;/p&gt;

&lt;p&gt;A single request on your laptop is simple, so a map works. Production is different because of concurrency.&lt;/p&gt;

&lt;p&gt;Concurrency&lt;br&gt;
Definition: Many tasks in progress at overlapping times.&lt;br&gt;
Simple meaning: 100 users sending requests at the same moment.&lt;br&gt;
Note: Concurrency is not the same as synchronous. Your server is concurrent. The problem isn't that one request is synchronous.&lt;br&gt;
Thread and Goroutine&lt;br&gt;
Thread: the smallest unit of execution the OS schedules. A server uses many threads to handle many requests.&lt;br&gt;
Goroutine: Go's lightweight version of a thread, managed by Go itself. A web server in Go usually runs each request in its own goroutine.&lt;br&gt;
Race condition&lt;br&gt;
Definition: A bug where the result depends on the timing of two or more threads touching the same data.&lt;br&gt;
Example: Two goroutines both read count = 5, both add 1, and both write 6. The answer should be 7. One update is lost.&lt;br&gt;
Problem 1: Concurrent map access&lt;/p&gt;

&lt;p&gt;In Go, writing to a map from two goroutines at once causes a fatal error (concurrent map writes) and the whole process crashes. (Node.js runs JS on one thread, so it doesn't crash this way, but logical races can still occur across await calls.)&lt;/p&gt;

&lt;p&gt;Mutex (Mutual Exclusion lock)&lt;br&gt;
Definition: A lock that allows only one goroutine at a time into a protected section of code.&lt;br&gt;
Simple meaning: A bathroom key. Whoever has the key goes in, and everyone else waits.&lt;br&gt;
sync.RWMutex (Read-Write mutex): many readers can read together (RLock), but a writer needs exclusive access (Lock).&lt;br&gt;
Thread-safe: code that behaves correctly when used by many threads at once.&lt;br&gt;
Note: A mutex doesn't make requests synchronous. It makes only the small locked part sequential.&lt;br&gt;
Problem 2: Memory grows forever&lt;/p&gt;

&lt;p&gt;A map has no size limit. Keep adding keys and RAM fills up.&lt;/p&gt;

&lt;p&gt;OOM (Out Of Memory): the OS kills your process because RAM ran out.&lt;br&gt;
Memory leak: memory that keeps growing and is never released.&lt;br&gt;
Eviction and LRU&lt;br&gt;
Eviction: automatically removing old data from a cache when it is full.&lt;br&gt;
LRU (Least Recently Used): remove the item that hasn't been used for the longest time.&lt;br&gt;
Other policies: LFU (Least Frequently Used), FIFO (First In First Out), random.&lt;br&gt;
Problem 3: Stale data (you missed this)&lt;br&gt;
TTL (Time To Live): a timer on a key. After it ends, the key is deleted automatically.&lt;br&gt;
Stale data: a cached copy that is out of date compared to the database.&lt;br&gt;
Example: SET user:42 data EX 60 means the key expires after 60 seconds.&lt;/p&gt;

&lt;p&gt;To build a real cache by hand you need Mutex + LRU eviction + TTL. This is hard and bug-prone.&lt;/p&gt;

&lt;p&gt;Part 3: Why not use the EC2 or RDS RAM as the cache?&lt;br&gt;
Typical setup&lt;br&gt;
EC2 (app server): runs your Go/Node code.&lt;br&gt;
RDS (database server): runs PostgreSQL/MySQL.&lt;br&gt;
Each has its own CPU, RAM and SSD. RAM is never shared between machines.&lt;br&gt;
Load balancer&lt;br&gt;
Definition: A component that spreads incoming requests across multiple servers.&lt;br&gt;
Simple meaning: A traffic police officer sending each car to a different lane.&lt;br&gt;
Horizontal scaling&lt;br&gt;
Definition: Handling more traffic by adding more servers (instead of making one server bigger).&lt;br&gt;
Vertical scaling: making one server bigger (more CPU/RAM).&lt;br&gt;
Issue A: Each server has its own separate RAM&lt;br&gt;
User A's data is cached on EC2 #1.&lt;br&gt;
The next request goes to EC2 #2, which has nothing, so that's a cache miss.&lt;br&gt;
The same data ends up cached 10 times.&lt;br&gt;
Worse, the copies can disagree, which is called inconsistency. EC2 #1 has old data and EC2 #2 has new data.&lt;/p&gt;

&lt;p&gt;For sessions, a miss means the user is logged out. For normal data, it only means an extra database trip.&lt;/p&gt;

&lt;p&gt;Session: data the server remembers about a logged-in user between requests.&lt;br&gt;
Issue B: Restarts wipe the cache&lt;/p&gt;

&lt;p&gt;Deploys, crashes and auto-scaling erase RAM.&lt;/p&gt;

&lt;p&gt;Cold cache: an empty cache after a restart.&lt;br&gt;
Thundering herd (cache stampede): after a cold start, thousands of requests miss at once and all hit the database together.&lt;br&gt;
Why not rely on RDS's own RAM?&lt;/p&gt;

&lt;p&gt;RDS does cache data pages in RAM automatically. But a database is built for correctness, not raw speed:&lt;/p&gt;

&lt;p&gt;ACID: Atomicity, Consistency, Isolation, Durability. Guarantees that transactions are safe and complete.&lt;br&gt;
Durability: once committed, data is saved to disk even if power fails. This forces disk writes.&lt;br&gt;
Query overhead: each query is parsed, planned and executed, with joins, filters and locks costing CPU.&lt;br&gt;
Connection limit: a database allows only a limited number of simultaneous connections. A connection pool reuses connections to avoid opening new ones.&lt;br&gt;
Hard to scale: adding database servers is much harder than adding app servers.&lt;/p&gt;

&lt;p&gt;Under 100,000 simple queries, RDS typically doesn't crash. It slows down or runs out of connections, and then your whole app slows down.&lt;/p&gt;

&lt;p&gt;Conclusion: We need one shared, central, in-memory cache that all EC2 servers talk to over the network. That is Redis.&lt;/p&gt;

&lt;p&gt;Part 4: What is Redis?&lt;/p&gt;

&lt;p&gt;Redis (REmote DIctionary Server) is an in-memory data store that works like a hash map running on its own server, accessible over the network. It has these features built in:&lt;/p&gt;

&lt;p&gt;Thread-safe by design: Redis executes commands one at a time (single-threaded command execution), so no race conditions on your data.&lt;br&gt;
Eviction policies: LRU, LFU, etc., so memory never grows forever.&lt;br&gt;
TTL: keys expire automatically.&lt;br&gt;
Shared: all EC2 servers see the same data.&lt;br&gt;
Optional persistence: it can save data to disk to survive restarts.&lt;br&gt;
Tradeoffs to know&lt;br&gt;
Network cost: Redis is a separate server, so it's slower than local RAM (~1 ms vs ~100 ns), but far faster than the database.&lt;br&gt;
Single point of failure (SPOF): if the one Redis server dies, everything depending on it breaks. The fix is replication (keeping copies on other servers).&lt;br&gt;
Still volatile by default, unless persistence is enabled.&lt;br&gt;
Cache invalidation: deciding when to delete or update cached data after the database changes. This is famously one of the hardest problems in computing.&lt;/p&gt;

</description>
      <category>development</category>
      <category>redis</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>I ran a small Go experiment to see how CPU cores affect performance.</title>
      <dc:creator>Rahul Kumar</dc:creator>
      <pubDate>Fri, 02 Oct 2026 13:53:31 +0000</pubDate>
      <link>https://dev.to/coderahul1/i-ran-a-small-go-experiment-to-see-how-cpu-cores-affect-performance-31hi</link>
      <guid>https://dev.to/coderahul1/i-ran-a-small-go-experiment-to-see-how-cpu-cores-affect-performance-31hi</guid>
      <description>&lt;p&gt;&lt;strong&gt;## 1. Understanding CPU cores at the system level&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the system level, the CPU is responsible for executing instructions. We have different numbers of CPU cores, such as 1 core, 2 cores, 4 cores, 8 cores, etc.&lt;/p&gt;

&lt;p&gt;Each logical CPU can execute one thread at a time.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 logical CPU → 1 thread executing at a time.&lt;/li&gt;
&lt;li&gt;2 logical CPUs → 2 threads executing simultaneously.&lt;/li&gt;
&lt;li&gt;4 logical CPUs → 4 threads executing simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, having more CPU cores does not automatically mean faster execution. It depends on how the application utilizes those cores and whether the workload can be executed in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Concurrency vs. Parallelism
&lt;/h2&gt;

&lt;p&gt;Concurrency: Multiple tasks are in progress, but they don't necessarily execute simultaneously.&lt;/p&gt;

&lt;p&gt;For example, on a single CPU core, the operating system can switch between multiple tasks. To humans, they may appear to be executing simultaneously because the switching happens very quickly.&lt;/p&gt;

&lt;p&gt;Parallelism: Two or more tasks execute at the same time using separate execution resources, such as different CPU cores.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Task 1 executes on Core 1.&lt;/li&gt;
&lt;li&gt;Task 2 executes on Core 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both tasks are executing simultaneously.&lt;/p&gt;

&lt;p&gt;Remember: Concurrency is about managing multiple tasks at once, whereas parallelism is about executing multiple tasks at the same instant.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How Go handles task execution
&lt;/h2&gt;

&lt;p&gt;Go provides its own concurrency mechanism through goroutines and a runtime scheduler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goroutines
&lt;/h3&gt;

&lt;p&gt;A goroutine is a lightweight execution unit managed by the Go runtime.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 task → potentially 1 goroutine.&lt;/li&gt;
&lt;li&gt;2 independent tasks → potentially 2 goroutines.&lt;/li&gt;
&lt;li&gt;10,000 independent tasks → potentially 10,000 goroutines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the number of goroutines depends on how we design our application. We don't necessarily need one goroutine for every individual job.&lt;/p&gt;

&lt;p&gt;Go can efficiently manage thousands of goroutines without creating an equivalent number of OS threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  OS Threads
&lt;/h3&gt;

&lt;p&gt;OS threads are execution contexts managed by the operating system.&lt;/p&gt;

&lt;p&gt;They are responsible for executing instructions on available logical CPUs.&lt;/p&gt;

&lt;p&gt;Go's runtime manages the relationship between goroutines and OS threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  GOMAXPROCS
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GOMAXPROCS&lt;/code&gt; controls how many goroutines can execute Go code simultaneously.&lt;/p&gt;

&lt;p&gt;By default, Go generally configures this according to the available CPU resources.&lt;/p&gt;

&lt;p&gt;For example, if our system has 2 available logical CPUs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available CPUs: 2
GOMAXPROCS: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means Go can execute Go code in parallel across up to 2 logical CPUs.&lt;/p&gt;

&lt;p&gt;Important: GOMAXPROCS is not the number of OS threads Go creates. Go's runtime can create and manage more OS threads as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Relationship between CPU cores, OS threads, and goroutines
&lt;/h2&gt;

&lt;p&gt;Suppose our system has only 2 logical CPUs.&lt;/p&gt;

&lt;p&gt;We can still have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 goroutines.&lt;/li&gt;
&lt;li&gt;10 OS threads.&lt;/li&gt;
&lt;li&gt;100 goroutines.&lt;/li&gt;
&lt;li&gt;Even thousands of goroutines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But having more threads or goroutines does not mean we have more physical CPU cores.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available logical CPUs: 2
Goroutines: 4
GOMAXPROCS: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go can execute up to 2 goroutines simultaneously, while the remaining goroutines wait for their turn or wait for some operation to complete.&lt;/p&gt;

&lt;p&gt;The Go runtime schedules goroutines onto OS threads, and the operating system schedules those threads onto available logical CPUs.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Does having more threads make execution faster?
&lt;/h2&gt;

&lt;p&gt;Sometimes yes, but not always.&lt;/p&gt;

&lt;p&gt;It depends on the workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 1: CPU-intensive tasks
&lt;/h3&gt;

&lt;p&gt;Suppose we are calculating prime numbers within a specific range.&lt;/p&gt;

&lt;p&gt;This task requires continuous CPU computation.&lt;/p&gt;

&lt;p&gt;If we have 2 logical CPUs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With GOMAXPROCS=1, only one goroutine can execute Go code at a time.&lt;/li&gt;
&lt;li&gt;With GOMAXPROCS=2, two goroutines can execute Go code simultaneously.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, the second configuration can complete the calculation faster because it utilizes both available CPUs.&lt;/p&gt;

&lt;p&gt;However, increasing GOMAXPROCS beyond the available CPU capacity does not provide additional physical processing power.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 2: I/O-intensive tasks
&lt;/h3&gt;

&lt;p&gt;Suppose our backend server is handling multiple requests, and some requests require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetching data from a database.&lt;/li&gt;
&lt;li&gt;Reading files from disk.&lt;/li&gt;
&lt;li&gt;Calling external APIs.&lt;/li&gt;
&lt;li&gt;Waiting for network responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;During these operations, a goroutine may become blocked or suspended while waiting for the result.&lt;/p&gt;

&lt;p&gt;Instead of keeping the CPU idle, Go can schedule another runnable goroutine.&lt;/p&gt;

&lt;p&gt;This allows the application to continue processing other requests while some operations are waiting.&lt;/p&gt;

&lt;p&gt;This is one of the major reasons backend applications use many goroutines even when they have relatively few CPU cores.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Why can excessive threads introduce overhead?
&lt;/h2&gt;

&lt;p&gt;Suppose we have 2 logical CPUs but many runnable OS threads.&lt;/p&gt;

&lt;p&gt;The operating system must schedule those threads onto the available CPUs.&lt;/p&gt;

&lt;p&gt;When switching between threads, the operating system may need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Save the execution state of the currently running thread.&lt;/li&gt;
&lt;li&gt;Restore the execution state of another thread.&lt;/li&gt;
&lt;li&gt;Update scheduling information.&lt;/li&gt;
&lt;li&gt;Handle CPU cache effects and other scheduling costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is called context switching.&lt;/p&gt;

&lt;p&gt;Context switching is not the same as moving a thread to disk or physically removing it from memory. Threads generally remain represented in memory while they are waiting to execute.&lt;/p&gt;

&lt;p&gt;If the system performs excessive scheduling and context switching, it can introduce overhead and reduce performance.&lt;/p&gt;

&lt;p&gt;However, more threads do not automatically mean more overhead. The impact depends on how many threads are runnable, what they are doing, and how the workload is structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Why does Go use more OS threads when the system has limited CPU cores?
&lt;/h2&gt;

&lt;p&gt;Go's runtime manages goroutines and OS threads separately.&lt;/p&gt;

&lt;p&gt;For CPU-intensive workloads, Go generally uses its available parallel execution capacity efficiently.&lt;/p&gt;

&lt;p&gt;For I/O-intensive workloads, some goroutines may wait for operations to complete.&lt;/p&gt;

&lt;p&gt;Go can schedule other runnable goroutines while those operations are waiting.&lt;/p&gt;

&lt;p&gt;For certain blocking system calls, an OS thread may become blocked, and the Go runtime can arrange for other goroutines to continue executing using other available threads.&lt;/p&gt;

&lt;p&gt;For network I/O, Go commonly uses runtime network polling to avoid requiring one blocked OS thread for every waiting network connection.&lt;/p&gt;

&lt;p&gt;This helps Go handle a large number of concurrent requests without requiring an equivalent number of OS threads.&lt;/p&gt;

&lt;h1&gt;
  
  
  8. Practical Experiment: Measuring CPU Parallelism in Go
&lt;/h1&gt;

&lt;p&gt;We created a Go program to calculate prime numbers within a specified range using multiple goroutines.&lt;/p&gt;

&lt;p&gt;The program used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 available logical CPUs.&lt;/li&gt;
&lt;li&gt;4 goroutines.&lt;/li&gt;
&lt;li&gt;Different &lt;code&gt;GOMAXPROCS&lt;/code&gt; configurations.&lt;/li&gt;
&lt;li&gt;Execution time as the performance measurement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Experiment results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GOMAXPROCS&lt;/th&gt;
&lt;th&gt;Workers&lt;/th&gt;
&lt;th&gt;Primes found&lt;/th&gt;
&lt;th&gt;Execution time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;78,498&lt;/td&gt;
&lt;td&gt;388.075 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;78,498&lt;/td&gt;
&lt;td&gt;205.7652 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;78,498&lt;/td&gt;
&lt;td&gt;244.6589 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;78,498&lt;/td&gt;
&lt;td&gt;272.6667 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What did we observe?
&lt;/h3&gt;

&lt;p&gt;When GOMAXPROCS = 1:&lt;/p&gt;

&lt;p&gt;Only one goroutine could execute Go code at a time.&lt;/p&gt;

&lt;p&gt;Execution time: approximately 388 ms.&lt;/p&gt;

&lt;p&gt;When GOMAXPROCS = 2:&lt;/p&gt;

&lt;p&gt;Two goroutines could execute Go code simultaneously across the available CPUs.&lt;/p&gt;

&lt;p&gt;Execution time: approximately 206 ms.&lt;/p&gt;

&lt;p&gt;The workload completed substantially faster.&lt;/p&gt;

&lt;p&gt;When GOMAXPROCS = 3 or 4:&lt;/p&gt;

&lt;p&gt;The configuration permitted more parallel execution capacity, but our machine still had only 2 available logical CPUs.&lt;/p&gt;

&lt;p&gt;The measured execution time increased compared with the 2-CPU configuration.&lt;/p&gt;

&lt;p&gt;This may involve scheduling overhead and measurement variability. Additional parallelism does not guarantee improved performance.&lt;/p&gt;

&lt;p&gt;We should repeat the benchmark multiple times and compare averages before drawing a firm conclusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion from the experiment
&lt;/h3&gt;

&lt;p&gt;More CPU cores can improve the performance of CPU-intensive workloads when the application can utilize them effectively.&lt;/p&gt;

&lt;p&gt;However, more threads, goroutines, or parallel execution slots do not automatically make an application faster.&lt;/p&gt;

&lt;p&gt;We must identify the workload, available hardware resources, and potential bottlenecks.&lt;/p&gt;

&lt;p&gt;CODE:&lt;/p&gt;

&lt;p&gt;package main&lt;/p&gt;

&lt;p&gt;import (&lt;br&gt;
    "flag"&lt;br&gt;
    "fmt"&lt;br&gt;
    "runtime"&lt;br&gt;
    "sync"&lt;br&gt;
    "time"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;func isPrime(n int) bool {&lt;br&gt;
    if n &amp;lt; 2 {&lt;br&gt;
        return false&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i := 2; i*i &amp;lt;= n; i++ {
    if n%i == 0 {
        return false
    }
}
return true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    procs := flag.Int("procs", 1, "Number of CPUs to use")&lt;br&gt;
    flag.Parse()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;runtime.GOMAXPROCS(*procs)

const limit = 1000000
const workers = 4

var wg sync.WaitGroup
var total int64

var mu sync.Mutex

start := time.Now()

chunk := limit / workers

for w := 0; w &amp;lt; workers; w++ {
    wg.Add(1)

    go func(worker int) {
        defer wg.Done()

        count := 0
        startNum := worker*chunk + 2
        endNum := (worker + 1) * chunk + 1

        for n := startNum; n &amp;lt;= endNum; n++ {
            if isPrime(n) {
                count++
            }
        }

        mu.Lock()
        total += int64(count)
        mu.Unlock()
    }(w)
}

wg.Wait()

elapsed := time.Since(start)

fmt.Println("Available CPUs:", runtime.NumCPU())
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
fmt.Println("Workers:", workers)
fmt.Println("Primes found:", total)
fmt.Println("Execution time:", elapsed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;OUTPUT :&lt;/p&gt;

&lt;p&gt;D:\Scaling&amp;gt;go run main.go -procs=1&lt;br&gt;
Available CPUs: 2&lt;br&gt;
GOMAXPROCS: 1&lt;br&gt;
Workers: 4&lt;br&gt;
Primes found: 78498&lt;br&gt;
Execution time: 388.075ms&lt;/p&gt;

&lt;p&gt;D:\Scaling&amp;gt;go run main.go -procs=2&lt;br&gt;
Available CPUs: 2&lt;br&gt;
GOMAXPROCS: 2&lt;br&gt;
Workers: 4&lt;br&gt;
Primes found: 78498&lt;br&gt;
Execution time: 205.7652ms&lt;/p&gt;

&lt;p&gt;D:\Scaling&amp;gt;go run main.go -procs=3&lt;br&gt;
Available CPUs: 2&lt;br&gt;
GOMAXPROCS: 3&lt;br&gt;
Workers: 4&lt;br&gt;
Primes found: 78498&lt;br&gt;
Execution time: 244.6589ms&lt;/p&gt;

&lt;p&gt;D:\Scaling&amp;gt;go run main.go -procs=4&lt;br&gt;
Available CPUs: 2&lt;br&gt;
GOMAXPROCS: 4&lt;br&gt;
Workers: 4&lt;br&gt;
Primes found: 78498&lt;br&gt;
Execution time: 272.6667ms&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
