Who are we?

Tim Driscoll

  • Application Architect at HedgeServ
  • Software Engineer for more than 12 years
  • Likes dogs

Rositsa Kotseva

  • Full Stack Engineer at HedgeServ
  • Lecturer at HackBulgaria Academy
  • Software Engineer for 4 years
  • likes elephants

What are we going to talk about?

Performance Profiling Techniques to Make Your Code Awesome

This talk will cover:

  • Case Study
  • Best Practice (Rositsa)
  • Anti-patterns (Tim) 

           Case Study:               Risk Reporting RESTful Service

Application Description:

  • Simplified version of an actual reporting tool
  • Collects information from different market data providers
  • Reporting API that provides users positions data

Application Architecture

What is the problem with our application?

What should we change to fix this?

Step 1: Add Caching

Step 1: Add Caching

Step 1: Choose Which Distributed Cache

Are we sure this is going to fix the problem?

Step 1: Understand the problem

What are the functional and non-functional requirements for the app?

Non-functional requirements

  • Why are they important?
  • Examples:
    • Availability
    • Maintainability
    • Performance
    • . . .

Performance

  • Latency/Throughput
  • Statistical Distribution
  • Our Performance SLA
    • 90% of requests under 100ms
    • 99% of requests under 500ms

Let's look again at the app architecture

Report

Service

Valuations

Service

Positions

DB

Currency

Service

Step 2: Use your intuition

Empirical Evidence

  • Evidence relating to our decision based on an experiment
  • Evidence that can be experienced or observed

Let's check our LOGS!

[2019-10-10 09:39:28,505] INFO in report_controller: Start loading ExposureReport data...
[2019-10-10 09:39:28,944] INFO in valuations_dao: Loaded all security greeks
[2019-10-10 09:39:28,980] INFO in currency_dao: Loading currencies...
[2019-10-10 09:39:36,515] INFO in report_controller: ExportReport data loaded.

You skipped a step again!

Availability Heuristic

  • We make a choice based on the immediate and easy examples that come to our mind
  • In general, frequently encountered events are easier to recall

Availability Heuristic

All the relevant information to a design decision

The information we use to make a design decision

Other Cognitive Biases

Anchoring Bias

Overconfidence Bias

Confirmation Bias

Bandwagon Bias

Logs can be lying to you!

Let's "observe" our code!

How?

Profile our code!

What is Profiling?

Profiling is a form of dynamic program analysis that measures the space, the time, the complexity of a program, the usage of components, the frequency and duration of function calls.

Example of profiling on the case study

def enable_profiling(app):
	app.wsgi_app = ProfilerMiddleware(app.wsgi_app,
    					  restrictions=[30],
                          		  profile_dir=Constants.PROFILE_DIR)

Example of profiling on the case study

Now we know where the issue comes from.

    def _create_row(self, position):
        security, underlying = self.security_dict[position.security_name]
        currency = self._get_currency(security.currency_code)
        delta_adjusted_exposure = position.calculate_exposure(security, underlying, currency)
        return [position.security_name,
                position.quantity,
                position.market_value,
                delta_adjusted_exposure]

    def _build_security_dict(self):
        result = {}
        for security in self.securities: # Iterating the first time
            security_name = security.security_name
            underlying = self._get_security_data(security.underlying_name)
            result[security_name] = (security, underlying)
        return result

    def _get_security_data(self, security_name):
        for security_data in self.securities: # Nested iteration :(
            if security_data.security_name == security_name:
                return security_data

Current Code

    def _create_row(self, position):
        security = self.security_dict[position.security_name]
        underlying = self.security_dict.get(security.underlying_name)
        currency = self._get_currency(security.currency_code)
        delta_adjusted_exposure = position.calculate_exposure(security, underlying, currency)
        return [position.security_name,
                position.quantity,
                position.market_value,
                delta_adjusted_exposure]

    def _build_security_dict(self):
        return {security.security_name: security for security in self.securities}

New and Improved Code!

I fixed it!

Considerations when Profiling

  • We profiled only one request in isolation

  • How can we be sure it will perform this fast all the time? What about other scenarios?

How to ensure our app keeps running this fast?

  • Add load testing

  • Add stress testing

  • Create performance tests on time (next time)

  • Use APM

App Performance Management

  • Track performance of individual web requests/transactions

  • Track usage of all app dependencies

  • Detailed traceback to specific lines of code

  • Code level performance profiling

  • Server metrics like CPU, memory, etc.

  • Custom applications metrics created by the Development team

  • Centralized application log data

  • Application errors

  • Real user monitoring

Popular APM systems

What we covered?

  • How bad we are at writing code!

What we covered?

  • How bad we are at writing code!

  • How important are the non-functional requirements

  • What kind of heuristics and biases we follow

  • Using performance profiling to overcome those biases

  • What kind of tests do we need

  • What is an APM and when to use it

  • We improved our performance!

Thank you!

Tim Driscoll

Rositsa Kotseva

hackconf2019

By Rositsa Zlateva

hackconf2019

  • 935