<?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: Moca</title>
    <description>The latest articles on DEV Community by Moca (@moca_fd0227eb23c29ea2d065).</description>
    <link>https://dev.to/moca_fd0227eb23c29ea2d065</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%2F3873307%2F2c2392c3-baac-4bcd-af34-09b02bc1e897.jpg</url>
      <title>DEV Community: Moca</title>
      <link>https://dev.to/moca_fd0227eb23c29ea2d065</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/moca_fd0227eb23c29ea2d065"/>
    <language>en</language>
    <item>
      <title>Why Your Local LLM Gives Different Answers at Temperature 0.</title>
      <dc:creator>Moca</dc:creator>
      <pubDate>Fri, 23 Oct 2026 05:57:30 +0000</pubDate>
      <link>https://dev.to/moca_fd0227eb23c29ea2d065/why-your-local-llm-gives-different-answers-at-temperature-0-32jn</link>
      <guid>https://dev.to/moca_fd0227eb23c29ea2d065/why-your-local-llm-gives-different-answers-at-temperature-0-32jn</guid>
      <description>&lt;p&gt;You set &lt;code&gt;temperature&lt;/code&gt; to 0, fix the seed, send the same prompt twice, and get two different answers. This post explains where the difference comes from, how to see it on your own machine, and what each engine offers if you need answers that repeat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;

&lt;p&gt;One measured case: I ran lm-evaluation-harness on gsm8k (100 problems) against llama.cpp's server (build b11643, Qwen2.5-7B-Instruct Q8_0, &lt;code&gt;-np 8&lt;/code&gt;), once with one request at a time and once with eight concurrent requests. Same model file, same settings, greedy decoding. The generated text differed on 41 of the 100 problems. On problem 0 the two runs agreed for 213 characters; then one wrote "so she makes" and the other "so the total amount she makes", and the rest of the solution went its own way. The strict-match score moved from 0.83 to 0.82.&lt;/p&gt;

