<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>isopov</title><link>https://isopov.github.io/</link><description>Recent content on isopov</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 29 Nov 2024 11:57:39 +0300</lastBuildDate><atom:link href="https://isopov.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Benchmarking memory throughput of 14900K CPU</title><link>https://isopov.github.io/posts/14900-and-memory-throughput-with-jmh/</link><pubDate>Fri, 29 Nov 2024 11:57:39 +0300</pubDate><guid>https://isopov.github.io/posts/14900-and-memory-throughput-with-jmh/</guid><description>&lt;p>In &lt;a href="https://isopov.github.io/posts/14900-with-jmh/" >previous post&lt;/a> I benchmark a heavily CPU-bound code. It may not seem so, however it &lt;a href="https://isopov.github.io/posts/jmc-profiling-cpu-and-memory/" >appears to be memory bound&lt;/a>.
I&amp;rsquo;ve read somewhere that 14900 CPU efficient cores scale worse on memory accesses, than on just cracking numbers. So I wrote the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>@State(Scope.&lt;span style="color:#007f7f">Thread&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@BenchmarkMode(Mode.&lt;span style="color:#007f7f">AverageTime&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Fork(3)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@OutputTimeUnit(TimeUnit.&lt;span style="color:#007f7f">MICROSECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.&lt;span style="color:#007f7f">SECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.&lt;span style="color:#007f7f">SECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">class&lt;/span> ThreadsBenchmark {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Param(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;default&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> String threads;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> BigInteger bench() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> result = BigInteger.&lt;span style="color:#007f7f">ONE&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> i = 1; i &amp;lt;= 2048; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result = result.&lt;span style="color:#007f7f">multiply&lt;/span>(BigInteger.&lt;span style="color:#007f7f">valueOf&lt;/span>(i));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> result;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> main(String[] args) &lt;span style="color:#fff;font-weight:bold">throws&lt;/span> RunnerException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> results = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> ArrayList&amp;lt;RunResult&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> i = 1; i &amp;lt;= 36; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Options opt = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> OptionsBuilder()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">include&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span> + ThreadsBenchmark.&lt;span style="color:#007f7f">class&lt;/span>.&lt;span style="color:#007f7f">getSimpleName&lt;/span>() + &lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">addProfiler&lt;/span>(GCProfiler.&lt;span style="color:#007f7f">class&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">verbosity&lt;/span>(VerboseMode.&lt;span style="color:#007f7f">SILENT&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">param&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;threads&amp;#34;&lt;/span>, String.&lt;span style="color:#007f7f">valueOf&lt;/span>(i))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">threads&lt;/span>(i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">build&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> results.&lt;span style="color:#007f7f">addAll&lt;/span>(&lt;span style="color:#fff;font-weight:bold">new&lt;/span> Runner(opt).&lt;span style="color:#007f7f">run&lt;/span>());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.&lt;span style="color:#007f7f">out&lt;/span>.&lt;span style="color:#007f7f">println&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Finished &amp;#34;&lt;/span> + i);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> OutputFormatFactory.&lt;span style="color:#007f7f">createFormatInstance&lt;/span>(System.&lt;span style="color:#007f7f">out&lt;/span>, Defaults.&lt;span style="color:#007f7f">VERBOSITY&lt;/span>).&lt;span style="color:#007f7f">endRun&lt;/span>(results);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And got the following results:&lt;/p></description></item><item><title>Benchmarking 14900K CPU</title><link>https://isopov.github.io/posts/14900-with-jmh/</link><pubDate>Mon, 25 Nov 2024 22:42:39 +0300</pubDate><guid>https://isopov.github.io/posts/14900-with-jmh/</guid><description>&lt;p>So, I got bored with waiting for tests of my laptop and built myself a new desktop for the first time in 10 years. I&amp;rsquo;ve built it around 14900K CPU and no separate GPU at all. For the OS I use Ubuntu 24.04. And I wanted to look closer to the differences between cores in this CPU. It has 8 performance cores and 16 efficient cores. So I wrote this benchmark to test them.&lt;/p></description></item><item><title>Go concurrent sort of the same slice</title><link>https://isopov.github.io/posts/go-concurrent-sort/</link><pubDate>Mon, 05 Aug 2024 11:55:49 +0300</pubDate><guid>https://isopov.github.io/posts/go-concurrent-sort/</guid><description>&lt;p>Recently I&amp;rsquo;ve stumbled upon a broken cache in my application written in Go. Some slices of structs got from database were
cached in memory. In the hindsight the problem is obvious - every consumer of this cache sorted these slices independently.
And since slices were cached in some cases they were sorting one and the same slice concurrently. Someone may suggest that
sorting should be done in the database with sort by, but even in the simplest case where it can be done on this level simply
adding &lt;code>SORT BY&lt;/code> clause it may be not desirable. Usually application layer is scaled much easier than the storage layer.
Only some newest databases introduce their own scalable computational nodes separate from the storage nodes. Traditionally
you may want to make your queries as lightweight as possible and move some CPU-bound postprocessing to application layer.
Also, you don&amp;rsquo;t want to query your database for one and the same information over and over. So caches are introduced to the
application layer. And here we go with DAO -&amp;gt; Cache -&amp;gt; Provider/Manager.&lt;/p></description></item><item><title>Go GC, generational hypothesis and throughput</title><link>https://isopov.github.io/posts/go-gc-and-old-gen-ballast/</link><pubDate>Mon, 18 Mar 2024 10:10:42 +0300</pubDate><guid>https://isopov.github.io/posts/go-gc-and-old-gen-ballast/</guid><description>&lt;p>In the &lt;a href="https://isopov.github.io/posts/java-gc-and-generations/" >previous post&lt;/a> I investigated effect of old generation objects on Java garbage collectors. Here I want to run similar test for Go language. It is a bit less interesting since there is only one option, but nevertheless some useful insights can be taken out. Like how much RAM to give for GC breathing. Or maybe is it worth giving some extra GBs of RAM to squeeze out last tiny percents of performance. And when adding more RAM starts making worse (in theory it is easy to achieve this especially if you measure your success in terms of latency and not throughput). However, any complex test is better done with more realistic workload. Here I want to check just the simple assumptions and dependencies.&lt;/p></description></item><item><title>Java GC, generational hypothesis and throughput</title><link>https://isopov.github.io/posts/java-gc-and-generations/</link><pubDate>Thu, 30 Nov 2023 15:10:49 +0300</pubDate><guid>https://isopov.github.io/posts/java-gc-and-generations/</guid><description>&lt;p>In recent years Java and Hotspot JVM in particular received several new garbage collectors. Most recently generational
Z collector &lt;a href="https://inside.java/2023/11/28/gen-zgc-explainer/" class="external-link" target="_blank" rel="noopener">was introduced&lt;/a>.&lt;/p>
&lt;p>It is common knowledge that it is possible to achieve almost any results in artificial benchmarks, but nonetheless it may
be interesting to create them anyway.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>@State(Scope.&lt;span style="color:#007f7f">Thread&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Fork(value = 1, jvmArgs = {&lt;span style="color:#0ff;font-weight:bold">&amp;#34;-Xmx100m&amp;#34;&lt;/span>})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Warmup(iterations = 3, time = 1)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Measurement(iterations = 5, time = 1)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">class&lt;/span> GenerationsBenchmark {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">final&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span> OBJECTS_1MB = 1024 * 1024 / 16;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> ArrayList&amp;lt;Object&amp;gt; olds;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Param({&lt;span style="color:#0ff;font-weight:bold">&amp;#34;0&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;20&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;40&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;60&amp;#34;&lt;/span>})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span> oldObjects;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Setup
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> setup() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> olds = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> ArrayList&amp;lt;&amp;gt;(oldObjects * OBJECTS_1MB);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> i = 0; i &amp;lt; oldObjects * OBJECTS_1MB; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> olds.&lt;span style="color:#007f7f">add&lt;/span>(&lt;span style="color:#fff;font-weight:bold">new&lt;/span> Object());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Fork(jvmArgsPrepend = {&lt;span style="color:#0ff;font-weight:bold">&amp;#34;-XX:+UseG1GC&amp;#34;&lt;/span>})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> g1(Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bench(bh);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> bench(Blackhole bh) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> i = 0; i &amp;lt; OBJECTS_1MB; i++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> bh.&lt;span style="color:#007f7f">consume&lt;/span>(&lt;span style="color:#fff;font-weight:bold">new&lt;/span> Object());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> main(String[] args) &lt;span style="color:#fff;font-weight:bold">throws&lt;/span> RunnerException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Options opt = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> OptionsBuilder().&lt;span style="color:#007f7f">include&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span> + GenerationsBenchmark.&lt;span style="color:#007f7f">class&lt;/span>.&lt;span style="color:#007f7f">getSimpleName&lt;/span>() + &lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span>).&lt;span style="color:#007f7f">build&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">new&lt;/span> Runner(opt).&lt;span style="color:#007f7f">run&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>First of all this benchmark is flawed that it is run with 1 fork of each configuration only. But results are quite
stable and difference between configurations is so large, that we may save the time. Also, 100 MB of heap is not a
common size for modern application, so this bench is truly artificial and micro. Some garbage collectors are not
suited for such a small heap, while the others are optimized for this extreme too. So the comparison is not fair at all.
Any judgments based on it should be applicable only in a similar very unusual situation.&lt;/p></description></item><item><title>Java map of maps of maps vs record keys</title><link>https://isopov.github.io/posts/java-map-of-maps-of-maps/</link><pubDate>Thu, 05 Oct 2023 16:10:49 +0300</pubDate><guid>https://isopov.github.io/posts/java-map-of-maps-of-maps/</guid><description>&lt;p>In the &lt;a href="https://isopov.github.io/posts/go-map-of-maps-of-maps/" >previous post&lt;/a> I investigated using single map with struct keys verses using map of maps of maps in Go language. Here I want to run the same test for Java.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>@State(Scope.&lt;span style="color:#007f7f">Thread&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@BenchmarkMode(Mode.&lt;span style="color:#007f7f">AverageTime&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Fork(3)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@OutputTimeUnit(TimeUnit.&lt;span style="color:#007f7f">MICROSECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.&lt;span style="color:#007f7f">SECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.&lt;span style="color:#007f7f">SECONDS&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">class&lt;/span> MapMapMapBenchmark {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> Map&amp;lt;Integer, Map&amp;lt;Integer, Map&amp;lt;Integer, String&amp;gt;&amp;gt;&amp;gt; mapMapMap() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> firsts = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> HashMap&amp;lt;Integer, Map&amp;lt;Integer, Map&amp;lt;Integer, String&amp;gt;&amp;gt;&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test(((first, second, third, value) -&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> seconds = firsts.&lt;span style="color:#007f7f">computeIfAbsent&lt;/span>(first, unused -&amp;gt; &lt;span style="color:#fff;font-weight:bold">new&lt;/span> HashMap&amp;lt;&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> thirds = seconds.&lt;span style="color:#007f7f">computeIfAbsent&lt;/span>(second, unused -&amp;gt; &lt;span style="color:#fff;font-weight:bold">new&lt;/span> HashMap&amp;lt;&amp;gt;());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> thirds.&lt;span style="color:#007f7f">put&lt;/span>(third, value);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> firsts;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> Map&amp;lt;Integer, Map&amp;lt;Integer, Map&amp;lt;Integer, String&amp;gt;&amp;gt;&amp;gt; mapMapMapExact() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> firsts = HashMap.&amp;lt;Integer, Map&amp;lt;Integer, Map&amp;lt;Integer, String&amp;gt;&amp;gt;&amp;gt;newHashMap(FIRSTS);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test(((first, second, third, value) -&amp;gt; {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> seconds = firsts.&lt;span style="color:#007f7f">computeIfAbsent&lt;/span>(first, unused -&amp;gt; HashMap.&lt;span style="color:#007f7f">newHashMap&lt;/span>(SECONDS));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> thirds = seconds.&lt;span style="color:#007f7f">computeIfAbsent&lt;/span>(second, unused -&amp;gt; HashMap.&lt;span style="color:#007f7f">newHashMap&lt;/span>(THIRDS));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> thirds.&lt;span style="color:#007f7f">put&lt;/span>(third, value);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> firsts;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">record&lt;/span> IntKey(&lt;span style="color:#fff;font-weight:bold">int&lt;/span> first, &lt;span style="color:#fff;font-weight:bold">int&lt;/span> second, &lt;span style="color:#fff;font-weight:bold">int&lt;/span> third) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> Map&amp;lt;IntKey, String&amp;gt; singleMap() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> map = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> HashMap&amp;lt;IntKey, String&amp;gt;();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test((first, second, third, value) -&amp;gt; map.&lt;span style="color:#007f7f">put&lt;/span>(&lt;span style="color:#fff;font-weight:bold">new&lt;/span> IntKey(first, second, third), value));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> map;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Benchmark
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> Map&amp;lt;IntKey, String&amp;gt; singleMapExact() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> map = HashMap.&amp;lt;IntKey, String&amp;gt;newHashMap(FIRSTS * SECONDS * THIRDS);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test((first, second, third, value) -&amp;gt; map.&lt;span style="color:#007f7f">put&lt;/span>(&lt;span style="color:#fff;font-weight:bold">new&lt;/span> IntKey(first, second, third), value));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> map;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">interface&lt;/span> Tester {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> accept(&lt;span style="color:#fff;font-weight:bold">int&lt;/span> first, &lt;span style="color:#fff;font-weight:bold">int&lt;/span> second, &lt;span style="color:#fff;font-weight:bold">int&lt;/span> third, String value);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007f7f">//not static, not final to prevent constant folding (see JMHSample_10_ConstantFold)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span> FIRSTS = 100;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span> SECONDS = 10;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span> THIRDS = 10;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> test(Tester tester) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> first = 0; first &amp;lt; FIRSTS; first++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> second = 0; second &amp;lt; SECONDS; second++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">for&lt;/span> (&lt;span style="color:#fff;font-weight:bold">int&lt;/span> third = 0; third &amp;lt; THIRDS; third++) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tester.&lt;span style="color:#007f7f">accept&lt;/span>(first, second, third, first + &lt;span style="color:#0ff;font-weight:bold">&amp;#34;-&amp;#34;&lt;/span> + second + &lt;span style="color:#0ff;font-weight:bold">&amp;#34;-&amp;#34;&lt;/span> + third);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">public&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> main(String[] args) &lt;span style="color:#fff;font-weight:bold">throws&lt;/span> RunnerException {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Options opt = &lt;span style="color:#fff;font-weight:bold">new&lt;/span> OptionsBuilder()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">include&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span> + MapMapMapBenchmark.&lt;span style="color:#007f7f">class&lt;/span>.&lt;span style="color:#007f7f">getSimpleName&lt;/span>() + &lt;span style="color:#0ff;font-weight:bold">&amp;#34;.*&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">addProfiler&lt;/span>(GCProfiler.&lt;span style="color:#007f7f">class&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">build&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">new&lt;/span> Runner(opt).&lt;span style="color:#007f7f">run&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running it I got these results.&lt;/p></description></item><item><title>Cassandra lightweight transactions mixed with ordinary queries</title><link>https://isopov.github.io/posts/cassandra-lwt-queries-mix/</link><pubDate>Thu, 05 Oct 2023 15:10:49 +0300</pubDate><guid>https://isopov.github.io/posts/cassandra-lwt-queries-mix/</guid><description>&lt;p>Recently I&amp;rsquo;ve discovered that adding a simple &amp;ldquo;if exists&amp;rdquo; clause to query in Cassandra turns it into something completely different. Lightweight transaction is started that is not really compatible with running some data modification queries without such clause. I debugged this problem to the simple independent reproducer, so I&amp;rsquo;d like to share it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-java" data-lang="java">&lt;span style="display:flex;">&lt;span>@Testcontainers
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">class&lt;/span> UpdateTest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Container
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">final&lt;/span> CassandraContainer cassandra
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> = (CassandraContainer) &lt;span style="color:#fff;font-weight:bold">new&lt;/span> CassandraContainer(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;cassandra:4.1.3&amp;#34;&lt;/span>).&lt;span style="color:#007f7f">withExposedPorts&lt;/span>(9042);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> CqlSession session;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @BeforeAll
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> setup() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session = CqlSession.&lt;span style="color:#007f7f">builder&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">addContactPoint&lt;/span>(cassandra.&lt;span style="color:#007f7f">getContactPoint&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">withLocalDatacenter&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;datacenter1&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .&lt;span style="color:#007f7f">build&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;drop keyspace if exists updatetests&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;create keyspace updatetests with replication = {&amp;#39;class&amp;#39; : &amp;#39;SimpleStrategy&amp;#39;, &amp;#39;replication_factor&amp;#39; : 1}&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;use updatetests&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;drop table if exists test&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;create table test (a text, b int, primary key(a))&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @AfterAll
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> tearDown() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">close&lt;/span>();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> testLwtInsertAndLwtUpdate() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;insert into test(a,b) values(?,?) if not exists&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;x&amp;#34;&lt;/span>, 1);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;update test set b=? where a=? if exists&amp;#34;&lt;/span>, 2, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;x&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> assertUpdate(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;x&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> testInsertAndLwtUpdate() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;insert into test(a,b) values(?,?)&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;y&amp;#34;&lt;/span>, 1);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;update test set b=? where a=? if exists&amp;#34;&lt;/span>, 2, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;y&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> assertUpdate(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;y&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> @Test
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> testLwtInsertAndUpdate() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;insert into test(a,b) values(?,?) if not exists&amp;#34;&lt;/span>, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;z&amp;#34;&lt;/span>, 1);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;update test set b=? where a=?&amp;#34;&lt;/span>, 2, &lt;span style="color:#0ff;font-weight:bold">&amp;#34;z&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> assertUpdate(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;z&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">private&lt;/span> &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">void&lt;/span> assertUpdate(String id) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">var&lt;/span> res = session.&lt;span style="color:#007f7f">execute&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;select * from test where a=?&amp;#34;&lt;/span>, id);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> res.&lt;span style="color:#007f7f">forEach&lt;/span>(row -&amp;gt; assertEquals(2, row.&lt;span style="color:#007f7f">getInt&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;b&amp;#34;&lt;/span>)));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running this sample only 2 out of 3 tests pass. &lt;code>testInsertAndLwtUpdate&lt;/code> fails. What is even more interesting is that in my tests it reports lightweight transaction applied.&lt;/p></description></item><item><title>Go map of maps of maps vs complex keys</title><link>https://isopov.github.io/posts/go-map-of-maps-of-maps/</link><pubDate>Thu, 05 Oct 2023 13:30:49 +0300</pubDate><guid>https://isopov.github.io/posts/go-map-of-maps-of-maps/</guid><description>&lt;p>In other languages I&amp;rsquo;ve seen single map performing better than a map of maps or even map of maps of maps. I wanted to test which way is better in Go. So I wrote this test:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">package&lt;/span> main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;testing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">type&lt;/span> structID &lt;span style="color:#fff;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	first &lt;span style="color:#fff;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	second &lt;span style="color:#fff;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> Blackholemapmapmap &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> Blackholemapstruct &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[structID]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> BenchmarkName(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;mapmapmap def maps&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			m := &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			test(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(first, second, third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, value &lt;span style="color:#fff;font-weight:bold">string&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				seconds := m[first]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">if&lt;/span> seconds == &lt;span style="color:#fff;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					seconds = &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					m[first] = seconds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				thirds := seconds[second]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">if&lt;/span> thirds == &lt;span style="color:#fff;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					thirds = &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					seconds[second] = thirds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				thirds[third] = value
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Blackholemapmapmap = m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;mapstruct def map&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			m := &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[structID]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			test(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(first, second, third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, value &lt;span style="color:#fff;font-weight:bold">string&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				m[structID{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					first: first,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					second: second,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					third: third,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}] = value
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Blackholemapstruct = m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;mapmapmap exact maps&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			m := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>, firstsCount)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			test(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(first, second, third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, value &lt;span style="color:#fff;font-weight:bold">string&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				seconds := m[first]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">if&lt;/span> seconds == &lt;span style="color:#fff;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					seconds = &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>, secondsCount)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					m[first] = seconds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				thirds := seconds[second]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">if&lt;/span> thirds == &lt;span style="color:#fff;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					thirds = &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>, thirdsCount)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					seconds[second] = thirds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				thirds[third] = value
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Blackholemapmapmap = m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;mapstruct exact map&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			m := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">map&lt;/span>[structID]&lt;span style="color:#fff;font-weight:bold">string&lt;/span>, firstsCount*secondsCount*thirdsCount)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			test(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(first, second, third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, value &lt;span style="color:#fff;font-weight:bold">string&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				m[structID{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					first: first,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					second: second,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					third: third,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}] = value
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			Blackholemapstruct = m
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">const&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	firstsCount = &lt;span style="color:#ff0;font-weight:bold">100&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	secondsCount = &lt;span style="color:#ff0;font-weight:bold">10&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	thirdsCount = &lt;span style="color:#ff0;font-weight:bold">10&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> test(add &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(first, second, third &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, value &lt;span style="color:#fff;font-weight:bold">string&lt;/span>)) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> first := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; first &amp;lt; firstsCount; first++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> second := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; second &amp;lt; secondsCount; second++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#fff;font-weight:bold">for&lt;/span> third := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; third &amp;lt; thirdsCount; third++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				add(first, second, third, fmt.Sprintf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;%d-%d-%d&amp;#34;&lt;/span>, first, second, third))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running it I got these results.&lt;/p></description></item><item><title>Go channel(s) contention</title><link>https://isopov.github.io/posts/go-channel-contention/</link><pubDate>Mon, 27 Mar 2023 20:22:39 +0300</pubDate><guid>https://isopov.github.io/posts/go-channel-contention/</guid><description>&lt;p>In the &lt;a href="https://isopov.github.io/posts/sync-pool-contention/" >previous post&lt;/a> I&amp;rsquo;ve tried to measure overhead from highly contended access to sync.Pool on go. There was some measurable overhead, but it was mild. However in order for another goroutine to recieve some work to be done or/and to publish work results you may need go channel also. In this post I&amp;rsquo;ll try to measure the overhead of contended access to go channel.&lt;/p>
&lt;p>So I&amp;rsquo;ve written the following code&lt;/p></description></item><item><title>Growing goroutine stacks</title><link>https://isopov.github.io/posts/goroutine-stack-grow/</link><pubDate>Fri, 24 Mar 2023 20:22:39 +0300</pubDate><guid>https://isopov.github.io/posts/goroutine-stack-grow/</guid><description>&lt;p>Go allows to run very lightweight goroutines and throw them away (for garbage collector) after they are no longer needed. You can execute any code inside them and this caode can allocate as much as needed. Both on stack and on heap. If goroutine stack is too small it grows. This grow is not free. Suppose you have a widely used function that needs to allocate a bit of memory on stack. Depending on whether there is enough of memory on stack or not it will take different amount of time, since in some cases it will include time to grow the stack. It happens that a really common function time.Now() is allocating. So this effect can be demoed with this function. Consider the following program.&lt;/p></description></item><item><title>Go sync.Pool contention</title><link>https://isopov.github.io/posts/sync-pool-contention/</link><pubDate>Fri, 24 Mar 2023 16:22:39 +0300</pubDate><guid>https://isopov.github.io/posts/sync-pool-contention/</guid><description>&lt;p>Recently my colleague suggested that contention on a single sync.Pool in Go may hit performance. I decided to reproduce this performance hit in a benchmark. This is what I was capable of writing.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;runtime&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;strconv&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;sync&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;sync/atomic&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;testing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> workWithPool(pool *sync.Pool) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">const&lt;/span> size = &lt;span style="color:#ff0;font-weight:bold">1000&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	values := [size]&lt;span style="color:#fff;font-weight:bold">int&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; size; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		values[i] = pool.Get().(&lt;span style="color:#fff;font-weight:bold">int&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; size; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		pool.Put(values[i])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> Benchmark_Pool(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> _, parallelism := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> []&lt;span style="color:#fff;font-weight:bold">int&lt;/span>{&lt;span style="color:#ff0;font-weight:bold">1&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">2&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">3&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">4&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">5&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">10&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">20&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">50&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">100&lt;/span>} {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Parallelism &amp;#34;&lt;/span>+strconv.Itoa(parallelism), &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;one pool&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				b.SetParallelism(parallelism)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				pool := &amp;amp;sync.Pool{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					New: &lt;span style="color:#fff;font-weight:bold">func&lt;/span>() any {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						&lt;span style="color:#fff;font-weight:bold">return&lt;/span> &lt;span style="color:#ff0;font-weight:bold">42&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				b.RunParallel(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(pb *testing.PB) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">for&lt;/span> pb.Next() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						workWithPool(pool)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;many pools&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				b.SetParallelism(parallelism)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				numPools := runtime.GOMAXPROCS(&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>) * parallelism
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				pools := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>([]*sync.Pool, numPools)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; numPools; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					pools[i] = &amp;amp;sync.Pool{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						New: &lt;span style="color:#fff;font-weight:bold">func&lt;/span>() any {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">return&lt;/span> &lt;span style="color:#ff0;font-weight:bold">42&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				procs := &lt;span style="color:#fff;font-weight:bold">uint32&lt;/span>(&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				b.RunParallel(&lt;span style="color:#fff;font-weight:bold">func&lt;/span>(pb *testing.PB) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					poolidx := atomic.LoadUint32(&amp;amp;procs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						&lt;span style="color:#fff;font-weight:bold">if&lt;/span> atomic.CompareAndSwapUint32(&amp;amp;procs, poolidx, poolidx+&lt;span style="color:#ff0;font-weight:bold">1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						poolidx = atomic.LoadUint32(&amp;amp;procs)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">for&lt;/span> pb.Next() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						workWithPool(pools[poolidx])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It seems that I need to comment a bit on the parallelism parameter. It stands for number of goroutines per each thread, while number of threads by default is equal to number of CPU cores. In my case I have 10 cores and thus 10 threads.
Here are the results with some omissions and reformatting to simplify reading.&lt;/p></description></item><item><title>Java Loom Thread Fairness with Noisy Neighbour</title><link>https://isopov.github.io/posts/loom-cooperative-downside/</link><pubDate>Sun, 05 Jun 2022 13:30:49 +0300</pubDate><guid>https://isopov.github.io/posts/loom-cooperative-downside/</guid><description>&lt;p>Recently Gunnar Morling &lt;a href="https://www.morling.dev/blog/loom-and-thread-fairness/" class="external-link" target="_blank" rel="noopener">posted&lt;/a> about Loom thread fairness. However despite the fact that difference between Loom and platform threads was clearly visible, Loom was even better. Total execution time was equal (or should be even better for Loom). But also there was some visible gradual progress.&lt;/p>
&lt;p>However it is possible to get into situation where Loom virtual threads will behave worse than the platform threads. This situation is a pretty common pattern and it is called &lt;a href="https://en.wikipedia.org/wiki/Cloud_computing_issues#Performance_interference_and_noisy_neighbors" class="external-link" target="_blank" rel="noopener">noisy neighbor problem&lt;/a>. Nowadays we usually meet it microservices level, but the general idea is universal. So here is how it can be met inside a single JVM:&lt;/p></description></item><item><title>Java Cryptography Extension Providers Performance</title><link>https://isopov.github.io/posts/jce-benchmarks/</link><pubDate>Sat, 28 May 2022 20:44:46 +0300</pubDate><guid>https://isopov.github.io/posts/jce-benchmarks/</guid><description>&lt;p>The &lt;a href="https://en.wikipedia.org/wiki/Java_Cryptography_Extension" class="external-link" target="_blank" rel="noopener">Java Cryptography Extensions&lt;/a> exist for many years, They allow to use cryptography that is not bundled in the Java platform itself. I&amp;rsquo;ve met &lt;a href="https://www.bouncycastle.org/" class="external-link" target="_blank" rel="noopener">Bouncycastle&lt;/a> crypto provider library many times in projects I worked on. But there are other third party crypto providers as well. At least two of them - &lt;a href="https://github.com/google/conscrypt" class="external-link" target="_blank" rel="noopener">Consrypt&lt;/a> and &lt;a href="https://github.com/corretto/amazon-corretto-crypto-provider" class="external-link" target="_blank" rel="noopener">Corretto&lt;/a> (named the same way as &lt;a href="https://aws.amazon.com/corretto" class="external-link" target="_blank" rel="noopener">Openjdk distribution by Amazon&lt;/a>) declare high performance as their features. I decided to measure these claims and wrote some &lt;a href="https://github.com/isopov/jce-benchmarks" class="external-link" target="_blank" rel="noopener">benchmarks&lt;/a>. To not compare apples with oranges I needed some algorithms that were implemented in all different crypto providers that I tried to test. So the choice may seem a bit strange.&lt;/p></description></item><item><title>Go sync.Pool and gc</title><link>https://isopov.github.io/posts/go-sync-pool-2/</link><pubDate>Tue, 01 Mar 2022 13:10:49 +0300</pubDate><guid>https://isopov.github.io/posts/go-sync-pool-2/</guid><description>&lt;p>I&amp;rsquo;ve posted my brief experiments with sync.Pool in go. However, reading about it guides that it may be hard to use tool. And sometimes it can degrade for third-party reasons.
It is easy to trap into old information regarding sync.Pool and gc interrogation. &lt;a href="https://github.com/golang/go/commit/2dcbf8b3691e72d1b04e9376488cef3b6f93b286" class="external-link" target="_blank" rel="noopener">Since go 1.13 sync.Pool is not cleared completely on every gc&lt;/a>. However, it is still affected by gc. Suppose the following code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">type&lt;/span> Obj []&lt;span style="color:#fff;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> (o *Obj) Fill() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; &lt;span style="color:#fff;font-weight:bold">cap&lt;/span>(*o); i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		*o = &lt;span style="color:#fff;font-weight:bold">append&lt;/span>(*o, i)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> (o Obj) Count() &lt;span style="color:#fff;font-weight:bold">int&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	result := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; &lt;span style="color:#fff;font-weight:bold">len&lt;/span>(o); i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		result += o[i]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">return&lt;/span> result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> NewObj() *Obj {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	o := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(Obj, &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">100&lt;/span>*&lt;span style="color:#ff0;font-weight:bold">1024&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">return&lt;/span> &amp;amp;o
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> objPool = &amp;amp;sync.Pool{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	New: &lt;span style="color:#fff;font-weight:bold">func&lt;/span>() &lt;span style="color:#fff;font-weight:bold">interface&lt;/span>{} {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">return&lt;/span> NewObj()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> ResultTrap &lt;span style="color:#fff;font-weight:bold">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> WorkWithPool() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	o := objPool.Get().(*Obj)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	o.Fill()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	ResultTrap = o.Count()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	released := (*o)[:&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	objPool.Put(&amp;amp;released)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And let&amp;rsquo;s use it this way:&lt;/p></description></item><item><title>Go sync.Pool</title><link>https://isopov.github.io/posts/go-sync-pool/</link><pubDate>Fri, 25 Feb 2022 13:30:49 +0300</pubDate><guid>https://isopov.github.io/posts/go-sync-pool/</guid><description>&lt;p>Recently I&amp;rsquo;ve seen sync.Pools in various Go libraries. Sometimes I wondered wether it is worth using at all. So I wrote some tests. Here are they:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">package&lt;/span> main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;crypto/sha256&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;hash&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;strings&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;sync&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;testing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> shaPool = &amp;amp;sync.Pool{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	New: &lt;span style="color:#fff;font-weight:bold">func&lt;/span>() &lt;span style="color:#fff;font-weight:bold">interface&lt;/span>{} {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">return&lt;/span> sha256.New()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> HashTrap hash.Hash
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> Source = []&lt;span style="color:#fff;font-weight:bold">byte&lt;/span>(strings.ToLower(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;asdfavzxcvsdfqawesfdz&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">var&lt;/span> ResultTrap []&lt;span style="color:#fff;font-weight:bold">byte&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> Benchmark(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Pool&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			HashTrap = shaPool.Get().(hash.Hash)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ResultTrap = HashTrap.Sum(Source)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			HashTrap.Reset()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			shaPool.Put(HashTrap)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;NoPool&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			HashTrap = sha256.New()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ResultTrap = HashTrap.Sum(Source)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	b.Run(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Local&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		b.ReportAllocs()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			h := sha256.New()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			ResultTrap = h.Sum(Source)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And the results:&lt;/p></description></item><item><title>Go scheduler fairness</title><link>https://isopov.github.io/posts/go-scheduler-fairness/</link><pubDate>Fri, 02 Apr 2021 18:30:49 +0300</pubDate><guid>https://isopov.github.io/posts/go-scheduler-fairness/</guid><description>&lt;p>Recently in Jaeger I stumbled at an interesting trace - the work was done for 1 second, than 20 there was a gap for 20 seconds and after that work continued. My best guess currently is that goroutine slept for 20 seconds. To reproduce it I wrote the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">package&lt;/span> main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;runtime&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;sync/atomic&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;github.com/couchbaselabs/ghistogram&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	runtime.GOMAXPROCS(&lt;span style="color:#ff0;font-weight:bold">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	ctx, cancel := context.WithCancel(context.Background())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	workers := &lt;span style="color:#ff0;font-weight:bold">300&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	done := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>([]&lt;span style="color:#fff;font-weight:bold">chan&lt;/span> &lt;span style="color:#fff;font-weight:bold">bool&lt;/span>, workers)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; workers; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		done[i] = &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">chan&lt;/span> &lt;span style="color:#fff;font-weight:bold">bool&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	times := ghistogram.NewHistogram(&lt;span style="color:#ff0;font-weight:bold">10&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">5000&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	minCount := &lt;span style="color:#fff;font-weight:bold">int64&lt;/span>(&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	maxCount := &lt;span style="color:#fff;font-weight:bold">int64&lt;/span>(&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; workers; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		doneChannel := done[i]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		count := &lt;span style="color:#fff;font-weight:bold">int64&lt;/span>(&lt;span style="color:#ff0;font-weight:bold">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">go&lt;/span> &lt;span style="color:#fff;font-weight:bold">func&lt;/span>(ctx context.Context) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			prev := time.Now()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#fff;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">select&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">case&lt;/span> &amp;lt;-ctx.Done():
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">if&lt;/span> count == &lt;span style="color:#ff0;font-weight:bold">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						&lt;span style="color:#fff;font-weight:bold">println&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Boom!&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						savedMinCount := minCount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						&lt;span style="color:#fff;font-weight:bold">if&lt;/span> count &amp;lt; savedMinCount || savedMinCount == &lt;span style="color:#ff0;font-weight:bold">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">if&lt;/span> atomic.CompareAndSwapInt64(&amp;amp;minCount, savedMinCount, count) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>								&lt;span style="color:#fff;font-weight:bold">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						} &lt;span style="color:#fff;font-weight:bold">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">for&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						savedMaxCount := maxCount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						&lt;span style="color:#fff;font-weight:bold">if&lt;/span> count &amp;gt; savedMaxCount {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">if&lt;/span> atomic.CompareAndSwapInt64(&amp;amp;maxCount, savedMaxCount, count) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>								&lt;span style="color:#fff;font-weight:bold">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						} &lt;span style="color:#fff;font-weight:bold">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>							&lt;span style="color:#fff;font-weight:bold">break&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>						}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">close&lt;/span>(doneChannel)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					&lt;span style="color:#fff;font-weight:bold">return&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				&lt;span style="color:#fff;font-weight:bold">default&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					next := time.Now()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					dif := next.Sub(prev)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					times.Add(&lt;span style="color:#fff;font-weight:bold">uint64&lt;/span>(dif), &lt;span style="color:#ff0;font-weight:bold">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					prev = next
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>					count++
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>				}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}(ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	time.Sleep(&lt;span style="color:#ff0;font-weight:bold">30&lt;/span> * time.Second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	cancel()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; workers; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&amp;lt;-done[i]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">println&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Times&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i, r := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> times.Ranges {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		fmt.Printf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;%s - %d\n&amp;#34;&lt;/span>, time.Duration(r), times.Counts[i])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	fmt.Printf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;min - %s\n&amp;#34;&lt;/span>, time.Duration(times.MinDataPoint))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	fmt.Printf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;max - %s\n&amp;#34;&lt;/span>, time.Duration(times.MaxDataPoint))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">println&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;Counts&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	fmt.Printf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;min - %d\n&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">int&lt;/span>(minCount))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	fmt.Printf(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;max - %d\n&amp;#34;&lt;/span>, &lt;span style="color:#fff;font-weight:bold">int&lt;/span>(maxCount))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this code I start 300 goroutines in 1 thread and try to iterate without any sleeps inside each goroutine. The only thing that is done in those loops - recording time of the iteration and storing it in histogram.
Output is:&lt;/p></description></item><item><title>Moving work to another goroutine</title><link>https://isopov.github.io/posts/moving-work-to-goroutine/</link><pubDate>Tue, 30 Mar 2021 12:25:42 +0300</pubDate><guid>https://isopov.github.io/posts/moving-work-to-goroutine/</guid><description>&lt;p>Concurency in Go is super easy. Still to write it inside my very large work project I first needed to get my hands with it on really small and simple example. So I wrote one and it seems that this blog is a perfect place to store it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">package&lt;/span> main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> main() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	worker := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">chan&lt;/span> &lt;span style="color:#fff;font-weight:bold">int&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">10&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	done := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>(&lt;span style="color:#fff;font-weight:bold">chan&lt;/span> &lt;span style="color:#fff;font-weight:bold">bool&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">go&lt;/span> &lt;span style="color:#fff;font-weight:bold">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">for&lt;/span> data := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> worker {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			time.Sleep(&lt;span style="color:#ff0;font-weight:bold">50&lt;/span> * time.Millisecond)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>			&lt;span style="color:#fff;font-weight:bold">println&lt;/span>(data)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		&lt;span style="color:#fff;font-weight:bold">close&lt;/span>(done)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">1&lt;/span>; i &amp;lt;= &lt;span style="color:#ff0;font-weight:bold">20&lt;/span>; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		worker &amp;lt;- i
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">close&lt;/span>(worker)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">println&lt;/span>(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;done settings tasks&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&amp;lt;-done
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The only thing that is too simplistic here is that we have only one worker goroutine. So let&amp;rsquo;s make some more - we need a separate bool channel to track finish.&lt;/p></description></item><item><title>Updating value in cache or simply invalidate it instead</title><link>https://isopov.github.io/posts/cache-update-or-invalidate/</link><pubDate>Wed, 04 Nov 2020 10:51:48 +0300</pubDate><guid>https://isopov.github.io/posts/cache-update-or-invalidate/</guid><description>&lt;p>Recently I worked on a process of updating many entities in DB. And since these entities are read really often, there was a Redis-based cache in front of DB. After updating the entity in DB it was updated in the cache. And to be safe the whole process was under Redsic-based lock. So:&lt;/p>
&lt;ul>
&lt;li>Lock for the entity is taken in Redis&lt;/li>
&lt;li>Entity is updated in DB&lt;/li>
&lt;li>Entity is updated in Redis&lt;/li>
&lt;li>Lost is released in Redis&lt;/li>
&lt;/ul>
&lt;p>Everything is good and data is consistent everywhere, but this process is too long. Lock acquire and release are the longest steps. But if we simply remove the locks data may become inconsistent between the DB and Redis. To craft such a sample is a too big task for a blog post, but a very similar concept can be recreated with any technology and at any level. In Java, there is &lt;a href="https://github.com/openjdk/jcstress" class="external-link" target="_blank" rel="noopener">Jcstress&lt;/a> - an excellent tool to dive into concurrency problems. Let&amp;rsquo;s create a sample with it:&lt;/p></description></item><item><title>Profiling Java CPU and RAM with Mission Control</title><link>https://isopov.github.io/posts/jmc-profiling-cpu-and-memory/</link><pubDate>Sun, 01 Nov 2020 16:34:58 +0300</pubDate><guid>https://isopov.github.io/posts/jmc-profiling-cpu-and-memory/</guid><description>&lt;p>Some time ago I was a lecturer on Java courses and wanted to craft a sample for profiling Java applications. Currently, we have plenty of profilers, and more than that - many of them are completely free to use. One of them is &lt;a href="https://openjdk.java.net/projects/jmc/" class="external-link" target="_blank" rel="noopener">Java Mission Control&lt;/a>. It was a commercial offering with a limited free version, but now it is completely free and open-sourced. It was required to buy it to use in production back in the days and this alone should give a hint, that this profiler is so lightweight that it can be used in production. Now you can download builds of JMC from different vendors. I downloaded one from &lt;a href="https://www.azul.com/products/zulu-mission-control/" class="external-link" target="_blank" rel="noopener">Azul&lt;/a>.&lt;/p></description></item><item><title>Using map[]struct{} or map[]bool in Golang</title><link>https://isopov.github.io/posts/map-bool-or-struct/</link><pubDate>Sun, 01 Nov 2020 13:35:52 +0300</pubDate><guid>https://isopov.github.io/posts/map-bool-or-struct/</guid><description>&lt;p>In Golang you do not have set data structure. So you can use map instead. When you do not need values there are two options widely used - using empty &lt;code>struct{}&lt;/code> or &lt;code>bool&lt;/code> as value. &lt;code>struct{}&lt;/code> should be more performant, while &lt;code>bool&lt;/code> is more convinient. I decided to check the first promise myslef. Golang have benchamrks support in its standard library. SO I wrote this one:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">package&lt;/span> mapstruct
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;math/rand&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#0ff;font-weight:bold">&amp;#34;testing&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> input() []&lt;span style="color:#fff;font-weight:bold">int&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	slice := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>([]&lt;span style="color:#fff;font-weight:bold">int&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">10_000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; &lt;span style="color:#ff0;font-weight:bold">10_000&lt;/span>; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		slice[i] = rand.Intn(&lt;span style="color:#ff0;font-weight:bold">1_000&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">return&lt;/span> slice
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> uniqueBool(input []&lt;span style="color:#fff;font-weight:bold">int&lt;/span>) []&lt;span style="color:#fff;font-weight:bold">int&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	vals := &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">bool&lt;/span>{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> _, v := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> input {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		vals[v] = &lt;span style="color:#fff;font-weight:bold">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	result := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>([]&lt;span style="color:#fff;font-weight:bold">int&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>, &lt;span style="color:#fff;font-weight:bold">len&lt;/span>(vals))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> v := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> vals {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		result = &lt;span style="color:#fff;font-weight:bold">append&lt;/span>(result, v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">return&lt;/span> result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> uniqueStruct(input []&lt;span style="color:#fff;font-weight:bold">int&lt;/span>) []&lt;span style="color:#fff;font-weight:bold">int&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	vals := &lt;span style="color:#fff;font-weight:bold">map&lt;/span>[&lt;span style="color:#fff;font-weight:bold">int&lt;/span>]&lt;span style="color:#fff;font-weight:bold">struct&lt;/span>{}{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> _, v := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> input {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		vals[v] = &lt;span style="color:#fff;font-weight:bold">struct&lt;/span>{}{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	result := &lt;span style="color:#fff;font-weight:bold">make&lt;/span>([]&lt;span style="color:#fff;font-weight:bold">int&lt;/span>, &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>, &lt;span style="color:#fff;font-weight:bold">len&lt;/span>(vals))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> v := &lt;span style="color:#fff;font-weight:bold">range&lt;/span> vals {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		result = &lt;span style="color:#fff;font-weight:bold">append&lt;/span>(result, v)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">return&lt;/span> result
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> BenchmarkBool(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		uniqueBool(input())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff;font-weight:bold">func&lt;/span> BenchmarkStruct(b *testing.B) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	&lt;span style="color:#fff;font-weight:bold">for&lt;/span> i := &lt;span style="color:#ff0;font-weight:bold">0&lt;/span>; i &amp;lt; b.N; i++ {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>		uniqueStruct(input())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>	}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And the result are quite variadic (Should developers write their own tooling to run benchamrks multiple times and compute averages, variances, etc?) Here are two results from different runs:&lt;/p></description></item><item><title>About</title><link>https://isopov.github.io/about/</link><pubDate>Sat, 31 Oct 2020 17:51:59 +0300</pubDate><guid>https://isopov.github.io/about/</guid><description>&lt;p>My name is Ivan Sopov. I am software developer working mostly with Java, Golang, various databases and similar things. You can find me on:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/isopov" class="external-link" target="_blank" rel="noopener">Github&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/i_sopov" class="external-link" target="_blank" rel="noopener">Twitter&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.linkedin.com/in/ivan-sopov-74623b28/" class="external-link" target="_blank" rel="noopener">LinkedIn&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>CTE Oracle vs Postgres</title><link>https://isopov.github.io/posts/cte-oracle-vs-postgres/</link><pubDate>Sat, 15 Sep 2018 15:37:17 +0300</pubDate><guid>https://isopov.github.io/posts/cte-oracle-vs-postgres/</guid><description>&lt;p>UPDATE(2021-11-08): Described here has changed in &lt;a href="https://www.postgresql.org/docs/12/queries-with.html" class="external-link" target="_blank" rel="noopener">Postgres 12&lt;/a> (more recent versions have even better documentation on this).&lt;/p>
&lt;p>Major part of my career to the moment passed working with databases and mostly with Oracle and Postgres. Several years ago I was really surprised by the difference in handling CTE (common table expressions) in these databases. From that time I spread this knowledge and recreated a sample for it several times. The last one was this week and I decided to store it somewhere - this mostly dead blog seems like a good place ;-)
So here is a really simple case of using CTE on Oracle.&lt;/p></description></item><item><title>Benchmarking an interview question</title><link>https://isopov.github.io/posts/benchmarking-add-exact/</link><pubDate>Sat, 15 Aug 2015 23:22:39 +0300</pubDate><guid>https://isopov.github.io/posts/benchmarking-add-exact/</guid><description>&lt;p>&lt;em>Author benchmarks a super-simple method with a dumb implementation from his recent job interview. Results of this benchmark contradict with assumptions. The process of benchmarking produces some public benefit.&lt;/em>&lt;/p>
&lt;p>So, on a recent job interview it was suggested to write implementation for a really simple method&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>public int sum(int x, int y);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The main question for implementation is surely dealing with integer overflow (we were speaking about Java programming language). As it was left for me to decide what to do in case of overflow I suggested a standard way - throwing exception. In Java8 there is a standard method that does exactly this - Math.addExact. However I needed to re-implement it myself in order to use on older versions. To ease possible migration I used the same ArithmeticException that is used in the 8th version of standard library.&lt;/p></description></item><item><title>Learning Vaadin7 Review</title><link>https://isopov.github.io/posts/learning-vaadin7-review/</link><pubDate>Sat, 28 Dec 2013 17:42:46 +0300</pubDate><guid>https://isopov.github.io/posts/learning-vaadin7-review/</guid><description>&lt;p>Some time ago I was proposed to review the second edition of the &lt;a href="http://www.packtpub.com/learning-vaadin-7-second-edition/book" class="external-link" target="_blank" rel="noopener">&amp;ldquo;Learning Vaadin 7&amp;rdquo;&lt;/a> (&lt;a href="http://www.amazon.com/Learning-Vaadin-7-Second-Edition/dp/1782169776" class="external-link" target="_blank" rel="noopener">On Amazon&lt;/a>) book. Shame on me for holding it for so long. Previously I have used Vaadin 6 and was really impressed by its features and simplicity of doing real desktop-like web-applications. But at the same time every now and then I was frustrated by some of the methods and mostly by the methods returning plain Objects. Fortunately now these type-safety problems are solved with the Vaadin 7.
But what about the book? It seems as really good introduction to Vaadin for junior developers:&lt;/p></description></item><item><title>Github vs Bitbucket</title><link>https://isopov.github.io/posts/github-vs-bitbucket/</link><pubDate>Thu, 09 May 2013 09:30:30 +0300</pubDate><guid>https://isopov.github.io/posts/github-vs-bitbucket/</guid><description>&lt;p>&lt;a href="https://github.com/" class="external-link" target="_blank" rel="noopener">Github&lt;/a> totally owns the open-source movement today. And this situation impacts enterprise development. But as far as I can tell &lt;a href="https://bitbucket.org/" class="external-link" target="_blank" rel="noopener">Bitbucket&lt;/a> is much better for proprietary development in private repositories. Here are the arguments:&lt;/p>
&lt;ul>
&lt;li>Option to &lt;a href="https://bitbucket.org/site/master/issue/3338/git-allow-option-to-enable-disable-force" class="external-link" target="_blank" rel="noopener">prevent non-fast-forward pushes&lt;/a>. It even allows to prevent non-fast-forward and still allowing removal of feature-branches! As far as I know, this may require a bit of configuration in standalone git repositories. &lt;del>No such option on Github&lt;/del>. &lt;strong>UPDATE:&lt;/strong> &lt;a href="https://twitter.com/cobyism/status/332159018019733505" class="external-link" target="_blank" rel="noopener">Requires email&lt;/a> to support@at@github.com but the response is fast - even in my not straightforward case.&lt;/li>
&lt;li>Pricing model. &lt;a href="https://github.com/plans" class="external-link" target="_blank" rel="noopener">Github offers&lt;/a> unlimited collaborators form the start and additional private repos for additional money. &lt;a href="https://bitbucket.org/plans" class="external-link" target="_blank" rel="noopener">Bitbucket offers&lt;/a> unlimited private repos from the start and additional collaborators for additional money. I think the latter option is better - division of project in modules residing in separate repositories is the technical task. And technical decision person should have freedom to do the best. Technical staff usually does not make financial decisions. And extending the team is really that kind of decision that could not be made without considering financial side. So I think if choosing what side of the plan to make dependent on money - number of collaborators is better because without any Github or Bitbucket this side does depend on money. And number of repositories, if not made artificially dependent on money, does not depend on money at all.&lt;/li>
&lt;/ul>
&lt;p>So there are only two of them - &lt;del>but the first I would consider as a show-stopper, and&lt;/del> the second really annoys leading to several unrelated projects in the single repository.&lt;/p></description></item><item><title>Use newer Java FTW!</title><link>https://isopov.github.io/posts/use-newer-java-ftw/</link><pubDate>Fri, 05 Apr 2013 09:44:46 +0300</pubDate><guid>https://isopov.github.io/posts/use-newer-java-ftw/</guid><description>&lt;p>One of my recent tasks was trying to convince client, that we should target the 7th version of the JVM, and not the 5th. I failed. Actually I don&amp;rsquo;t think that such task is possible unless there are no any real reasons in using older Java. Nevertheless here is the text, that I wrote.&lt;/p>
&lt;p>What Java implementation are we going to use? The most used one is Oracle.
Oracle Java5 reached its official end of life in Oct 2009
Oracle Java6 reached its official end of life in Feb 2013 (there will be no public updates anymore, nor any security updates)&lt;/p></description></item><item><title>Order of Enums in Hashmap</title><link>https://isopov.github.io/posts/order-of-enums-in-hashmap/</link><pubDate>Fri, 10 Feb 2012 09:50:33 +0300</pubDate><guid>https://isopov.github.io/posts/order-of-enums-in-hashmap/</guid><description>&lt;p>Recently I have found myself writing a complex sql-builder for two weeks already. Of cause I used &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" class="external-link" target="_blank" rel="noopener">TDD&lt;/a> technique and this was much less like a pain comparing to the times when I didn&amp;rsquo;t wrote any tests. In some moment I decided that just operating with strings in some thousand or more lines of code is not very cool and started to write some &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" class="external-link" target="_blank" rel="noopener">OOD&lt;/a> for the problem. In the resulting type hierarchy there were some enums. And I used these enums as keys in HashMap. Maybe some curious reader have already guessed what this post is about ;-) If not or if you just want some detailed illustration, read the rest:&lt;/p></description></item><item><title>ForkJoin factorial</title><link>https://isopov.github.io/posts/forkjoin-factorial/</link><pubDate>Sun, 09 Oct 2011 10:05:54 +0300</pubDate><guid>https://isopov.github.io/posts/forkjoin-factorial/</guid><description>&lt;p>Several months ago I wrote a &lt;a href="https://isopov.github.io/posts/multithreaded-factorial/" >multithreaded factorial&lt;/a> method. It was very simple from the point of view of the underlying technology, but not so trivial from the point of view of synchronizing threads. It used simple start() and join() methods that are available since the Java 1.0 And than I thought that with all the power of java I can improve it. So i used the ThreadPoolExecutor - a piece of technology from the Java 5 and really improved - here is my post about &lt;a href="https://isopov.github.io/posts/multithreaded-factorial-with-threadpoolexecutor/" >multithreaded factorial using TreadPoolExecutor&lt;/a>.&lt;/p></description></item><item><title>Multithreaded factorial with ThreadPoolExecutor</title><link>https://isopov.github.io/posts/multithreaded-factorial-with-threadpoolexecutor/</link><pubDate>Fri, 22 Jul 2011 10:00:17 +0300</pubDate><guid>https://isopov.github.io/posts/multithreaded-factorial-with-threadpoolexecutor/</guid><description>&lt;p>&lt;strong>Update&lt;/strong>: &lt;a href="https://isopov.github.io/posts/forkjoin-factorial/" >the follow up post&lt;/a> gives another version using newly introduced API.&lt;/p>
&lt;p>In the &lt;a href="https://isopov.github.io/posts/multithreaded-factorial/" >previous post&lt;/a> I have written a multithreaded factorial. It was not really good, because last one of the worker threads worked significantly longer than the first one. Fortunately we are living in 2011 and Java 1.5 was released so long time ago. The solution is simple - do not create 4 threads of the 4 virtual cores processors. Create really many threads and let the standard concurrent library execute them all. So ok, here is the code for this:&lt;/p></description></item><item><title>Multithreaded factorial</title><link>https://isopov.github.io/posts/multithreaded-factorial/</link><pubDate>Sun, 03 Jul 2011 09:55:51 +0300</pubDate><guid>https://isopov.github.io/posts/multithreaded-factorial/</guid><description>&lt;p>&lt;strong>Update&lt;/strong>: &lt;a href="https://isopov.github.io/posts/multithreaded-factorial-with-threadpoolexecutor/" >the follow up post&lt;/a> gives another version, that lacks the drawback of unequal load on processor cores.&lt;/p>
&lt;p>There is a very good blog post about &lt;a href="https://chaosinmotion.com/blog/?p=622" class="external-link" target="_blank" rel="noopener">how to write the factorial&lt;/a> method in Java (really it is not about righting factorial). However, even in this master peace;-) there is no multi-threaded factorial. So here it is.
There are some problems with it. The most significant is that multiplying all the BigIntegers from 1 to 50000 is much less heavy job for the computer, than multiplying all BigIntegers from 50001 to 100000. So the the amount of work that is done by each worker-thread is not equal. But investigation on how this may be resolved in a common way is much harder problem, than just writing what I am publishing here. Maybe I&amp;rsquo;ll do it in the other post.&lt;/p></description></item><item><title>Not symmetric equals after override</title><link>https://isopov.github.io/posts/not-symmetric-equals-after-override/</link><pubDate>Sat, 30 Apr 2011 10:23:54 +0300</pubDate><guid>https://isopov.github.io/posts/not-symmetric-equals-after-override/</guid><description>&lt;p>There is really cool tool &lt;a href="http://findbugs.sourceforge.net/" class="external-link" target="_blank" rel="noopener">FindBugs&lt;/a> and recently I found that it can discover (and discovered such a situation in my experience) &lt;a href="http://findbugs.sourceforge.net/bugDescriptions.html#EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC" class="external-link" target="_blank" rel="noopener">broken symmetry for equals&lt;/a> method when overriding it. So this is just a little example of this bug. (HashCode method is generated and I assume it to be all right).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span style="display:flex;">&lt;span>public &lt;span style="color:#fff;font-weight:bold">class&lt;/span> BrokenEqualsTest {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public &lt;span style="color:#fff;font-weight:bold">static&lt;/span> void main(String[] args) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Test1 test1 = new Test1();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test1.field = &lt;span style="color:#0ff;font-weight:bold">&amp;#34;test&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Test2 test2 = new Test2();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ((Test1) test2).field = &lt;span style="color:#0ff;font-weight:bold">&amp;#34;test&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test2.field2 = &lt;span style="color:#0ff;font-weight:bold">&amp;#34;test&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;test1 equals test2 - &amp;#34;&lt;/span> + test1.equals(test2));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> System.out.println(&lt;span style="color:#0ff;font-weight:bold">&amp;#34;test2 equals test1 - &amp;#34;&lt;/span> + test2.equals(test1));
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">class&lt;/span> Test1 {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private String field;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f00">@&lt;/span>Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public int hashCode() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> final int prime = &lt;span style="color:#ff0;font-weight:bold">31&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int result = &lt;span style="color:#ff0;font-weight:bold">1&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result = prime * result + ((field == null) &lt;span style="color:#f00">?&lt;/span> &lt;span style="color:#ff0;font-weight:bold">0&lt;/span> : field.hashCode());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> result;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f00">@&lt;/span>Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public boolean equals(Object obj) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">if&lt;/span> (obj instanceof Test1) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> field.equals(((Test1) obj).field);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> false;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private &lt;span style="color:#fff;font-weight:bold">static&lt;/span> &lt;span style="color:#fff;font-weight:bold">class&lt;/span> Test2 &lt;span style="color:#fff;font-weight:bold">extends&lt;/span> Test1 {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> private String field2;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f00">@&lt;/span>Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public int hashCode() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> final int prime = &lt;span style="color:#ff0;font-weight:bold">31&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> int result = super.hashCode();
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> result = prime * result + ((field2 == null) &lt;span style="color:#f00">?&lt;/span> &lt;span style="color:#ff0;font-weight:bold">0&lt;/span> : field2.hashCode());
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> result;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f00">@&lt;/span>Override
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> public boolean equals(Object obj) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">if&lt;/span> (obj instanceof Test2) { //here is the error reported by FindBugs
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> field2.equals(((Test2) obj).field2);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#fff;font-weight:bold">return&lt;/span> false;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output of running main of this class is:&lt;/p></description></item><item><title>How to change sid of Oracle Express (XE)</title><link>https://isopov.github.io/posts/change-oracle-express-sid/</link><pubDate>Sat, 30 Apr 2011 10:12:28 +0300</pubDate><guid>https://isopov.github.io/posts/change-oracle-express-sid/</guid><description>&lt;p>No way.&lt;/p></description></item><item><title>Collisions of hashcodes - much more real than an md5 collision</title><link>https://isopov.github.io/posts/identity-hashcode-collisions/</link><pubDate>Sat, 26 Mar 2011 10:19:24 +0300</pubDate><guid>https://isopov.github.io/posts/identity-hashcode-collisions/</guid><description>&lt;p>So okay, hashcode is int and so there are only 4294967296 distinct hashcodes. It is not a little number, but in modern systems there could be definitely much more objects.&lt;/p>
&lt;p>In a &lt;a href="http://www.javapuzzlers.com/" class="external-link" target="_blank" rel="noopener">very good book&lt;/a> by Joshua Bloch I have read that there is a probability that two consequently created objects will have an equal system identity hashcode. Actually from the time of publishing that book major version of Java was released and the situation has improved dramatically. But still 232 is a very little number for objects. And still two consequently created objects may have an equal identity hashcode:&lt;/p></description></item></channel></rss>