Question

I am writing a benchmark for a program in Clojure. I have n threads accessing a cache at the same time. Each thread will access the cache x times. Each request should be logged inside a file.

To this end I created an agent that holds the path to the file to be written to. When I want to write I send-off a function that writes to the file and simply returns the path. This way my file-writes are race-condition free.

When I execute my code without the agent it finished in a few miliseconds. When I use the agent, and ask each thread to send-off to the agent each time my code runs horribly slow. I'm talking minutes.

(defn load-cache-only [usercount cache-size]
  "Test requesting from the cache only."
  ; Create the file to write the benchmark results to.
  (def sink "benchmarks/results/load-cache-only.txt")
  (let [data-agent (agent sink)
        ; Data for our backing store generated at runtime.
        store-data (into {} (map vector (map (comp keyword str)
                                             (repeat "item") 
                                             (range 1 cache-size)) 
                                        (range 1 cache-size)))
        cache      (create-full-cache cache-size store-data)]
    (barrier/run-with-barrier (fn [] (load-cache-only-work cache store-data data-agent)) usercount)))

(defn load-cache-only-work [cache store-data data-agent]
  "For use with 'load-cache-only'. Requests each item in the cache one.
   We time how long it takes for each request to be handled."
  (let [cache-size  (count store-data)
        foreachitem (fn [cache-item]
                      (let [before  (System/nanoTime)
                            result  (cache/retrieve cache cache-item)
                            after   (System/nanoTime)
                            diff_ms ((comp str float) (/ (- after before) 1000))]
                        ;(send-off data-agent (fn [filepath] 
                                           ;(file/insert-record filepath cache-size diff_ms)
                                           ;filepath))
                        ))]
    (doall (map foreachitem (keys store-data)))))

The (barrier/run-with-barrier) code simply spawns usercount number of threads and starts them at the same time (using an atom). The function I pass is the body of each thread.

The body willl simply map over a list named store-data, which is a key-value list (e.g., {:a 1 :b 2}. The length of this list in my code right now is 10. The number of users is 10 as well.

As you can see, the code for the agent send-off is commented out. This makes the code execute normally. However, when I enable the send-offs, even without writing to the file, the execution time is too slow.

Edit:

I made each thread, before he sends off to the agent, print a dot. The dots appear just as fast as without the send-off. So there must be something blocking in the end.

Am I doing something wrong?

Was it helpful?

Solution

You need to call (shutdown-agents) when you're done sending stuff to your agent if you want the JVM to exit in reasonable time.

The underlying problem is that if you don't shutdown your agents, the threads backing its threadpool will never get shut down, and prevent the JVM from exiting. There's a timeout that will shutdown the pool if there's nothing else running, but it's fairly lengthy. Calling shutdown-agents as soon as you're done producing actions will resolve this problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top