&lt;p&gt;It is not just one engine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thinking Machines Lab sent the same prompt 1,000 times at temperature 0 and got 80 different completions (&lt;a href="https://thinkingmachines.ai/blog/defeating-nondeterminism-in-llm-inference/" rel="noopener noreferrer"&gt;Defeating Nondeterminism in LLM Inference&lt;/a&gt;, 2025).&lt;/li&gt;
&lt;li&gt;Yuan et al. measured up to 9% accuracy variation for DeepSeek-R1-Distill-Qwen-7B in BF16 when only the number of GPUs, the GPU type or the evaluation batch size changed (&lt;a href="https://arxiv.org/abs/2506.09501" rel="noopener noreferrer"&gt;arXiv 2506.09501&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The issue trackers of llama.cpp, Ollama, LM Studio and vLLM all have reports of temperature-0 output that changes with the prompt cache, speculative decoding, the number of slots or the GPU.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What temperature 0 and the seed actually fix
&lt;/h2&gt;

&lt;p&gt;Temperature and the seed control one step: picking the next token from the probabilities the model computed. At temperature 0 the engine takes the most probable token, and the seed stops mattering.&lt;/p&gt;

&lt;p&gt;Neither touches the probabilities themselves. If the logits come out slightly different, greedy decoding faithfully picks from slightly different numbers. So the real question is why the same model computes slightly different logits for the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 1: floating-point addition depends on order
&lt;/h2&gt;

&lt;p&gt;A computer rounds after every floating-point addition, so &lt;code&gt;(a + b) + c&lt;/code&gt; is not always equal to &lt;code&gt;a + (b + c)&lt;/code&gt;. Each logit is the end of millions of multiply-adds, and the order in which a GPU kernel adds them up decides the last bits.&lt;/p&gt;

&lt;p&gt;A few lines of NumPy show it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_rng&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;standard_normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;                      &lt;span class="c1"&gt;# add in float32, front to back
&lt;/span&gt;    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;fsum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;   &lt;span class="c1"&gt;# False: a different order changes the last digits
&lt;/span&gt;
&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# the same values on an integer grid
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;           &lt;span class="c1"&gt;# True: integer sums don't depend on order
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep the last two lines in mind. They come back at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cause 2: the engine changes the order for you
&lt;/h2&gt;

&lt;p&gt;GPU kernels are written for speed, and the fastest way to add numbers depends on the shape of the work. Your request stays the same, but the order changes whenever the engine changes how it computes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batching and concurrent requests.&lt;/strong&gt; The server computes your request together with whatever else it is doing. A matrix multiplication for one row and for eight rows can use different kernels with different reduction orders, so your row comes out slightly different depending on who else was in the batch. Thinking Machines showed that this lack of &lt;em&gt;batch invariance&lt;/em&gt; is the main cause in inference servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The prompt cache and chunk size.&lt;/strong&gt; A prompt computed in one piece, in chunks, or restored from a cache goes through different kernel paths. llama.cpp's server documentation says so directly: logits are not guaranteed to be bit-for-bit identical for different batch sizes, so the prompt cache (on by default) can cause nondeterministic results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speculative decoding.&lt;/strong&gt; The verify step scores several draft tokens in one batch, which is a different computation from generating them one by one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KV cache quantization, the GPU, the driver and library versions.&lt;/strong&gt; Each can change which kernels run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernels that are not deterministic from run to run,&lt;/strong&gt; such as reductions built on atomic additions. They are rarer in inference, but they exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why a tiny difference changes the whole answer
&lt;/h2&gt;

&lt;p&gt;Usually the top token wins by a wide margin, and a difference in the last bits changes nothing. But a long answer passes through steps where two tokens are nearly tied. There, a one-bit difference can change which token wins. From then on the model is continuing a different text, so everything after that point is regenerated: different wording, sometimes a different line of reasoning, sometimes a different final answer.&lt;/p&gt;

&lt;p&gt;The flips go both ways, which is why scores move far less than answers. In one of my measurements (600 gsm8k problems, DeepSeek-R1-Distill-Qwen-7B Q8_0, greedy), switching between two engines that run the same weights with different numerics changed the text of most answers and flipped correctness on 53 problems: 25 became wrong and 28 became right, a net change of 3 problems. So when you compare two models or two quantizations, compare the outputs item by item, not just the average score.&lt;/p&gt;

&lt;h2&gt;
  
  
  See it on your own machine
&lt;/h2&gt;

&lt;p&gt;Start llama.cpp's server with several slots (use your own model path):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llama-server &lt;span class="nt"&gt;-m&lt;/span&gt; qwen2.5-7b-instruct-q8_0.gguf &lt;span class="nt"&gt;-ngl&lt;/span&gt; 99 &lt;span class="nt"&gt;-np&lt;/span&gt; 8 &lt;span class="nt"&gt;-c&lt;/span&gt; 16384
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run this script. It sends one prompt alone, then again while seven other prompts run at the same time, and compares the two answers. It works with any OpenAI-compatible server; for Ollama or LM Studio, pass the URL and a model name as arguments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Send one prompt alone, then again while 7 other prompts run at the same time,
# and compare the two answers. Standard library only.
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:8080/v1/chat/completions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;MODEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  &lt;span class="c1"&gt;# Ollama and LM Studio need a model name
&lt;/span&gt;&lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain why the sky is blue in three short paragraphs.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OTHERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a short story about a lighthouse keeper.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;List ten facts about octopuses.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain how a hash map works.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the plot of Hamlet.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Describe how vaccines train the immune system.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write four haiku about autumn.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain the difference between TCP and UDP.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MODEL&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                 &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;alone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TARGET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OTHERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;futures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;OTHERS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;together&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alone&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;together&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;identical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alone&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;together&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;together&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;different: first difference at character &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alone:    ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;together: ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;together&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a GPU build you will often see &lt;code&gt;different&lt;/code&gt; and the character where the answers part. How often depends on the model, the GPU and the build, so try a few prompts. Sending the same prompt twice in a row can differ as well: the second request may reuse the cached prompt and take another path. llama.cpp lets you turn that off per request with &lt;code&gt;"cache_prompt": false&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do today
&lt;/h2&gt;

&lt;p&gt;What to set depends on the engine, and each option costs something.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;What to set&lt;/th&gt;
&lt;th&gt;What it costs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;llama.cpp&lt;/td&gt;
&lt;td&gt;One slot (&lt;code&gt;-np 1&lt;/code&gt;), &lt;code&gt;"cache_prompt": false&lt;/code&gt;, no speculative decoding&lt;/td&gt;
&lt;td&gt;Concurrency and the prompt cache. The maintainers do not aim for identical results across batch sizes; one slot without the prompt cache is the way they point to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ollama&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;seed&lt;/code&gt; and &lt;code&gt;temperature: 0&lt;/code&gt; in the options, &lt;code&gt;OLLAMA_NUM_PARALLEL=1&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;VLLM_BATCH_INVARIANT=1&lt;/code&gt; (beta; NVIDIA compute capability 8.0+, AMD Instinct, Intel XPU)&lt;/td&gt;
&lt;td&gt;Some speed; parts of tensor parallelism are not covered yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SGLang&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--enable-deterministic-inference&lt;/code&gt; (FlashInfer, FA3 or Triton attention)&lt;/td&gt;
&lt;td&gt;Some speed (34% on average in its 2025 announcement); no radix cache with FlashInfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugging Face transformers&lt;/td&gt;
&lt;td&gt;Batch size 1, &lt;code&gt;torch.use_deterministic_algorithms(True)&lt;/code&gt;, &lt;code&gt;CUBLAS_WORKSPACE_CONFIG=:4096:8&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Speed; holds only on the same GPU and library versions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things none of these give you: identical results on a different GPU, let alone between NVIDIA and AMD, and a way for someone else to check your numbers without your hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the order irrelevant
&lt;/h2&gt;

&lt;p&gt;The NumPy example holds the other half of the answer: integer sums don't depend on order. That suggests a different approach. Instead of forcing every kernel to add in one fixed order, make the order not matter.&lt;/p&gt;

&lt;p&gt;That is what I built GeulPU to do (disclosure: I'm its author). It is a local engine for GGUF models (Qwen2.5, Qwen3, Llama 3, Mistral) on NVIDIA (CUDA) and other GPUs (Vulkan). It turns activations into blocks of 32 integers with a power-of-two scale, sums the products exactly on int8 tensor cores, and fixes every remaining rounding point in a written precision contract. The GPU output matches a NumPy CPU reference bit for bit, so batching, the prompt cache and speculative decoding stop mattering:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;GeulPU&lt;/th&gt;
&lt;th&gt;llama.cpp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 vs 8 concurrent requests (lm-eval gsm8k, 100 items)&lt;/td&gt;
&lt;td&gt;identical&lt;/td&gt;
&lt;td&gt;41 of 100 generations changed (b11643)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speculative decoding on vs off&lt;/td&gt;
&lt;td&gt;identical tokens and logits&lt;/td&gt;
&lt;td&gt;2 of 8 greedy answers changed (b11721, ngram-simple)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt cache restored from disk vs recomputed (54 agent requests)&lt;/td&gt;
&lt;td&gt;identical (1,728 steps)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 3080 Ti / 4070 Ti / 5070 / Radeon RX 9060 XT (exact mode)&lt;/td&gt;
&lt;td&gt;identical (82,673 steps)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The deterministic modes of vLLM and SGLang above cost speed. Against llama.cpp, GeulPU's default mode doesn't. On an RTX 4070 Ti with Qwen2.5-7B Q4_K_M, version 0.11.0 took 0.76–0.97× llama.cpp's time at every prompt length (512 to 65,536 tokens) and session depth (8k to 64k) measured. The llama.cpp build was b11827, using whichever of its f16 and q8_0 KV caches was faster. A recorded 54-request coding-agent session took 0.59× the time of llama.cpp's fastest setting, a 0.5B draft model. Exact mode, which also makes NVIDIA and AMD agree, is slower: 1.19–1.40× llama.cpp's time on prompts up to 8k tokens. GeulPU is Windows-first (Linux builds from source), runs dense models only for now, and is a solo project: &lt;a href="https://github.com/wwoosshh/GeulPUv2" rel="noopener noreferrer"&gt;https://github.com/wwoosshh/GeulPUv2&lt;/a&gt;. The study behind the "flips go both ways" numbers, with its raw data, is at &lt;a href="https://github.com/wwoosshh/GeulPU" rel="noopener noreferrer"&gt;https://github.com/wwoosshh/GeulPU&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Set the seed anyway. Not every engine or API treats temperature 0 as strictly greedy, and it costs nothing.&lt;/li&gt;
&lt;li&gt;Temperature 0 and a seed fix the sampling step, not the numbers it samples from.&lt;/li&gt;
&lt;li&gt;If answers change, look first at concurrency, the prompt cache and speculative decoding, then at the GPU, driver and library versions.&lt;/li&gt;
&lt;li&gt;For repeatable runs with a standard engine: one request at a time, no prompt cache, no speculation, the same hardware and versions; or the deterministic modes of vLLM and SGLang.&lt;/li&gt;
&lt;li&gt;Compare outputs item by item, not just the final score. Most flips cancel out in the average.&lt;/li&gt;
&lt;li&gt;If you need identical results with concurrency and caching on, or across GPUs, you need an engine built for it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article was drafted with an AI assistant and checked by the author.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Geul: A Native Compiled Language Designed Around Korean Grammar (SOV, Particle Binding, Self-Hosting)</title>
      <dc:creator>Moca</dc:creator>
      <pubDate>Sat, 11 Apr 2026 10:11:22 +0000</pubDate>
      <link>https://dev.to/moca_fd0227eb23c29ea2d065/geul-a-native-compiled-language-designed-around-korean-grammar-sov-particle-binding-3n51</link>
      <guid>https://dev.to/moca_fd0227eb23c29ea2d065/geul-a-native-compiled-language-designed-around-korean-grammar-sov-particle-binding-3n51</guid>
      <description>&lt;p&gt;Every non-English programming language I've seen does the same thing: translate &lt;br&gt;
the keywords. &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt; become local words, but the grammar stays &lt;br&gt;
identical to C or Python.&lt;/p&gt;

&lt;p&gt;That never felt right to me for Korean.&lt;/p&gt;

&lt;p&gt;Korean isn't just "English with different words." It's a fundamentally different &lt;br&gt;
grammatical structure — SOV word order, agglutinative morphology, and a particle &lt;br&gt;
system that carries meaning independently of word position.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;글 (Geul)&lt;/strong&gt; — a natively compiled, self-hosting programming language &lt;br&gt;
that actually models Korean grammar instead of just wearing it as a skin.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Core Problem: Korean is SOV, Not SVO
&lt;/h2&gt;

&lt;p&gt;English (and most programming languages) follow SVO order:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;S&lt;/strong&gt;ubject &lt;strong&gt;V&lt;/strong&gt;erb &lt;strong&gt;O&lt;/strong&gt;bject → &lt;code&gt;add(5, 3)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Korean follows SOV:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;S&lt;/strong&gt;ubject &lt;strong&gt;O&lt;/strong&gt;bject &lt;strong&gt;V&lt;/strong&gt;erb → &lt;code&gt;나는 밥을 먹다&lt;/code&gt; (I rice eat)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But more importantly, Korean uses &lt;strong&gt;grammatical particles&lt;/strong&gt; to mark roles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Particle&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;을/를&lt;/td&gt;
&lt;td&gt;Object marker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;에&lt;/td&gt;
&lt;td&gt;Location / target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;로&lt;/td&gt;
&lt;td&gt;Direction / method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이/가&lt;/td&gt;
&lt;td&gt;Subject marker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This means word order is &lt;em&gt;flexible&lt;/em&gt; in Korean — the particles carry the meaning, &lt;br&gt;
not the position.&lt;/p&gt;


&lt;h2&gt;
  
  
  What This Looks Like in Code
&lt;/h2&gt;

&lt;p&gt;In Geul, a function call follows Korean SOV structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5를 3에 더하다
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;5를&lt;/code&gt; — 5 marked with &lt;strong&gt;를&lt;/strong&gt; (object marker)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3에&lt;/code&gt; — 3 marked with &lt;strong&gt;에&lt;/strong&gt; (target marker)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;더하다&lt;/code&gt; — "to add" (verb form)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare this to the equivalent in C:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function declaration mirrors this structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[정수 왼쪽을 정수 오른쪽에 더하기]는 -&amp;gt; 정수 {
    반환 왼쪽 + 오른쪽.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;왼쪽을&lt;/code&gt; declares that the parameter &lt;code&gt;왼쪽&lt;/code&gt; (left) expects the &lt;strong&gt;를/을&lt;/strong&gt; particle.&lt;br&gt;&lt;br&gt;
&lt;code&gt;오른쪽에&lt;/code&gt; declares that &lt;code&gt;오른쪽&lt;/code&gt; (right) expects the &lt;strong&gt;에&lt;/strong&gt; particle.&lt;/p&gt;

&lt;p&gt;The parser resolves argument-to-parameter binding through particle matching — &lt;br&gt;
not positional order.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Morpheme Problem
&lt;/h2&gt;

&lt;p&gt;Korean verbs conjugate heavily. The call site uses a conjugated verb form, but &lt;br&gt;
the function is declared with a nominalized (noun) form:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Form&lt;/th&gt;
&lt;th&gt;Korean&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Function name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;더하기&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"adding" (noun form)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Call site&lt;/td&gt;
&lt;td&gt;&lt;code&gt;더하다&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"to add" (verb form)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The compiler needs to know that &lt;code&gt;더하다&lt;/code&gt; → &lt;code&gt;더하기&lt;/code&gt; at parse time.&lt;/p&gt;

&lt;p&gt;To solve this, I built a &lt;strong&gt;3-level recursive morpheme analyzer&lt;/strong&gt; that strips &lt;br&gt;
Korean verb conjugation suffixes and maps call-site verb forms to their &lt;br&gt;
declared function names automatically.&lt;/p&gt;


&lt;h2&gt;
  
  
  Compiler Architecture
&lt;/h2&gt;

&lt;p&gt;Geul has two backends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.글 source
    │
    ▼
Morpheme Analysis   ← 3-level recursive affix splitting
    │
    ▼
Recursive Descent Parser   ← SOV + particle-aware
    │
    ▼
AST → 3-Address IR + Static Type Checking
    │
    ├──────────────────────┐
    ▼                      ▼
C Transpiler          x86-64 Native
(cl/gcc)              (PE direct write)
    │                      │
    ▼                      ▼
  .exe                   .exe
                    (zero external deps)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native backend writes &lt;strong&gt;PE headers and x86-64 machine code directly&lt;/strong&gt; — &lt;br&gt;
no &lt;code&gt;cl.exe&lt;/code&gt;, no &lt;code&gt;gcc&lt;/code&gt;, no external assembler required.&lt;/p&gt;
&lt;h3&gt;
  
  
  Optimizations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compare-branch fusion&lt;/strong&gt; — &lt;code&gt;cmp + jcc&lt;/code&gt; direct emit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Register pinning&lt;/strong&gt; — loop variables pinned to callee-saved registers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peephole&lt;/strong&gt; — dead store elimination, redundant copy removal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop-invariant code motion&lt;/strong&gt; — constant loads hoisted out of loops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tail call optimization&lt;/strong&gt; — self-recursion converted to &lt;code&gt;jmp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Self-Hosting
&lt;/h2&gt;

&lt;p&gt;In v0.5, Geul became self-hosting — the compiler is written in Geul and &lt;br&gt;
compiles itself.&lt;/p&gt;

&lt;p&gt;Verification: bootstrap binary → compile source → output binary SHA256 &lt;br&gt;
fixed-point matches. ✓&lt;/p&gt;

&lt;p&gt;This was the hardest milestone. Getting a compiler to compile itself means &lt;br&gt;
every language feature used in the compiler itself must be fully functional &lt;br&gt;
and stable.&lt;/p&gt;


&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;Measured on Intel Ultra 5 226V, 5-run median. C compiled with MSVC &lt;code&gt;/O2&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;Geul&lt;/th&gt;
&lt;th&gt;C /O2&lt;/th&gt;
&lt;th&gt;vs C&lt;/th&gt;
&lt;th&gt;vs Python&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Recursive fib(40)&lt;/td&gt;
&lt;td&gt;464ms&lt;/td&gt;
&lt;td&gt;321ms&lt;/td&gt;
&lt;td&gt;1.45x&lt;/td&gt;
&lt;td&gt;15.9x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sieve of 1M primes&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;td&gt;8ms&lt;/td&gt;
&lt;td&gt;1.30x&lt;/td&gt;
&lt;td&gt;16.7x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bubble sort 30k&lt;/td&gt;
&lt;td&gt;1564ms&lt;/td&gt;
&lt;td&gt;564ms&lt;/td&gt;
&lt;td&gt;2.77x&lt;/td&gt;
&lt;td&gt;24.5x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The bubble sort gap (2.77x) is the most notable — nested loop register &lt;br&gt;
allocation pressure is the likely cause. It's on the roadmap.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Syntax Tour
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Variables and types:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;정수 나이 = 25.          (* int *)
실수 원주율 = 3.14159.   (* double *)
문자열 이름 = "홍길동".  (* string *)
참거짓 활성 = 참.        (* bool *)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;String interpolation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"{이름}은 {나이}세입니다\n"을 쓰다.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Variables are embedded directly with automatic type formatting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;값 &amp;gt; 10이면 {
    "크다\n"을 쓰다.
}
아니면 {
    "작다\n"을 쓰다.
}

(번호 &amp;lt; 10)동안 {
    번호를 증가하다.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Structs:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;묶음 점은 정수 가로, 정수 세로.

점 위치.
위치의 가로 = 10.
위치의 세로 = 20.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Current State
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ Windows 10/11 (Linux ELF planned)&lt;/li&gt;
&lt;li&gt;✅ VS Code extension (syntax highlight, F5 run, LSP autocomplete)&lt;/li&gt;
&lt;li&gt;✅ 9 stdlib modules, 62 functions&lt;/li&gt;
&lt;li&gt;✅ Static type checking at compile time&lt;/li&gt;
&lt;li&gt;✅ Self-hosting since v0.5&lt;/li&gt;
&lt;li&gt;🔲 Package manager (planned)&lt;/li&gt;
&lt;li&gt;🔲 Debug info / PDB (planned)&lt;/li&gt;
&lt;li&gt;🔲 Error handling syntax (&lt;code&gt;시도/실패&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently at &lt;strong&gt;v0.7.1&lt;/strong&gt; (18 releases since March 2026, MIT license).&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;No install needed — browser playground:&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://geul-web.vercel.app/playground" rel="noopener noreferrer"&gt;https://geul-web.vercel.app/playground&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Source:&lt;br&gt;&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/wwoosshh/geul-lang" rel="noopener noreferrer"&gt;https://github.com/wwoosshh/geul-lang&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Curious what approaches others have seen for non-English or non-SVO language &lt;br&gt;
design — are there other languages that go beyond keyword translation?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>opensource</category>
      <category>languages</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
