{"id":670,"date":"2021-04-07T21:56:16","date_gmt":"2021-04-07T21:56:16","guid":{"rendered":"https:\/\/uqcodewall.com\/?page_id=670"},"modified":"2023-11-12T14:48:42","modified_gmt":"2023-11-12T14:48:42","slug":"profiling","status":"publish","type":"page","link":"https:\/\/uqcodewall.com\/?page_id=670","title":{"rendered":"Profiling"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">It is essential to understand what is slow before starting to optimize. And understanding the slow pieces in code requires profiling the code to figure out the hot spots and making efforts appropriately to optimize slowness inside the hotspots.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I have been writing code and designing systems for two decades now. Even with this experience, when profiling via systematic tools, I see hotspots that I had not initially expected. Thus just using the gut or briefly exploring the logs isn&#8217;t sufficient to decide what to optimize.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below I am pointing out some tools that I have used in profiling java services:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here are some general tips on how you should profile:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Profile on a host with similar hardware to the production host. Don&#8217;t profile using your laptop or workstation, even though that may be more convenient.<\/li><li>Warm the service before profiling. Profiling from a cold start will give you inaccurate data.<\/li><li>Profile using production data so that the profiler reflects coverage, CPU time, and memory usage based on the production load.<\/li><li>I prefer profiling via sampling to understand the hot spots and only run profiling via instrumentation on specific routines to dig in deeper. Instrumentation adds significant overhead on execution and can skew the numbers if looking from a bird&#8217;s eye&#8217;s view.<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><span style=\"text-decoration: underline;\"><strong>CPU profiling<\/strong><\/span><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">See below the execution call tree under-sampling. This diagram indicates that the hot spots are in NettingExecutor and ClassificationEvaluator. Later we spent more time instrumenting the code and figuring patterns in the processed data. In this case, we realized below optimizations were needed:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Modify the algorithm to reduce the number of redundant steps<\/li><li>Memorize previously calculated values and write them into a temporary cache to avoid redundant calculations.<\/li><li>We modified the data structure and algorithm in use here. Some of our routines were using a treeset that would internally sort the data. We changed it to a list on which we wrote some code to prune the data, we didn&#8217;t need all the data, and then we sorted the list via timsort.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"690\" class=\"wp-image-672\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling.jpg 1638w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling-300x207.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling-1024x706.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling-768x530.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/CPUProfiling-1536x1060.jpg 1536w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\"><strong>Bring cache size from 12GB to 4GB<\/strong><\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our application was using a lot of memory. We needed to understand where the memory is going and realized that a few in-memory caches were the biggest culprits per the below diagram. We later modified the objects holding the data to reduce memory usage and shared the references between objects using an object pool. With these changes, we could bring the startup memory of the application down to less than 4GB from over 12GB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"269\" class=\"wp-image-677\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects.jpg 2557w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects-300x81.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects-1024x276.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects-768x207.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects-1536x413.jpg 1536w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingBigObjects-2048x551.jpg 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\">Understanding allocation rates of objects per execution<\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Our application was designed as a stateless service that worked on large data sets where it initialized the data internally, worked on it, and then released the memory. These actions were resulting in frequent GC spikes and pauses. We looked at the allocation call tree to understand areas that executed the most frequent allocations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"379\" class=\"wp-image-674\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingAllocationCallTree.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingAllocationCallTree.jpg 1380w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingAllocationCallTree-300x114.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingAllocationCallTree-1024x388.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryProfilingAllocationCallTree-768x291.jpg 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is another way to look at the allocations to check the objects allocated between two different execution times. As per the below diagram that shows the number of allocations between two times, we realized that we frequently pulled into the process memory a large object over pub-sub. Per below, parsing and transforming that object to POJO took many allocations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"200\" class=\"wp-image-676\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1.jpg 2558w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1-300x60.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1-1024x205.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1-768x154.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1-1536x307.jpg 1536w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/MemoryHotSpotsBetweenTwoTimes-1-2048x410.jpg 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\">JVisual VM<\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is another excellent and simple tool that shows a high-level overview of memory, threads, and CPU.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"497\" class=\"wp-image-678\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm.jpg 2275w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm-300x149.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm-1024x509.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm-768x382.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm-1536x764.jpg 1536w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/JVisual-Vm-2048x1018.jpg 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\">Flight recorder<\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Flight recorder is an excellent tool available via oracle JDK. The below image shows GC activity between two different times and, in addition, the length of GC pauses. This information offers a bird&#8217;s eye overview of JVM memory and GC activity.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"576\" class=\"wp-image-679\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder.jpg 1966w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder-300x173.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder-1024x590.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder-768x443.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorder-1536x885.jpg 1536w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\">Understanding TLAB activity<\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The below diagram shows several objects inside and outside TLAB while connecting to JVM under load. Per below, we see a significant number of objects allocated outside TLAB. Note the slowness in allocations when objects are allocated outside of TLAB.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"237\" class=\"wp-image-680\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo.jpg 1874w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo-300x71.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo-1024x243.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo-768x182.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/FlightRecorderTlabInfo-1536x365.jpg 1536w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">See below where I analyzed allocation calls resulting in allocations outside TLAB. This occurs while saving a large binary object to a blob in DB and making a call to a service where large JSON objects are being transmitted over wire. In the case of the binary object being allocated and persisted to DB, we modified its design to run asynch, so slowness in its allocation and persistence to DB doesn&#8217;t slow down the processing. In addition, the developers should look into trimming down the size of the object transmitted to an external service over JSON.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"500\" class=\"wp-image-681\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab.jpg 2163w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab-300x150.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab-1024x512.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab-768x384.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab-1536x768.jpg 1536w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/AllocatoionCallsOutsideTlab-2048x1024.jpg 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span style=\"text-decoration: underline;\">Analyzing JVM GC logs<\/span><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GC logs over a long period are massive. Mentally creating a timeline of GC activity over a long period is not easy. GC viewer helps visualize GC activity over a period that is useful to understand how the application performed under a load test. GC viewer is an open-source tool and available at: https:\/\/github.com\/chewiebug\/GCViewer<br>See below an image of GC viewer here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"433\" class=\"wp-image-682\" style=\"width: 1000px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs.jpg 2556w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs-300x130.jpg 300w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs-1024x444.jpg 1024w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs-768x333.jpg 768w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs-1536x666.jpg 1536w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerGCLogs-2048x888.jpg 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a summary of GC activity that GC viewer displayed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><img loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"510\" class=\"wp-image-683\" style=\"width: 500px;\" src=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerSummary.jpg\" alt=\"\" srcset=\"https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerSummary.jpg 506w, https:\/\/uqcodewall.com\/wp-content\/uploads\/2021\/04\/GCViewerSummary-294x300.jpg 294w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is an excellent article from uber that describes their performance tuning exercise to improve efficiency in their data streaming pipelines: <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/eng.uber.com\/building-scalable-streaming-pipelines\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/eng.uber.com\/building-scalable-streaming-pipelines\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>It is essential to understand what is slow before starting to optimize. And understanding the slow pieces in code requires profiling the code to figure out the hot spots and making efforts appropriately to optimize slowness inside the hotspots. I have been writing code and designing systems for two decades now. Even with this experience,<a class=\"more-link\" href=\"https:\/\/uqcodewall.com\/?page_id=670\">Continue reading <span class=\"screen-reader-text\">&#8220;Profiling&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"wf_page_folders":[18],"class_list":["post-670","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/pages\/670","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/uqcodewall.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=670"}],"version-history":[{"count":5,"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/pages\/670\/revisions"}],"predecessor-version":[{"id":2330,"href":"https:\/\/uqcodewall.com\/index.php?rest_route=\/wp\/v2\/pages\/670\/revisions\/2330"}],"wp:attachment":[{"href":"https:\/\/uqcodewall.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=670"}],"wp:term":[{"taxonomy":"wf_page_folders","embeddable":true,"href":"https:\/\/uqcodewall.com\/index.php?rest_route=%2Fwp%2Fv2%2Fwf_page_folders&post=670"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}