<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Multithreading on josiete.com</title><link>https://www.josiete.com/en/tags/multithreading/</link><description>Recent content in Multithreading on josiete.com</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 24 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.josiete.com/en/tags/multithreading/index.xml" rel="self" type="application/rss+xml"/><item><title>Polars: Python speeds up DataFrames with Rust and multiple threads</title><link>https://www.josiete.com/en/posts/polars-python/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://www.josiete.com/en/posts/polars-python/</guid><description>&lt;p&gt;&lt;img src="https://www.josiete.com/images/polars-python.webp" alt="Illustration of the Polars logo surrounded by data columns and parallel processing lines"&gt;&lt;/p&gt;&#10;&lt;p&gt;Python remains one of the most comfortable languages for exploring and transforming data. But DataFrame convenience does not always translate into speed: as rows and columns grow, memory and CPU costs become obvious. &lt;a href="https://pola.rs/"&gt;Polars&lt;/a&gt; addresses that problem with a Rust-based DataFrame library exposed through a Python API.&lt;/p&gt;&#10;&lt;h2 id="what-is-polars"&gt;What is Polars?&lt;/h2&gt;&#10;&lt;p&gt;Polars is an analytical query engine and DataFrame library for tabular data. Its core is written in Rust, uses an Apache Arrow-compatible memory model, and combines vectorized, columnar processing with query optimization.&lt;/p&gt;&#10;&lt;p&gt;The API offers two styles. Eager mode executes each operation immediately, which is useful for interactive exploration. Lazy mode builds a query plan: Polars can reorder operations, remove unused columns, and push filters down before executing the result.&lt;/p&gt;&#10;&lt;p&gt;It is not a universal replacement for a database or distributed system. Its strength is using one machine—laptop or server—efficiently while working with CSV, Parquet, JSON, and other sources without requiring a cluster.&lt;/p&gt;&#10;&lt;h2 id="parallelism-multiple-threads-without-writing-concurrency-code"&gt;Parallelism: multiple threads without writing concurrency code&lt;/h2&gt;&#10;&lt;p&gt;Unlike many traditional Python workflows, Polars does not leave all heavy computation on the interpreter&amp;rsquo;s main thread. Its engine is implemented in Rust and uses a thread pool to distribute work across available CPU cores. The engine is vectorized, columnar, and multi-threaded, so a filter, aggregation, or &lt;code&gt;join&lt;/code&gt; can automatically use several cores.&lt;/p&gt;&#10;&lt;p&gt;In practice, users write ordinary Polars expressions and the engine decides how to parallelize them. There is no need to create a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;, serialize every partition, or coordinate results manually. Python is the interface; intensive computation runs in the native core.&lt;/p&gt;&#10;&lt;p&gt;The current API exposes the pool size through &lt;code&gt;pl.thread_pool_size()&lt;/code&gt;. Polars chooses a reasonable value automatically, but in a shared environment—such as a worker with an existing CPU limit—you can set &lt;code&gt;POLARS_MAX_THREADS&lt;/code&gt; before importing the library:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;POLARS_MAX_THREADS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; python analyze_sales.py&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should not be confused with distributed execution: multiple threads use the cores of one machine. For data larger than memory, the lazy engine can also stream a plan when supported; horizontal scaling is provided by a separate distributed layer.&lt;/p&gt;&#10;&lt;h2 id="the-company-behind-the-project"&gt;The company behind the project&lt;/h2&gt;&#10;&lt;p&gt;Polars began in 2020 as a personal project by &lt;a href="https://pola.rs/about-us/"&gt;Ritchie Vink&lt;/a&gt;, initially to learn about query engines, Apache Arrow, and Rust. On August 3, 2023, Vink and &lt;a href="https://pola.rs/posts/company-announcement/"&gt;Chiel Peters&lt;/a&gt; announced the creation of Polars Inc., an Amsterdam-based company focused on providing engineering and long-term stability for the ecosystem.&lt;/p&gt;&#10;&lt;p&gt;The company did not replace the community project: Polars remains open source under the MIT license. Its role is to fund engine development, improve connectors, and offer products for running queries at larger scale, including Polars Cloud. It is an interesting separation: a company can build commercial services around a project while keeping the open core available to everyone.&lt;/p&gt;&#10;&lt;h2 id="a-python-example"&gt;A Python example&lt;/h2&gt;&#10;&lt;p&gt;Install the library with &lt;code&gt;pip&lt;/code&gt; and build a small transformation. &lt;code&gt;scan_csv&lt;/code&gt; returns a &lt;code&gt;LazyFrame&lt;/code&gt;, so operations are planned and &lt;code&gt;collect()&lt;/code&gt; executes the optimized plan.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install polars&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; polars &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; pl&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sales &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scan_csv(&lt;span style="color:#e6db74"&gt;&amp;#34;sales.csv&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sales&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter(pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;col(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;paid&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;with_columns(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;col(&lt;span style="color:#e6db74"&gt;&amp;#34;units&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;col(&lt;span style="color:#e6db74"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;))&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alias(&lt;span style="color:#e6db74"&gt;&amp;#34;revenue&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;group_by(&lt;span style="color:#e6db74"&gt;&amp;#34;category&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;agg(&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;col(&lt;span style="color:#e6db74"&gt;&amp;#34;revenue&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sum()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alias(&lt;span style="color:#e6db74"&gt;&amp;#34;revenue&amp;#34;&lt;/span&gt;),&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;col(&lt;span style="color:#e6db74"&gt;&amp;#34;units&amp;#34;&lt;/span&gt;)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sum()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alias(&lt;span style="color:#e6db74"&gt;&amp;#34;units&amp;#34;&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort(&lt;span style="color:#e6db74"&gt;&amp;#34;revenue&amp;#34;&lt;/span&gt;, descending&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;collect()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(result)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code states what we want and lets Polars decide how to scan columns and divide the work. With a large file, lazy mode can avoid reading unused columns and push the filter into the scan. For Parquet, &lt;code&gt;pl.scan_parquet(&amp;quot;data/*.parquet&amp;quot;)&lt;/code&gt; follows the same pattern.&lt;/p&gt;&#10;&lt;h2 id="when-should-you-choose-it"&gt;When should you choose it?&lt;/h2&gt;&#10;&lt;p&gt;Polars is a strong fit for transformation pipelines, large exploratory analyses, model preparation, and services that need quick responses without immediately adopting a distributed system. If a project depends heavily on the pandas ecosystem, migration requires reviewing some operations and types; both libraries can still coexist, and Polars can convert to pandas when needed.&lt;/p&gt;&#10;&lt;p&gt;The central idea is simple: Python keeps an expressive interface while Rust handles intensive work, memory, and parallelism. That combination makes Polars compelling when a DataFrame is no longer small but we still want the analysis to live close to our code.&lt;/p&gt;&#10;</description></item></channel></rss>