Pro Tip for Python Developers: If you're working with pandas DataFrames, avoid using lambda functions with apply() to process your data column-wise or row-wise. While it might seem straightforward, this approach is often inefficient and can significantly slow down your code, especially with large datasets. 🔍 Why? Performance Hit: apply() with lambda functions operates in a Python-level loop, which is slower compared to optimized, vectorized operations. Scalability Issues: As your data grows, the time taken by apply() increases dramatically. 🚀 Better Alternatives: Use Vectorized Operations with NumPy or Pandas: These operations are implemented in C and are highly optimized. They process entire arrays at once, leading to significant performance gains. Utilize Built-in Pandas Functions: Functions like df.sum(), df.mean(), df['col'].str.lower(), etc., are vectorized and efficient. 🛠 Example: Instead of using apply(): # Inefficient approach df['new_col'] = df['col'].apply(lambda x: some_function(x)) Use vectorized operations: # Efficient approach df['new_col'] = some_vectorized_function(df['col']) Or with NumPy's where: import numpy as np df['new_col'] = np.where(condition, value_if_true, value_if_false) 📈 Benefits: Speed: Faster execution times, making your code more efficient. Readability: Cleaner and more concise code. Scalability: Better performance with larger datasets. 💡 Tip: Always look for a vectorized solution before resorting to apply() with lambda. Your future self (and your code's performance) will thank you! Happy coding! 🐍 #python #datascience
How to Improve Array Iteration Performance in Code
Explore top LinkedIn content from expert professionals.
Summary
Improving array iteration performance in code means making programs run faster by processing arrays more efficiently, which is essential in fields like data analysis and high-frequency trading. At its core, this concept focuses on using smarter methods to loop through arrays, so your computer spends less time and resources on each operation.
- Use vectorized operations: Choose built-in functions and vectorized approaches, such as those found in NumPy or pandas, instead of custom loops, to process entire arrays in one step.
- Consider memory access patterns: Arrange your data and loop order to match the way memory is laid out, so your CPU can retrieve information quickly and avoid unnecessary delays.
- Apply loop unrolling: Modify your loops to handle multiple array elements at once, which reduces overhead and helps your CPU work more efficiently.
-
-
Loop unrolling is one of those compiler tricks that feels like it should not work, but it does :) It is also super easy to implement. The idea is simple: instead of iterating one element at a time, the compiler (or you, manually) writes the loop to process multiple elements per iteration. A loop that runs 1000 times becomes one that runs 250 times but does 4x the work per pass. 1. sum += arr[i] 2. sum += (arr[i] + arr[i+1] + arr[i+2] + arr[i+3]) Why does this help? Modern CPUs are deeply pipelined. The branch check at the end of every iteration - "are we done yet?" - is small, but it adds up. Fewer branches mean fewer pipeline stalls and more room for instruction-level parallelism. Another improvement is memory prefetching. When you process four elements per iteration, the CPU has a cleaner, more predictable access pattern. Prefetchers love this and will load upcoming cache lines well before you need them. The trade-off is code size and readability. Unrolling a loop 4x means 4x the instructions in the binary. This might blow the instruction cache, which can actually make things slower. Most compilers use heuristics to find the sweet spot. If you want to, you can write this by hand. But in most cases, you should let the compiler do it for you. GCC and Clang will often do it automatically with `-O2` or `-O3`, and they may even leverage SIMD instructions to squeeze out more throughput. By the way, you can code every single thing I mentioned in under 15 minutes and see it for yourself. Anyway, it's Friday, so give it a shot. It will be fun!
-
I stumbled upon a fast alternative to NumPy. With 𝗡𝘂𝗺𝗘𝘅𝗽𝗿 I transformed a sluggish 650 ms loop into a 60 ms calculation and that was on a single thread! Here’s how NumExpr turbocharges your array computations: • 𝗖𝗵𝘂𝗻𝗸𝗲𝗱, 𝗶𝗻-𝗰𝗮𝗰𝗵𝗲 𝗲𝘅𝗲𝗰𝘂𝘁𝗶𝗼𝗻: Avoids building giant temporaries by splitting arrays into cache-sized blocks and streaming them through a lightweight virtual machine. • 𝗦𝗜𝗠𝗗 & 𝗩𝗠𝗟 𝗮𝗰𝗰𝗲𝗹𝗲𝗿𝗮𝘁𝗶𝗼𝗻: Leverages single-instruction-multiple-data instructions and-when available-Intel’s Math Kernel Library for transcendent functions. • 𝗧𝗿𝘂𝗲 𝗺𝘂𝗹𝘁𝗶-𝗰𝗼𝗿𝗲 𝘀𝗰𝗮𝗹𝗶𝗻𝗴: Automatically farms out chunks to all your CPU cores, delivering up to 5×–15× speed-ups on complex expressions. Check it out: github(dot)com/pydata/numexpr
-
Let’s talk about matrix multiplication and the lies told by algorithmic complexity. Below I have two naive matmul implementations. The only thing I change is in which order the for-loops iterate over dimensions: 𝗶, 𝗷, 𝗸 vs 𝗶, 𝗸, 𝗷. The total number of floating point operations in either case is 2*N^3, where N = 4096. From an algorithmic complexity point of view, these two are completely equivalent. Turns out i, j, k takes around 𝟳.𝟴 𝗺𝗶𝗻𝘂𝘁𝗲𝘀 while i, k, j takes only 𝟭𝟵 𝘀𝗲𝗰𝗼𝗻𝗱𝘀. A ~𝟮𝟰𝘅 difference in throughput! Why this difference? For this we need to understand how 2D arrays are represented in memory and CPU memory hierarchy. 2D arrays are 𝘶𝘴𝘶𝘢𝘭𝘭𝘺 represented in row-major order: a 2x2 matrix [[a, b], [c, d]] would be represented as a flat buffer [a, b, c, d] in main memory. When the CPU reads a value from main memory, it reads a cache line and stores it in the CPU's cache(s). A sequential read will likely result in a cache hit, resulting in a very fast access. Modern CPUs also employ heuristics for memory prefetching. Sequential accesses are about as simple to optimize for as possible. Now, consider accesses along a column which happens in the i, j, k order. Each access in matrix B belongs to a different row. But as we said, rows are flattened in memory, so these accesses are a row width apart! This access pattern has so-called poor spatial locality, requiring more frequent reads from main memory. These more frequent main memory accesses are what causes the poor performance. Want to learn more about performance programming? I recently started auditing OpenCourseWare’s 𝘗𝘦𝘳𝘧𝘰𝘳𝘮𝘢𝘯𝘤𝘦 𝘌𝘯𝘨𝘪𝘯𝘦𝘦𝘳𝘪𝘯𝘨 𝘰𝘧 𝘚𝘰𝘧𝘵𝘸𝘢𝘳𝘦 𝘚𝘺𝘴𝘵𝘦𝘮𝘴. Linked in the comments. For the measurements, I used clang 18.1.3 on Ubuntu compiled with a few optimizing flags: clang++ main.cpp -O2 -march=native -mavx2 -mfma --- If you enjoy learning about how software works under the hood, consider also subscribing to my Substack publication From Scratch where I cover similar topics. Today we hit a nice milestone with 2^10 subscribers - thanks a lot!
-
🚀 Cache Locality in C++: The Invisible Performance Killer In low-latency requirements, the CPU cache is your true data center. Main memory is hundreds of cycles away — a single cache miss can destroy your latency budget. 💡 A common layout (Array of Structs - AoS): struct Trade { double price; int quantity; char side; // 'B' or 'S' }; std::vector<Trade> trades; This is convenient, but not always cache-efficient. When you access price, the CPU fetches the entire cache line containing that field. If the struct is large or misaligned, unnecessary data (quantity, side) may get pulled in, and fewer useful price values fit in the cache line. ⚡ A cache-friendlier layout (Structure of Arrays - SoA): struct Trades { std::vector<double> prices; std::vector<int> quantities; std::vector<char> sides; }; Here, if your algorithm only touches prices, the cache lines are filled with exactly the data you need — no wasted bandwidth. 🔑 Takeaway: • AoS is convenient, but can waste cache capacity • SoA improves utilization when access patterns are predictable • In HFT, this translates directly into nanoseconds saved per iteration 👉 Next time you design a performance-critical loop, ask yourself: Am I feeding the CPU cache what it needs, or wasting bandwidth? 💭 I’m curious — what’s your favorite technique to get the most out of CPU caches in performance-critical systems? #Cplusplus #Performance #LowLatency #HighFrequencyTrading #SystemDesign
-
Have you ever struggled with painfully slow loops in Python DataFrames? Here are some tips on how you can speed things up: 1. 𝗔𝘃𝗼𝗶𝗱 .𝗶𝘁𝗲𝗿𝗿𝗼𝘄𝘀(): • Using .iterrows() is slow because it iterates row by row while creating Series objects every single time becoming a performance bottleneck. • You can use .itertuples() for a small performance gain, as it returns more lightweight namedtuples. 2. 𝗚𝗼 𝗳𝗼𝗿 𝗯𝘂𝗶𝗹𝗱-𝗶𝗻 𝗳𝘂𝗻𝗰𝘁𝗶𝗼𝗻𝘀 𝗮𝗻𝗱 𝘃𝗲𝗰𝘁𝗼𝗿𝗶𝘇𝗲𝗱 𝗼𝗽𝗲𝗿𝗮𝘁𝗶𝗼𝗻𝘀: • Aim at using built-in functions of Pandas, which are optimized in C. • Functions like .map() and built-in arithmetic operations can greatly improve performance. 3. 𝗨𝘀𝗲 .𝗮𝗽𝗽𝗹𝘆() 𝗶𝗻𝘀𝘁𝗲𝗮𝗱 𝗼𝗳 𝗹𝗼𝗼𝗽𝘀: • With .apply() you have a more efficient alternative for applying custom functions to your DataFrame. • It’s not as fast as vectorized operations, but it can be a great middle ground. 4. 𝗪𝗼𝗿𝗸 𝗱𝗶𝗿𝗲𝗰𝘁𝗹𝘆 𝗶𝗻 𝗡𝘂𝗺𝗣𝘆: • NumPy is optimized for large array operations and can handle tasks much faster than standard loops. • If you need to use loops a lot, try converting your columns to NumPy arrays and perform calculations directly. Loops can quickly become a performance bottleneck when working with DataFrames. Save yourself time by using performance-optimized approaches instead. What tricks do you use to make your Pandas code run faster? ---------------- ♻️ 𝗦𝗵𝗮𝗿𝗲 if you find this post useful ➕ 𝗙𝗼𝗹𝗹𝗼𝘄 for more daily insights on how to grow your career in the data field #dataanalytics #datascience #python #pandas #careergrowth
-
🚀 Boosting Performance with C# Parallel.ForEach[Async] 🚀 When dealing with multiple I/O-bound operations—like HTTP requests to a REST API—sequential execution (`foreach`) can slow down performance significantly. Fortunately, `Parallel.ForEach` and `Parallel.ForEachAsync` allow us to execute these operations concurrently, unlocking massive speed improvements. 🔥 The Problem: Sequential Execution Consider the scenario where we need to make N remote calls where N=6. Assuming the remote endpoint takes 2 seconds to respond, using a `foreach` loop will take 12 seconds to complete (left code). Borrowing from the big O notation, this runs in O(N) time, meaning the time is linearly relative to the number of iterations (N), so 100 calls would take around 200 seconds (over 3 minutes) to complete, which is enormous. ⚡ The Solution: Parallel Execution With `Parallel.ForEachAsync`, we can execute the calls in parallel. Assuming the server has enough resources to send the N requests simultaneously, it would take only about 2 seconds to complete the execution of the endpoint, no matter what the value of N is (right code). In big O terms, this runs in O(1) time, meaning the time it takes to complete the execution is constant and will always be the same (a.k.a. two seconds). Since we live in a discreet world, we do not have unlimited resources, so the actual execution time would be O(N/k), where k is the MaxDegreeOfParallelism (a.k.a. the maximum number of calls we can do at once). 📊 Results—100 remote calls using my personal computer 🏆 `Parallel.ForEachAsync`: 10.2095184 seconds. 😞 `foreach` loop: 3:20.9339857 minutes. 📖 Conclusion While `Parallel.ForEachAsync` can dramatically reduce execution time, the real-world performance boost depends on the system's ability to handle parallel execution. ✅ Ideal for independent I/O-bound operations like HTTP requests. ✅ Use MaxDegreeOfParallelism to control how many requests to send in parallel. ✅ Be cautious of shared state (e.g., List<T>)—use thread-safe collections like ConcurrentBag<T> instead. 📌 Tip You can use `Parallel.ForEach` to execute synchronous operations in parallel, while `Parallel.ForEachAsync` allows you to use async/await code. 💬 What’s your experience with parallel execution? Have you tried `Parallel.ForEachAsync`, or do you have a different approach? Do you have stories to share? Drop your thoughts in the comments! 🚀👇 #ParallelProgramming #Performance #ASPNETCore #SoftwareArchitecture #dotnet #csharp #DesignAndArchitecture #CodeDesign #ProgrammingTips #CleanCode
-
🏇 𝗢𝘃𝗲𝗿𝗵𝗲𝗮𝗱𝘀 𝗜: 𝗧𝗵𝗲 𝗰𝗼𝘀𝘁 𝗼𝗳 𝗹𝗼𝗼𝗽𝘀 Iteration is one of the core building blocks of software. It is so common that people perceive the loop header as mostly a declaration of intent. But there is a lot of administration and bookkeeping involved: • incrementing counters • update pointers • "are we done?" checks • jump to the beginning 🛄 In the simple example below, only 𝟭 out of 𝟰 instructions (green vs. red) is 𝘱𝘳𝘰𝘥𝘶𝘤𝘵𝘪𝘷𝘦 towards producing the desired result. Everything else is overhead incurred in every iteration. The yellow section is the one-time setup cost of the loop and the last two instructions are the return statement. Main strategies increase the proportion of "green" instructions per iteration: ✅ loop unrolling (process 2 or more items per iteration) ✅ merging loops (iterate only once instead of repeatedly) ✅ single iteration variable (in case of multiple inputs/outputs) Many compilers already perform some of these optimizations themselves, others need manual intervention to become effective. #FiveHorsemen #loops #overhead #optimization
-
Boost Your .NET Performance: When to Use Span<T> and ReadOnlySpan<T> in C# Span<T> and ReadOnlySpan<T> offer zero-allocation, stack-based slicing for better performance. ✅ Use Span<T> when: ▪️ You need efficient, mutable slicing of arrays, strings, or memory. ▪️ Reducing allocations in performance-critical code. ▪️ You need mutability—Span<T> allows modifying the underlying data. 🔒 Use ReadOnlySpan<T> when: ▪️ You only need to read the data. ▪️ Working with immutable sources like strings. 🔹 You want safer, allocation-free access to data structures. ⚡ Why Use These? ✅ Zero allocations (vs. string.Substring() or Array.Copy()) ✅ Stack-based memory (faster than heap allocations) ✅ Better performance for high-speed operations 🚨 Limitations: ❌ Cannot be stored in fields (lifetime constraints) ❌ Not usable with async methods (lifetime issues) ❌ Requires unsafe code for certain scenarios Want to optimize your .NET apps? Start leveraging Span<T> and ReadOnlySpan<T>!
Explore categories
- Hospitality & Tourism
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Healthcare
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Career
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development