<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://sergey.party/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sergey.party/" rel="alternate" type="text/html" /><updated>2024-02-01T03:49:47+00:00</updated><id>https://sergey.party/feed.xml</id><title type="html">megaserg blog</title><subtitle>This is my programming blog with my (sometimes original) thoughts and opinions.
</subtitle><entry><title type="html">How to speed up your PyTorch training</title><link href="https://sergey.party/2020/10/13/pytorch-performance-guide.html" rel="alternate" type="text/html" title="How to speed up your PyTorch training" /><published>2020-10-13T00:00:00+00:00</published><updated>2020-10-13T00:00:00+00:00</updated><id>https://sergey.party/2020/10/13/pytorch-performance-guide</id><content type="html" xml:base="https://sergey.party/2020/10/13/pytorch-performance-guide.html">&lt;p&gt;If you’re training PyTorch models, you want to train as fast as possible. This means you can try more things faster and get better results. Let’s learn how to speedup your training!&lt;/p&gt;

&lt;p&gt;This performance optimization guide is written with cloud-based, multi-machine multi-GPU training in mind, but can be useful even if all you have is a desktop with a single GPU under your bed.&lt;/p&gt;

&lt;h2 id=&quot;hardware-factors&quot;&gt;Hardware factors&lt;/h2&gt;

&lt;p&gt;The training speed is fundamentally limited by three factors: CPU, GPU, and network.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;CPU speed. Each training process uses CPUs 1) to receive, parse, preprocess, and batch data examples; 2) to execute some NN operations not supported by GPU; and 3) to postprocess metrics, save checkpoints, and write summaries. In cloud, you can choose how many CPU cores your machine has.&lt;/li&gt;
  &lt;li&gt;GPU speed. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistributedDataParallel&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DDP&lt;/code&gt;, current SotA for distributed training), we run one training PyTorch process per one GPU. Each training process uses GPU for most of the forward pass, backward pass, and weight update. In synchronized distributed training, GPUs communicate on each step to share gradients. In cloud, you can choose how many GPUs your machine has.&lt;/li&gt;
  &lt;li&gt;Network speed. Each training process uses network for data downloading and GPU communication. The “main” process also uploads model checkpoints and summaries. If you’re in Google Cloud and store your dataset on GCS, for example, each machine has about &lt;strong&gt;10-100 Gbit/s&lt;/strong&gt; ingress network which means it can download &lt;strong&gt;1-10 GB/s&lt;/strong&gt; from GCS. For comparison, my desktop can download from GCS at &lt;strong&gt;100 MB/s&lt;/strong&gt;. In cloud, you can scale total network bandwidth by adding more machines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that each training process has its whole GPU to itself, but network and CPUs are shared equally between all training processes. So it’s useful to think about CPU/GPU ratio or network/GPU ratio.&lt;/p&gt;

&lt;h2 id=&quot;the-typical-training-step&quot;&gt;The typical training step&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Data loading (uses CPU + network)
    &lt;ul&gt;
      &lt;li&gt;Download example (bound by network speed, parallelizable per example)&lt;/li&gt;
      &lt;li&gt;Parse, preprocess, augment example (bound by CPU, parallelizable per example)&lt;/li&gt;
      &lt;li&gt;Build a batch (bound by CPU, parallelizable per batch)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Memcpy batch to GPU (bound by PCI-e, parallelizable per GPU)&lt;/li&gt;
  &lt;li&gt;Training (uses GPU + network, parallelizable per batch)
    &lt;ul&gt;
      &lt;li&gt;Forward pass, backward pass (bound by GPU)&lt;/li&gt;
      &lt;li&gt;Share gradients (bound by network)&lt;/li&gt;
      &lt;li&gt;Update weights (bound by GPU)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Postprocessing (uses CPU)&lt;/li&gt;
  &lt;li&gt;Save checkpoint and summaries (uses CPU)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;realizing-the-problem-and-ways-to-solve-it&quot;&gt;Realizing the problem and ways to solve it&lt;/h2&gt;

&lt;p&gt;How do you even know the training or dataloading is slow? You can measure the time spent by the training process getting a batch from the dataloader and compare it to the total time of the step. Ideally, all dataloading is offloaded to background processes, so getting the next batch is nearly instantaneous and only take a few percents of total time. Another way is to print, e.g. every &lt;strong&gt;10 batches&lt;/strong&gt;, how long did it take to process them. That way you can see if you have suspicious pauses, e.g. when a new epoch starts.&lt;/p&gt;

&lt;p&gt;We have several ways of making training faster:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;we can &lt;em&gt;do things in parallel&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;we can &lt;em&gt;make individual things faster&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;we can &lt;em&gt;avoid doing some things&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;parallelization&quot;&gt;Parallelization&lt;/h2&gt;

&lt;p&gt;Ideally, CPU, GPU, and network should be used by the training &lt;em&gt;simultaneously&lt;/em&gt;, which means each of them should always have something to do. Also, a machine has multiple CPUs, multiple GPUs, and multiple network connections. We want to get to the state where:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;either all CPUs are &lt;strong&gt;100%&lt;/strong&gt; used&lt;/li&gt;
  &lt;li&gt;or all GPUs are &lt;strong&gt;100%&lt;/strong&gt; used (this is the ultimate goal as in the cloud, GPUs is the most expensive resource of all)&lt;/li&gt;
  &lt;li&gt;or the network is &lt;strong&gt;100%&lt;/strong&gt; used&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that’s not the case, e.g. your monitoring charts show &lt;strong&gt;CPU &amp;lt; 100%&lt;/strong&gt;, &lt;strong&gt;GPU &amp;lt; 100%&lt;/strong&gt;, and &lt;strong&gt;network &amp;lt; 1 GB/s&lt;/strong&gt;, it means there is not enough parallelization, i.e. there is some “artificial” software limit or lock preventing full utilization of hardware. For the cloud, this also means we can’t scale vertically (by adding more CPUs/GPUs) but only horizontally (by adding more machines), and in fact, might need to downscale vertically if we can’t fully use the resources anyway.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.tensorflow.org/guide/images/data_performance/naive.svg&quot; alt=&quot;without parallelization&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.tensorflow.org/guide/images/data_performance/parallel_interleave.svg&quot; alt=&quot;with parallelization&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;how-to-see-utilization-on-your-desktop&quot;&gt;How to see utilization on your desktop&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;CPU: I like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htop&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install -y htop&lt;/code&gt;). It shows how much each core is utilized with horizontal bars. Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Shift + H&lt;/code&gt; to hide individual threads. Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; to sort processes by CPU usage.&lt;/li&gt;
  &lt;li&gt;GPU: I like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo nvtop&lt;/code&gt; (&lt;a href=&quot;https://github.com/Syllo/nvtop#distribution-specific-installation-process&quot;&gt;install&lt;/a&gt;). It shows graphical history of GPU and memory utilization, as well as processes using GPU. Alternatively, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;watch -n 1 nvidia-smi&lt;/code&gt; is okay.&lt;/li&gt;
  &lt;li&gt;Network: I like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo iftop&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install -y iftop&lt;/code&gt;). It shows the incoming/outgoing throughput per connection. The three columns are average bandwidth during the last &lt;strong&gt;2, 10 and 40 seconds&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;To isolate dataloading from the other things, I like to use a separate “dataloading benchmark” script which just fetches batches as fast as possible without engaging GPU. This is to reduce the number of moving parts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;notes-on-parallelization-data-loading&quot;&gt;Notes on parallelization: data loading&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;The main obstacle to dataloading parallelization is Python’s GIL: only one stream of computation can progress within a Python process at any moment. The way around it is to have multiple Python processes, or fall to C++ code which is not bound by GIL.&lt;/li&gt;
  &lt;li&gt;That’s why each training process has its own GPU to feed with data. We shard the dataset into as many pieces as we have GPUs. The processes don’t communicate except for sharing gradients, so GPUs can be kept busy in parallel.
    &lt;ul&gt;
      &lt;li&gt;Note: you can tune this by having more or fewer GPUs. Remember to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.distributed.launch&lt;/code&gt; to enable multi-GPU training, otherwise only one GPU will be used.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;With PyTorch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataLoader&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num_workers &amp;gt; 0&lt;/code&gt;, each training process offloads the dataloading to subprocesses. Each worker subprocess receives one batch worth of example indices, downloads them, preprocesses, and stacks the resulting tensors, and shares the resulting batch with the training process (by pickling into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt;). The training process then only has to feed the GPU and postprocess. As a result, GPU computation and CPU computation overlap.
    &lt;ul&gt;
      &lt;li&gt;Worker subprocesses are also working in parallel, making use of CPU and network. At any moment a process uses either only CPU or only network, so it’s even okay to have more processes than you have CPU cores.&lt;/li&gt;
      &lt;li&gt;You can scale this parallelization by increasing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num_workers&lt;/code&gt;, but too many processes exhaust &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt; space which manifests as “bus error” and training crashes. Remember that if the machine has several GPUs, &lt;strong&gt;each&lt;/strong&gt; training process will launch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num_workers&lt;/code&gt; processes!&lt;/li&gt;
      &lt;li&gt;Worker processes are destroyed and recreated every epoch, although there is a way around that by &lt;a href=&quot;https://github.com/pytorch/pytorch/issues/15849#issuecomment-573921048&quot;&gt;wrapping the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sampler&lt;/code&gt; around&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;The workers need to assemble the whole batch, and they sequentially download and process the examples in the batch (sad!). Therefore, in the beginning of the epoch, when all worker processes start dataloading, you might observe a long pause while each of them is assembling their first batch. This pause goes away after the first batch, as long as the dataloading is not slower than training.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Network requests to GCS are issued in parallel but parallelism is bounded by the underlying connection pool size (by default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10&lt;/code&gt;). Increasing that did not achieve any speedup; likely because the library talking to GCS is in Python and therefore subject to GIL, and therefore unable to make use of more than &lt;strong&gt;10&lt;/strong&gt; simultaneous connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;side-note-parquet-format&quot;&gt;Side note: Parquet format&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;You might consider to use Parquet as your dataset format. Parquet is a columnar format, meaning the records are written on disk not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id1 | name1 | age1 | id2 | name2 | age2&lt;/code&gt;, but rather &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id1 | id2 | name1 | name2 | age1 | age2&lt;/code&gt; (however, this holds true only within a “rowgroup” which is a group of several consecutive rows). This means columns of a record can be downloaded and parsed in parallel (unlike e.g. TFRecord format which is just one protobuf blob). You can read Parquet files with PyArrow library, which uses C++-based thread pool to read the columns in parallel. You can tune &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OMP_NUM_THREADS&lt;/code&gt; environment variable to increase the size of that thread pool, but too many threads can cause thrashing. Probably stick to default unless you know what you’re doing. Then, wrap the resulting dataset in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataLoader&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;An alternative reader of Parquet is Petastorm, which in my opinion has made less optimal architectural choices compared to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataLoader&lt;/code&gt;. Petastorm also has a pool of workers which receive tasks (rowgroup ids), download rowgroups, and perform filtering and transforms (but not batching). There are two implementations of worker pool in Petastorm: thread pool and process pool. You can tune this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reader_pool_type=&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;thread&quot;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;process&quot;&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Thread pool is subject to GIL so there’s no CPU parallelization, and all computation happens in the same process that feeds the GPU, but I think network requests are parallelized (Python yields GIL when performing I/O).&lt;/li&gt;
      &lt;li&gt;Process pool parallelizes both CPU and network usage. The worker processes send records back to the main process via ZeroMQ. This means records must be serialized, and the main process needs to spend time deserializing. The fastest way to do that is PyArrow serialization, but it can only serialize certain data types. The alternative is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pickle&lt;/code&gt; serialization which understands all data types but is slower.&lt;/li&gt;
      &lt;li&gt;Also, with Petastorm, the main training process is assembling the batches, which takes some CPU time and can leave GPU underutilized.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;notes-on-parallelization-memcpy&quot;&gt;Notes on parallelization: memcpy&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;There is a region in RAM called “pinned memory” which is the waiting area for tensors before they can be placed on GPU. For faster CPU-to-GPU transfer, we can copy tensors in the pinned memory region in the background thread, before GPU asks for the next batch. This is available with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pin_memory=True&lt;/code&gt; argument to PyTorch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataLoader&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Documentation on pinned memory: &lt;a href=&quot;https://pytorch.org/docs/stable/data.html#memory-pinning&quot;&gt;pytorch&lt;/a&gt;, &lt;a href=&quot;https://devblogs.nvidia.com/how-optimize-data-transfers-cuda-cc/&quot;&gt;nvidia&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;If tensors are placed in pinned memory, it’s also possible to copy them to GPU asynchronously, so that CPU is not blocked by the copying operation. This is useful if for some reason you have CPU-based computation between memcpy and kickstarting forward pass. Enable asynchronous memcpy-to-GPU with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;your_tensor.to(device, non_blocking=True)&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Documentation on async transfer: &lt;a href=&quot;https://pytorch.org/docs/stable/notes/cuda.html#use-pinned-memory-buffers&quot;&gt;pytorch&lt;/a&gt;, &lt;a href=&quot;https://devblogs.nvidia.com/how-overlap-data-transfers-cuda-cc/&quot;&gt;nvidia&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;notes-on-parallelization-training&quot;&gt;Notes on parallelization: training&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Typical models are composed of sequential layers, so there is not a lot of opportunity to overlap GPU operations. It’s theoretically possible to launch kernels in parallel on the same GPU, but in different CUDA streams, which would benefit graph-like models &lt;a href=&quot;https://discuss.pytorch.org/t/how-can-l-run-two-blocks-in-parallel/61618/5&quot;&gt;and maybe even sequential models&lt;/a&gt; – this seems to be an ongoing effort in PyTorch.&lt;/li&gt;
  &lt;li&gt;However, Nvidia Apex library provides optimized &lt;a href=&quot;https://nvidia.github.io/apex/parallel.html#apex.parallel.DistributedDataParallel&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DistributedDataParallel&lt;/code&gt;&lt;/a&gt; which overlaps communication (gradient sharing) and backward pass.&lt;/li&gt;
  &lt;li&gt;Increasing batch size &lt;a href=&quot;https://www.pugetsystems.com/labs/hpc/GPU-Memory-Size-and-Deep-Learning-Performance-batch-size-12GB-vs-32GB----1080Ti-vs-Titan-V-vs-GV100-1146/&quot;&gt;doesn’t increase parallelism linearly&lt;/a&gt;, however there is a fixed cost of memcpy and gradient sharing per batch, so it still makes sense to make the batch as large as possible.&lt;/li&gt;
  &lt;li&gt;In case of model parallelism, it’s possible to &lt;a href=&quot;https://torchgpipe.readthedocs.io/en/stable/gpipe.html#pipeline-parallelism&quot;&gt;automatically parallelize even sequential models&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;making-things-faster-and-avoiding-doing-things&quot;&gt;Making things faster and avoiding doing things&lt;/h2&gt;

&lt;p&gt;Suppose you see either CPU, GPU, or network &lt;strong&gt;100%&lt;/strong&gt; (or almost &lt;strong&gt;100%&lt;/strong&gt;) used. That saturated resource is the &lt;em&gt;bottleneck&lt;/em&gt; i.e. the slowest part for which other resources have to wait. Optimizing the slowest part will speed up the whole thing. Optimizing other parts will have no effect. You can use these tips even if the resource is not &lt;strong&gt;100%&lt;/strong&gt; used, and then save money by downscaling vertically (reducing number of CPUs/GPUs) without losing training speed.&lt;/p&gt;

&lt;h1 id=&quot;making-cpu-computation-faster&quot;&gt;Making CPU computation faster&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;To figure out what is so slow in the computation, you need to profile.
    &lt;ul&gt;
      &lt;li&gt;I like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py-spy&lt;/code&gt; sampling profiler. It can connect to a running Python process, profile &lt;em&gt;both Python and native code&lt;/em&gt;, and produce a &lt;a href=&quot;http://www.brendangregg.com/flamegraphs.html&quot;&gt;“flamegraph” visualization&lt;/a&gt; where the widest part is the slowest method.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install py-spy&lt;/code&gt; if it’s not yet installed.&lt;/li&gt;
      &lt;li&gt;Figure out the Python process ID to profile. For the training process, check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nvtop&lt;/code&gt; to see which process is using GPU. For the dataloading worker process, pick any of them in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htop&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;Do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py-spy record -r 29 -o profile.svg -p &amp;lt;PID&amp;gt; --native&lt;/code&gt;. Wait for &lt;strong&gt;5-10 seconds&lt;/strong&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+C&lt;/code&gt;. The resulting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;profile.svg&lt;/code&gt; image is your profile, open it in your browser.&lt;/li&gt;
      &lt;li&gt;Caveats:
        &lt;ul&gt;
          &lt;li&gt;Worker processes are killed and recreated every epoch, so start profiling in the beginning of the epoch.&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-r&lt;/code&gt; argument is how many samples to take per second. If it’s too high, the sampling overhead slows the target Python process down.&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--native&lt;/code&gt; argument will profile even C++ code! But if the profiler just crashes, try to remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--native&lt;/code&gt;.&lt;/li&gt;
          &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--subprocesses&lt;/code&gt; will create a combined profile for the main process and all the worker subprocesses, but it’s harder to interpret.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In Parquet, record fields (columns) are written separately from each other, so it’s possible to avoid reading columns you don’t use for training. Petastorm supports that out of the box.&lt;/li&gt;
  &lt;li&gt;Local caching avoids downloading+parsing+preprocessing the same training example twice. The preprocessed example can be saved to disk and loaded from there during the next epochs. Petastorm supports that out of the box. Random-based augmentation still needs to be done outside of caching!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://www.tensorflow.org/guide/images/data_performance/cached_dataset.svg&quot; alt=&quot;caching&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Some options to speed up slow Python code:
    &lt;ul&gt;
      &lt;li&gt;add &lt;a href=&quot;https://numba.pydata.org/numba-doc/dev/user/5minguide.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@numba.jit&lt;/code&gt;&lt;/a&gt; decorator to a slow method (even with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;numpy&lt;/code&gt;) for automatic conversion to machine code (there’s more to Numba than that, e.g. parallelization and vectorization)&lt;/li&gt;
      &lt;li&gt;memoize computation + parallelize a for-loop with &lt;a href=&quot;https://joblib.readthedocs.io/en/latest/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;joblib&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;manually vectorize code with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;numpy&lt;/code&gt; operations instead of doing for-loops. Make sure your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;numpy&lt;/code&gt; is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MKL&lt;/code&gt;!&lt;/li&gt;
      &lt;li&gt;parallelize a for-loop with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.multiprocessing&lt;/code&gt; (same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multiprocessing&lt;/code&gt;, but uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/shm&lt;/code&gt; for communication)&lt;/li&gt;
      &lt;li&gt;use Cython to convert your code into optimized C++ and make it an extension&lt;/li&gt;
      &lt;li&gt;see if there are different implementations of the same logic. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OpenCV&lt;/code&gt; can be faster than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PIL&lt;/code&gt;, etc.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Some CPU computation (e.g. image preprocessing) can be done much faster on GPU instead! We’re working on enabling that with &lt;a href=&quot;https://docs.nvidia.com/deeplearning/sdk/dali-developer-guide/docs/&quot;&gt;Nvidia DALI library&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can override the collation function for a faster one, e.g. consider &lt;a href=&quot;https://github.com/NVIDIA/apex/blob/2ca894da7be755711cbbdf56c74bb7904bfd8417/examples/imagenet/main_amp.py#L28&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fast_collate&lt;/code&gt; from NVIDIA&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Within your training loop, be careful to not use loss variable outside of the loop or use it in such a manner that a reference will be held. If you do that, a separate copy of your model graph will be held in memory, preventing you from using large batch size. &lt;a href=&quot;https://pytorch.org/docs/stable/notes/faq.html&quot;&gt;See this for more details&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;making-network-faster&quot;&gt;Making network faster&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;GCS API supports GET-requests that ask only for a range of bytes, but some libraries (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcsfs&lt;/code&gt; without configuration) inflate that range to &lt;strong&gt;5MB&lt;/strong&gt; for caching purposes. The Parquet files don’t benefit from such a large readahead cache (most columns are super small), and this results in unnecessarily huge incoming traffic and a significant slowdown. Check that global variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcsfs.core.GCSFileSystem.default_block_size&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1024&lt;/code&gt; or less!&lt;/li&gt;
  &lt;li&gt;Opening and closing connections is pretty expensive. If you need to download a lot of files from GCS, make sure you’re reusing connections.&lt;/li&gt;
  &lt;li&gt;Again, caching avoids repeated downloads of the same file. Caching could theoretically be done on filesystem level to persist raw bytes (instead of parsed+preprocessed records).&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;making-gpu-computation-faster&quot;&gt;Making GPU computation faster&lt;/h1&gt;

&lt;p&gt;If you maxed out the GPUs, congratulations! You’re using the hardware efficiently. Still a few optimization opportunities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.autograd.profiler&lt;/code&gt; to see how long do the GPU operations take. By default, you would only see the CPU’s point of view; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use_cuda=True&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;num_workers=0&lt;/code&gt;, you will see exact GPU kernel timings.
    &lt;ul&gt;
      &lt;li&gt;The resulting profiles are JSON files. Open them in &lt;a href=&quot;chrome://tracing&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome://tracing&lt;/code&gt;&lt;/a&gt; timeline viewer.&lt;/li&gt;
      &lt;li&gt;You might see some kernels, unsupported by GPU, executing on CPU. This is sometimes inefficient because GPU has to send the inputs to CPU and wait for the results to come back. Maybe you can replace it with GPU-supported operations?&lt;/li&gt;
      &lt;li&gt;You might also see kernels that are inefficient on GPU and take super long time to execute. You can try placing them on CPU and get speedup even with the overhead of back-and-forth copying!&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://pytorch.org/tutorials/_images/trace_img.png&quot; alt=&quot;torch_profiler&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Try setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.backends.cudnn.benchmark = True&lt;/code&gt; in the beginning of training. This chooses &lt;a href=&quot;https://discuss.pytorch.org/t/what-does-torch-backends-cudnn-benchmark-do/5936&quot;&gt;optimal convolution algorithm if the input shapes don’t change&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;“Mixed precision” mode (where some operations work with fp16 inputs but accumulate in fp32) can be &lt;a href=&quot;https://docs.nvidia.com/deeplearning/performance/mixed-precision-training/index.html#mptrain&quot;&gt;several times faster&lt;/a&gt; than fully fp32 operations, and also allow you to use 2x larger batch size.
    &lt;ul&gt;
      &lt;li&gt;Try manually converting your inputs to fp16.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://github.com/NVIDIA/apex&quot;&gt;Nvidia Apex library&lt;/a&gt; can automatically search for opportunities to enable mixed precision (AMP). &lt;a href=&quot;https://github.com/NVIDIA/apex/blob/master/examples/imagenet/main_amp.py#L157&quot;&gt;imagenet example&lt;/a&gt;. PyTorch also has some &lt;a href=&quot;https://github.com/pytorch/pytorch/issues/25081&quot;&gt;native AMP support&lt;/a&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.cuda.amp&lt;/code&gt; exists since Feb 2020).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Try reordering tensor dimensions. GPUs like NCHW, but “tensor cores” &lt;a href=&quot;https://devblogs.nvidia.com/tensor-core-ai-performance-milestones/&quot;&gt;like NHWC&lt;/a&gt; and &lt;a href=&quot;https://docs.nvidia.com/deeplearning/sdk/pdf/Deep-Learning-Performance-Guide.pdf&quot;&gt;sizes divisible by 8&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;In cloud, you can simply replace a GPU with a more powerful one. Be aware that it can be several times more expensive, but not several times faster!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h2&gt;

&lt;p&gt;PyTorch is a powerful tool under active development, and it’s useful to learn how it works. Don’t be afraid to profile, experiment, and read the source code!&lt;/p&gt;</content><author><name></name></author><category term="pytorch," /><category term="optimization," /><category term="performance" /><summary type="html">If you’re training PyTorch models, you want to train as fast as possible. This means you can try more things faster and get better results. Let’s learn how to speedup your training!</summary></entry><entry><title type="html">Road trip across United States</title><link href="https://sergey.party/2020/08/14/roadtrip-united-states.html" rel="alternate" type="text/html" title="Road trip across United States" /><published>2020-08-14T00:00:00+00:00</published><updated>2020-08-14T00:00:00+00:00</updated><id>https://sergey.party/2020/08/14/roadtrip-united-states</id><content type="html" xml:base="https://sergey.party/2020/08/14/roadtrip-united-states.html">&lt;p&gt;This post is the summary of my travel plans, reasoning for it, and some technical details.&lt;/p&gt;

&lt;p&gt;The transition to a digital nomad has happened successfully. I’ve moved out of my apartments, stored/threw away/donated my belongings, and left San Francisco. With friends, we stayed a week in Santa Barbara and spent the whole July in a nice tropical-themed house in San Diego. Then the question appeared: where to next?&lt;/p&gt;

&lt;p&gt;As you remember from the &lt;a href=&quot;https://sergey.party/2020/06/14/digital-nomad.html&quot;&gt;previous post&lt;/a&gt;, my company has said we’re going to be in WFH mode until Labor Day (&lt;strong&gt;Sep 7, 2020&lt;/strong&gt;). Last month, this policy was extended until the end of &lt;strong&gt;2020&lt;/strong&gt; with a few clarifications: you can temporarily relocate within the US, but be available during the normal working hours of your team; you’re expected to return to the office in &lt;strong&gt;January 2021&lt;/strong&gt;; and we don’t recommend traveling outside of US due to the possible restrictions at the border. This prompted an idea of a road trip across United States! I’ve been to quite a few US cities in the past years, but there are still many more unexplored ones.&lt;/p&gt;

&lt;p&gt;Usually long-distance road trips span several weeks. E.g. traveling coast-to-coast can take about &lt;strong&gt;50 hours&lt;/strong&gt; or &lt;strong&gt;6 days&lt;/strong&gt; of &lt;strong&gt;8+-hour&lt;/strong&gt; non-stop driving each day; but where’s the fun in that? According to the internet, assuming you stay in the points of interest for a few days, a memorable trip takes &lt;strong&gt;3-4 weeks&lt;/strong&gt; (2-3 “resting” days per each “driving day”). However, in my situation I would have &lt;strong&gt;5 months&lt;/strong&gt; before I’m summoned back to the office. Of course, I should also work remotely as I’m a full-time employee.&lt;/p&gt;

&lt;p&gt;(Side note: actually, this week the company WFH policy was extended again, until the end of &lt;strong&gt;July 2021&lt;/strong&gt;. Google and Facebook also announced their extensions until the same dates a couple weeks earlier.)&lt;/p&gt;

&lt;p&gt;So, the plan was hatched: I can start from San Diego, stay each week (or two) in a new place, drive on weekends, work during the weekdays, and be back in San Francisco by January. I’ve reasoned that I can comfortably drive &lt;strong&gt;6 hours&lt;/strong&gt; per day (this turned out to be pretty accurate). So I can construct a route with the week-long stops up to &lt;strong&gt;12 hours&lt;/strong&gt; apart (and stay in an intermediate point on Saturday night if needed).&lt;/p&gt;

&lt;p&gt;I opened Google Maps and started to pick cities that I’m interested in, never been to, or at least heard something about. I decided I’ll go in the clockwise direction, because the South is too hot in summer, and the North is too cold in winter – we’ll see how that strategy works. Here is my itinerary. There is some buffer, and I don’t need to be in SF in January anymore, so I have quite some flexibility. &lt;strong&gt;Please let me know&lt;/strong&gt; if I should absolutely visit some city that isn’t on the list. You can also let me know that I should absolutely NOT visit some city, but then offer a better one nearby :)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;August&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;San Diego, California&lt;/li&gt;
      &lt;li&gt;Las Vegas, Nevada&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Salt Lake City, Utah (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;West Yellowstone, Montana&lt;/li&gt;
      &lt;li&gt;Casper, Wyoming&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Denver, Colorado (2 weeks)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Rapid City, South Dakota&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Fargo, North Dakota (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Minneapolis, Minnesota&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Milwaukee, Wisconsin (1 week)&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;September&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;St. Louis, Missouri (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Indianapolis, Indiana (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Cincinnati, Ohio&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Cleveland, Ohio (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Upstate New York (1 week)&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;October&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Boston, Massachusetts (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Portland, Maine (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;New York City (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Washington, D.C. (1 week)&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;November&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;Norfolk, Virginia&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Charlotte, North Carolina (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Nashville, Tennessee (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Birmingham, Alabama&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;New Orleans, Louisiana (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Houston, Texas&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;South Padre Island, Texas (2 weeks)&lt;/strong&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;December&lt;/em&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Austin, Texas (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Amarillo, Texas&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Albuquerque, New Mexico (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Phoenix, Arizona&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Los Angeles, California (1 week)&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;San Francisco&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/20200814-roadtrip-map.jpg&quot; alt=&quot;Map of the trip&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I decided to search the week-long stays on Airbnb, and book the overnight stays on booking.com. On Airbnb, I put in the dates from Sunday to Saturday (or the next Sunday – depends on whether I need to drive both weekend days), filter by “has A/C” and limit the price by &lt;strong&gt;$100/night&lt;/strong&gt; (my inspiration is to fit into &lt;strong&gt;$3000/month&lt;/strong&gt;, which is comparable to what I would be paying for a 1-bedroom apartment, had I stayed in SF). Then I check the location (walkable distance to “civilization” like restaurants and grocery stores), photos, and reviews. For some reason, Airbnb doesn’t include the service fee/cleaning fee in the per-night price, so the lowest-price place might not end up being cheaper!&lt;/p&gt;

&lt;p&gt;Another gotcha is non-“instant booking” listings: one time, I submitted a request to book the place, and Airbnb told me that I’ll get a response within &lt;strong&gt;24 hours&lt;/strong&gt;… except that &lt;strong&gt;24 hours&lt;/strong&gt; later, there was no response from the host at all, and my request just expired, while I needed a place to sleep tonight. I’m not sure what is the expected behavior here (apart from booking far in advance): if I attempt to book multiple places in parallel, Airbnb warns me I can be double-charged. I’d like to be allowed some redundancy, with the first-response-wins or another strategy.&lt;/p&gt;

&lt;p&gt;Of course, to do a road trip, you need a car. In San Diego, I’ve bought a certified pre-owned 2017 Toyota Prius. Thanks to my amazing friends for helping with negotiations – that was a memorable farcical process. Pretty happy with its fuel efficiency: I can travel about &lt;strong&gt;400 miles&lt;/strong&gt; on a full tank (&lt;strong&gt;9 gallons&lt;/strong&gt;), which costs about &lt;strong&gt;$20&lt;/strong&gt;. Did you know that California has &lt;a href=&quot;https://gasprices.aaa.com/&quot;&gt;the second most expensive gas&lt;/a&gt; in the United States? Only in Hawaii it’s more expensive!&lt;/p&gt;

&lt;p&gt;Another concern is the ongoing pandemic. In United States, the number of new daily cases is staying high, and the preventive measures are varying from state to state. Many indoor sightseeings like museums and cathedrals are closed, but many outdoor ones are open and operating. I’m personally trying to stay as safe as possible, wearing face covering, ensuring distance from other people, and so on.&lt;/p&gt;

&lt;p&gt;Finally, follow my photoblog: &lt;a href=&quot;https://instagram.com/petroskoilainen&quot;&gt;https://instagram.com/petroskoilainen&lt;/a&gt;!&lt;/p&gt;</content><author><name></name></author><category term="travel," /><category term="roadtrip" /><summary type="html">This post is the summary of my travel plans, reasoning for it, and some technical details.</summary></entry><entry><title type="html">Becoming a digital nomad</title><link href="https://sergey.party/2020/06/14/digital-nomad.html" rel="alternate" type="text/html" title="Becoming a digital nomad" /><published>2020-06-14T00:00:00+00:00</published><updated>2020-06-14T00:00:00+00:00</updated><id>https://sergey.party/2020/06/14/digital-nomad</id><content type="html" xml:base="https://sergey.party/2020/06/14/digital-nomad.html">&lt;p&gt;I’m going to move out of my San Francisco apartment and spend some time living in the countryside.&lt;/p&gt;

&lt;p&gt;My company (based in SF) has been in working-from-home mode since &lt;strong&gt;Mar 9, 2020&lt;/strong&gt;. The latest communication said that we’ll be WFH until at least Labor Day (&lt;strong&gt;Sep 7, 2020&lt;/strong&gt;). There are doubts about whether it will be considered safe to return to the office at that time. Meanwhile, San Francisco is sinking into its glorious future, becoming less and less pleasant to live in. In the pandemic/quarantine/shelter-in-place situation, it’s clear big cities are overrated. I’ve recently spent a month in Crescent City (small town at the California/Oregon border), working remotely, and enjoyed it a lot. Also I realized that I only need &lt;strong&gt;1 backpack + 1 suitcase&lt;/strong&gt; worth of stuff to maintain most of my lifestyle indefinitely. So I think I’ll enjoy spending some more time in smaller towns, continuing to work remotely. I’m putting my stuff in the storage and leaving SF for a while.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;My stuff
    &lt;ul&gt;
      &lt;li&gt;For the last &lt;strong&gt;5 years&lt;/strong&gt;, I’ve been living in a half of a 2-bedroom/2-bathroom apartment in SoMa, San Francisco. It’s a pretty new and comfortable building: free gym on the second floor, arriving packages are signed for by the reception and won’t be stolen from your door, if any appliances in your apartment break they’ll fix it for you. I have washer+dryer in unit (a dream of many in SF!), walk-in shower, huge floor-to-ceiling windows, ceramic floor. At the rental price of &lt;strong&gt;$2200+utilities&lt;/strong&gt;, I consider it a great deal.&lt;/li&gt;
      &lt;li&gt;The downsides are nitpicks: all the doors are sliding, so sound isolation within the apartment is not great; the ventilation is pretty weird, feels like it’s blowing cold wind all the time, so I had to tape it down. The bigger problem is location: on 9th St and Market St, it’s a pretty rough neighborhood. You’re guaranteed to meet strange-looking people if you walk outside the building in any direction, and you better watch where you step if you care about your shoes being clean. But at the same time, it’s convenient to get to BART/MUNI Civic Center station, Walgreens/Market-on-Market grocery stores, SoMa coffeeshops. The Twitter office is right across the street (the reason I moved there in the first place) - when I worked there, my horizontal commute was shorter than my vertical commute!&lt;/li&gt;
      &lt;li&gt;Anyway, over these years I’ve accumulated quite a lot of stuff. Interestingly, mostly things that I got as gifts or planned to give as a gift. Particularly, I collect tech T-shirts as a hobby (I have about &lt;strong&gt;300&lt;/strong&gt;). Collecting T-shirts is nice because I can show off my collection, one shirt at a time. I have a circular queue rotation system so that I only wear any particular shirt once or twice a year, and a folding/stacking method to store them compactly. But even then, they take quite a lot of space. I also have more buttoned shirts, pants, and jackets that I know what to do with. Besides clothing and gifts, I have some furniture: a couch, a queen bed, a desk, a chair, a floorlamp, a nightstand - not much at all, and most of these things I bought &lt;strong&gt;7 years&lt;/strong&gt; ago, when I just arrived in US.&lt;/li&gt;
      &lt;li&gt;So, I’m going to box most of that stuff and ship it to the storage. I found &lt;a href=&quot;https://www.boomboxstorage.com/&quot;&gt;Boombox Storage&lt;/a&gt; where you can rent &lt;strong&gt;5’x10’&lt;/strong&gt; storage cell for &lt;strong&gt;$135/month&lt;/strong&gt;, &lt;strong&gt;3 months&lt;/strong&gt; minimum. They’ll come to you, pick up your packed stuff, and drive it away, then you can pick your items from the online gallery to be delivered back.&lt;/li&gt;
      &lt;li&gt;While packing, I’ll go through all the things and see if they spark joy or if I can donate them to Goodwill or throw them away.  I did that exercise several years ago (without moving anywhere), got rid of a lot of junk, and established a single place for every category of things.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;My address
    &lt;ul&gt;
      &lt;li&gt;It’s useful to have an permanent address (for banks, insurance companies, government agencies). When you don’t have a permanent address, life could be hard. I’ve &lt;a href=&quot;https://www.theupsstore.com/mailboxes&quot;&gt;rented a mailbox at UPS&lt;/a&gt; for &lt;strong&gt;$30/month&lt;/strong&gt;, &lt;strong&gt;3 months&lt;/strong&gt; minimum. This provides you with a real mailing address, not a P.O.Box one. You can come check your mailbox at any time or e-mail them to ask if there is any mail for you.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, I’m pretty excited about the change of scenery, and even about going through all my stuff. See you around!&lt;/p&gt;</content><author><name></name></author><category term="travel," /><category term="sf" /><summary type="html">I’m going to move out of my San Francisco apartment and spend some time living in the countryside.</summary></entry><entry><title type="html">How to get 100 job offers in two weeks</title><link href="https://sergey.party/2018/08/20/hiring-platforms.html" rel="alternate" type="text/html" title="How to get 100 job offers in two weeks" /><published>2018-08-20T00:00:00+00:00</published><updated>2018-08-20T00:00:00+00:00</updated><id>https://sergey.party/2018/08/20/hiring-platforms</id><content type="html" xml:base="https://sergey.party/2018/08/20/hiring-platforms.html">&lt;p&gt;I often get asked how did I choose a job, or how to choose a software engineering jobs in general. I believe the first step is to explore what’s out there and find out what you are worth (arguably it’s useful even if you’re not looking for the job right now). The current reality is that the demand for the engineers is higher than the supply, so naturally it makes sense for companies to reach out to you, instead of you reaching out to them.&lt;/p&gt;

&lt;p&gt;Below you will find the list of services that are reflecting this philosophy. They are non-traditional in the sense that they’re not simple job boards where you apply by sending your resume. They provide a higher level of service: either by matching you with companies, or by allowing you to stand out to them. They are free to use for you - they get paid by companies when you’re hired through them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I have personally tried all these services (in 2016-2017) and got contacted by companies. Some of the links are referral links. Many of the services will only work with you if you’re looking for job in United States/Bay Area. Some will only work with you if you’re authorized to work in US.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://triplebyte.com/iv/SSbrWcs/cp&quot;&gt;&lt;strong&gt;TripleByte&lt;/strong&gt;&lt;/a&gt; conducts their own technical phone screen with you, and then matches you with companies. If you’re interested in any of those companies, you can go directly to onsite interview. I think this is a nice approach that allows you to skip repetitive phone screens. They supposedly also help you to negotiate your offer. Last time I used it, I’ve got about 6 hand-picked matches.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two have a selection of partner companies. You choose a company, solve their coding challenge online, and then the company contacts you.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.hackerrank.com/jobs/search&quot;&gt;&lt;strong&gt;HackerRank Jobs&lt;/strong&gt;&lt;/a&gt; is a programming competition website. They run competitions sponsored by employers so that the sponsor can contact the top performers. This is how I got my first full-time job in US! However, they also have a section where you can apply to companies by solving their coding test. No competition participation needed.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://app.codesignal.com/signup/sGDw5eo6AQbunS4Ah/main&quot;&gt;&lt;strong&gt;CodeSignal&lt;/strong&gt;&lt;/a&gt; is also a tournament website. They have a “Company Challenges” section with several companies, where they claim that “solving these challenges will increase your chances of getting matched to open positions”. They also have a “Jobs” section where you can create a profile and get matched with companies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many services where you fill out the profile, they put you in an “auction” where companies “bid” on you, and you decide which ones to interview with. This is really reversing the job search. The auction is usually limited-time. The best part is that companies make you “pre-offers” with salary and equity, so you don’t waste time if those numbers are too low. However, it does require some investment on your side to fill out and polish your profile (specify your experience, desired industry, technologies, etc.) - but I believe it’s worth it.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://hired.com/x/ph5hL7&quot;&gt;&lt;strong&gt;Hired&lt;/strong&gt;&lt;/a&gt; probably has been there the longest of them all. They have a lot of small (1-15 employees) companies. Last time I tried it, I’ve got 37 pre-offers in two weeks.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://prime.indeed.com/refer/c-p1Ez8ev&quot;&gt;&lt;strong&gt;Indeed Prime&lt;/strong&gt;&lt;/a&gt; is a pretty similar service. About 27 pre-offers in two weeks.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://alist.co/candidates/refer/9078&quot;&gt;&lt;strong&gt;A-List&lt;/strong&gt;&lt;/a&gt; showcases you to startups on AngelList.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://fbuy.me/eygyL&quot;&gt;&lt;strong&gt;Vettery&lt;/strong&gt;&lt;/a&gt; is based in NY and their companies are mostly from NY (or at least that was my impression). About 20 pre-offers in two weeks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some services are similar to the above: they let you fill out the profile and then companies send you pre-offers - but the “auction” is not time-limited. However, I find that the interest to your profile is only high for a few weeks.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.gloat.com/i/fGQ4vL&quot;&gt;&lt;strong&gt;Gloat&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://woo.io/inv/INC-955274518faab936ef156bfed1502581&quot;&gt;&lt;strong&gt;Woo.io&lt;/strong&gt;&lt;/a&gt;. About 10 matches in the most active month.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://underdog.io&quot;&gt;&lt;strong&gt;Underdog&lt;/strong&gt;&lt;/a&gt;. About 6 matches in the first week.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://anthology.co&quot;&gt;&lt;strong&gt;Anthology&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, during my last job search, I got contacted by about 100 companies (not including the ones reaching out through e-mail or LinkedIn). Your mileage may vary! I talked to about 25, went to 17 onsites, got 7 offers, and accepted one :) Good luck!&lt;/p&gt;</content><author><name></name></author><category term="engineering," /><category term="job," /><category term="career," /><category term="interviews" /><summary type="html">I often get asked how did I choose a job, or how to choose a software engineering jobs in general. I believe the first step is to explore what’s out there and find out what you are worth (arguably it’s useful even if you’re not looking for the job right now). The current reality is that the demand for the engineers is higher than the supply, so naturally it makes sense for companies to reach out to you, instead of you reaching out to them.</summary></entry><entry><title type="html">Thoughts on engineering process</title><link href="https://sergey.party/2018/08/02/thoughts-on-engineering-process.html" rel="alternate" type="text/html" title="Thoughts on engineering process" /><published>2018-08-02T00:00:00+00:00</published><updated>2018-08-02T00:00:00+00:00</updated><id>https://sergey.party/2018/08/02/thoughts-on-engineering-process</id><content type="html" xml:base="https://sergey.party/2018/08/02/thoughts-on-engineering-process.html">&lt;p&gt;Engineering is solving problems. Software engineering is solving problems with software. &lt;em&gt;Professional&lt;/em&gt; SWE is doing so sustainably, repeatedly, continuously. This means some kind of process is necessary.&lt;/p&gt;

&lt;h1 id=&quot;understand-the-problem&quot;&gt;Understand the problem&lt;/h1&gt;

&lt;p&gt;It starts with problems. It’s very rare to see someone asking: &lt;em&gt;what problem are you trying to solve?&lt;/em&gt; I personally ask it all the time. &lt;em&gt;What are the goals we want to achieve?&lt;/em&gt; &lt;em&gt;What usecases do we expect?&lt;/em&gt; Understanding the problem makes the next steps much easier, and you eliminate a lot of failure cases. It’s very cheap to iterate on problem statement. It’s very expensive to jump to implementation and at the end, realize that the problem was underspecified, or that you were solving the wrong problem all this time.
This applies both to high-level goals as well as to day-to-day tasks. Of course, the cost of mistake is higher in the former case.&lt;/p&gt;

&lt;p&gt;When someone tells you to do something, and especially says it’s urgent, it’s tempting to just jump into coding. Stop. Are you sure you understand what problem they want to solve? Separate &lt;em&gt;what they want to achieve&lt;/em&gt; (the problem) from &lt;em&gt;what they tell you to do&lt;/em&gt; (the “solution”). Engage your critical thinking. It will often be the case that the actual thing that needs to be done is very different from what they told you originally.&lt;/p&gt;

&lt;p&gt;Similarly, when someone asks you how to do something, it’s tempting to just offer an advice. Stop. Are you sure you understand what problem they are trying to solve? Separate &lt;em&gt;what they want to do&lt;/em&gt; (the problem) from &lt;em&gt;what they ask you how to do&lt;/em&gt; (the “solution”). It’s called &lt;a href=&quot;https://en.wikipedia.org/wiki/XY_problem&quot;&gt;the XY problem&lt;/a&gt;. It will often be the case that you need that context to provide better advice.&lt;/p&gt;

&lt;p&gt;Clarify the requirements ruthlessly. Get them to open a ticket. Bonus points if you can agree on metrics to define &lt;em&gt;how do you know&lt;/em&gt; when the problem is solved.&lt;/p&gt;

&lt;h1 id=&quot;design-before-implementation&quot;&gt;Design before implementation&lt;/h1&gt;

&lt;p&gt;When you understand the problem, it’s tempting to jump straight to the implementation. Stop. Think about your approach, on a high level. How can it fail? Again, engage your critical thinking: you can foresee a lot of failure cases. It’s possible that at this point you’ll realize the problem is underspecified - it’s great, go back and clarify the requirements. Consider different approaches and compare them. A little imagination goes a long way. Again, it’s cheap to iterate on the design. It’s very expensive to jump to implementation and at the end, realize that there already exists a solution, or there is a better approach.
This applies both to large long-term projects as well as day-to-day ones. Of course, the cost of mistake is higher in the former case.&lt;/p&gt;

&lt;p&gt;Describe your approach. For larger projects, write a design doc. Iterate on it. Ask others to review it. State the motivation, goals, non-goals, limitations, and risks. Come up with alternative solutions and compare them.&lt;/p&gt;

&lt;p&gt;If you think hard and make important design decisions at the design stage, you’ll notice that the implementation actually becomes pretty much straightforward. The explicitly written problem statement and the high-level design document will make the code review much easier. They will also be helpful when someone else many months later will try to understand why things are implemented the way they are.&lt;/p&gt;

&lt;h1 id=&quot;bottom-line&quot;&gt;Bottom line&lt;/h1&gt;
&lt;p&gt;For smaller tasks, the temptation to shortcut the process is higher. It’s understandable; at a minimum, use the ticket to iterate on the problem statement and the approach. At the end of the day, it’s the result that counts. And the result is that you’ll be solving exactly the problem you want to solve, using the most suitable approach that you chose consciously.&lt;/p&gt;

&lt;p&gt;Always ask what problem are we actually trying to solve. Don’t waste time solving the wrong problem. Don’t overengineer. Think about failure cases. Ship reliable software.&lt;/p&gt;</content><author><name></name></author><category term="problem," /><category term="solution," /><category term="design," /><category term="engineering" /><summary type="html">Engineering is solving problems. Software engineering is solving problems with software. Professional SWE is doing so sustainably, repeatedly, continuously. This means some kind of process is necessary.</summary></entry><entry><title type="html">A case for written communication</title><link href="https://sergey.party/2018/07/21/written-communication.html" rel="alternate" type="text/html" title="A case for written communication" /><published>2018-07-21T00:00:00+00:00</published><updated>2018-07-21T00:00:00+00:00</updated><id>https://sergey.party/2018/07/21/written-communication</id><content type="html" xml:base="https://sergey.party/2018/07/21/written-communication.html">&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; In corporate world, explicit written communication is better than word-of-mouth and telephone game. Most meetings are better when they start and finish with a written document. To become a happy and productive team, ask to put things in writing.&lt;/p&gt;

&lt;h1 id=&quot;human-memory-vs-written-documents&quot;&gt;Human memory vs. written documents&lt;/h1&gt;

&lt;p&gt;One day you might notice that none of your team meetings have any writing associated with it. It’s almost like every meeting you have has no agenda in the beginning and no action items at the end - except they most often do, but that information is all “remembered” - stored in people’s memories.&lt;/p&gt;

&lt;p&gt;Human memory is a bad storage. Having to remember too many things induces stress. Accessing memorized information needs the human to be alive, awake, and employed. It decays over time. It’s barely searchable. There is no way of making sure it’s synchronized (as in, my memory is the same as yours). The only way of sharing it is through speaking. Speaking is a lossy protocol: you said one thing, I heard another, and remembered a third. Speaking is synchronous: it requires ongoing attention from all participants. Speaking is non-repeatable: what was said once has to be repeated for another person at the same cost.&lt;/p&gt;

&lt;p&gt;On the other hand, written document is great storage. Information there is preserved after layoffs. It’s trivial to have a single version of it. It never decays. It’s searchable. After single-time cost of writing, it can be shared in its entirety for free. Writing is lossy as well, but the chance of miscommunication is lower. It’s asynchronous: readers can consume it whenever it’s convenient. Written document can be shared with more readers later. Moreover, it can be evolved over time, collaboratively. For most people, typing is as fast as speaking and reading is faster than listening.&lt;/p&gt;

&lt;h1 id=&quot;when-you-should-prefer-writing&quot;&gt;When you should prefer writing&lt;/h1&gt;

&lt;p&gt;Spoken communication is great for synchronous bidirectional interactions: 1:1s, brainstorms, status updates, pair programming. It’s great for hackathon-style development: small team with short-term goals, all physically in the same room. But there is no excuse not to have something in writing when the communication involves many people and potentially more later, or when the information is important to get right, or when the information is expected to have a long lifetime and be referred to repeatedly, or when it requires feedback from everyone but not immediately: planning on monthly/weekly scale, requirements, design documents, APIs.&lt;/p&gt;

&lt;p&gt;Writing is great tool for meetings: it encourages coherent thought, things become explicit, hidden problems bubble up and provoke questions from co-authors and readers both. Why would you use a sub-optimal tool when there is a better one? If you’re holding a meeting, at least sketch an agenda; it will structure the discussion and limit misunderstanding. During the meeting, flesh out the agenda items as you go through them and make decisions. Use the resulting document for the next meeting in series. Bonus points if you can simply share the document and ask for feedback instead of holding a meeting. Any meeting that did not result in any written document means time spent sub-optimally.&lt;/p&gt;

&lt;p&gt;Push for more things stated in writing. If someone is hesitant to do so, they value neither their time nor yours. They want to hoard tribal knowledge in their heads. They don’t want you to be productive in the long term. They don’t want to encourage feedback. They want to perpetuate hackathon culture. They want to make your life stressful.&lt;/p&gt;</content><author><name></name></author><category term="communication," /><category term="reading," /><category term="writing" /><summary type="html">TL;DR: In corporate world, explicit written communication is better than word-of-mouth and telephone game. Most meetings are better when they start and finish with a written document. To become a happy and productive team, ask to put things in writing.</summary></entry><entry><title type="html">What is compensation made of</title><link href="https://sergey.party/2018/06/03/what-is-compensation-made-of.html" rel="alternate" type="text/html" title="What is compensation made of" /><published>2018-06-03T00:00:00+00:00</published><updated>2018-06-03T00:00:00+00:00</updated><id>https://sergey.party/2018/06/03/what-is-compensation-made-of</id><content type="html" xml:base="https://sergey.party/2018/06/03/what-is-compensation-made-of.html">&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; This post is about software engineer compensation in United States. I’m using &lt;strong&gt;40%&lt;/strong&gt; total tax rate in calculations (can be lower depending on your family situation). I am also not a lawyer or tax advisor.&lt;/p&gt;

&lt;p&gt;Sometimes you find yourself in the situation where you have multiple job offers and need to choose between them. There are many variables that should come into your decision, and one of them is comparing the financial side of the offers. Some of the components are not obvious, so this post will hopefully help you to evaluate the total fair market value of your offers. Make sure to ask your contacts at the company about these items, if they are not explicitly stated in writing.&lt;/p&gt;

&lt;h1 id=&quot;basics&quot;&gt;Basics&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Base yearly salary.&lt;/strong&gt; Pretty clear. Usually paid every other week. For Bay Area, varies within &lt;strong&gt;$100k-200k&lt;/strong&gt; for most people, depending on experience. Taxed at about &lt;strong&gt;38%&lt;/strong&gt; (&lt;strong&gt;22%&lt;/strong&gt; federal tax + &lt;strong&gt;8%&lt;/strong&gt; California tax + &lt;strong&gt;6%&lt;/strong&gt; Social Security tax + &lt;strong&gt;2%&lt;/strong&gt; other taxes).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Equity: RSU/options.&lt;/strong&gt; There are &lt;a href=&quot;https://github.com/jlevy/og-equity-compensation&quot;&gt;multiple&lt;/a&gt; &lt;a href=&quot;https://gist.github.com/yossorion/4965df74fd6da6cdc280ec57e83a202d&quot;&gt;articles&lt;/a&gt; explaining how to evaluate equity, so I won’t go into details. RSU profits are taxed a bit higher than cash salary. Usually equity is vesting over four years, so total yearly compensation is calculated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base_salary + equity_grant / 4&lt;/code&gt;. However, there are other components in the equation.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;bonuses&quot;&gt;Bonuses&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Signing bonus.&lt;/strong&gt; One-time taxable bonus, usually is given with the first paycheck. Can be as high as &lt;strong&gt;$100,000&lt;/strong&gt; (e.g., for returning interns at Facebook). If you leave the company before your first anniversary, you would be asked to return it pro-rated for the rest of the year. Can be easier to negotiate than the base salary: for the company it’s a one-time expense, as opposed to a recurring one.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Relocation bonus.&lt;/strong&gt; Also taxable and usually given with the first paycheck (which means you will need to spend your own money until then). Can also be non-cash: the company might pay for the ticket and provide accommodation e.g. for the first month.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Annual bonus policy.&lt;/strong&gt; Profitable companies often have a “target” annual bonus which can be &lt;strong&gt;10%&lt;/strong&gt; or &lt;strong&gt;15%&lt;/strong&gt; of the base salary, and the actual bonus for the year is somehow computed from the company performance and your personal performance.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Raises/promotion policy.&lt;/strong&gt; Usually the companies at least try to keep up with inflation, so you can expect &lt;strong&gt;1-2%&lt;/strong&gt; raise yearly. However, you should also keep in mind whether it’s going to be easy to get promoted (and get a raise/bonus associated with it).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance/per-project bonus policy.&lt;/strong&gt; Some companies reward engineers based on evidence they’ve put in good work: after a performance review, or after a release was shipped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonuses might be taxed a bit higher than your normal pay.&lt;/p&gt;

&lt;h1 id=&quot;other-perks&quot;&gt;Other perks&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;ESPP.&lt;/strong&gt; You can set off some post-tax money from your paycheck to buy your company stock at a discount, e.g. &lt;strong&gt;15%&lt;/strong&gt;. The amount that can go towards the purchase is limited by the Purchase Plan at some % (e.g. again &lt;strong&gt;15%&lt;/strong&gt;) of your base salary, and by IRS at &lt;strong&gt;$25,000&lt;/strong&gt; per calendar year - whichever is lower - so the minimum profit (if you sell immediately) is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.15 x min(25000, &amp;lt;base&amp;gt;x0.15)&lt;/code&gt;, which is &lt;strong&gt;2.25%&lt;/strong&gt; of your base or &lt;strong&gt;$3,750&lt;/strong&gt;, whichever is smaller. The better the discount and the better your stock is doing, the better your profit.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PTO/sick leave policy.&lt;/strong&gt; There are &lt;strong&gt;50&lt;/strong&gt; full business weeks in a year. If two companies offer the same base pay, but company A offers &lt;strong&gt;15 days (3 weeks)&lt;/strong&gt; of vacation, and company B offers &lt;strong&gt;20 days (4 weeks)&lt;/strong&gt;, that extra week is equivalent to getting &lt;strong&gt;1/50 = 2%&lt;/strong&gt; of the base pay without having to work for it. You basically get paid extra &lt;strong&gt;2%&lt;/strong&gt; for every extra vacation week over baseline. Be cautious about the “unlimited PTO” policy: as you can guess, it’s not &lt;em&gt;really&lt;/em&gt; unlimited - either you come up with a guilt-induced limit for yourself, or you hit the informal unspoken limit, beyond which your manager is not comfortable with your absence.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;401(k) matching.&lt;/strong&gt; Some companies encourage you to set aside a portion of your paycheck (pre-tax) to save for your retirement, by matching your contribution to your retirement account. If the company has this program (not just &lt;em&gt;401(k)&lt;/em&gt; program, but &lt;em&gt;401(k) matching&lt;/em&gt; program), then for every dollar you contribute, the company will contribute &lt;strong&gt;50&lt;/strong&gt; cents (if they match &lt;strong&gt;50%&lt;/strong&gt;) or a dollar (if they match &lt;strong&gt;100%&lt;/strong&gt;). There is a limit set by government on how much you can contribute per year (&lt;strong&gt;$18,500&lt;/strong&gt; as of 2018). Additionally, the company may have a limit of how much they will contribute (e.g. &lt;strong&gt;$1,500&lt;/strong&gt;/year at Twitter). The company’s one-time contribution happens after the year has ended, e.g. in mid-February of the next year.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;reimbursements-and-subsidies&quot;&gt;Reimbursements and subsidies&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Free/subsidized food.&lt;/strong&gt; In Bay Area, this is easily worth &lt;strong&gt;$20-$30&lt;/strong&gt; per meal, &lt;strong&gt;$50-60&lt;/strong&gt; with dinners. &lt;strong&gt;$25&lt;/strong&gt; daily (post-tax) = &lt;strong&gt;$10500&lt;/strong&gt;/year (pre-tax).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Shuttles/commuter subsidy/Uber credits.&lt;/strong&gt; Depending on how you commute, every &lt;strong&gt;$12&lt;/strong&gt; (post-tax) spent on a daily commute are worth &lt;strong&gt;$5000&lt;/strong&gt;/year (pre-tax).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Reimbursement policy:&lt;/strong&gt; books, conferences, travel, offsites. This could be easily worth thousands per year.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Gym reimbursement.&lt;/strong&gt; In Bay Area, this can cost &lt;strong&gt;$50-150&lt;/strong&gt; per month (post-tax), so gym reimbursement is equivalent to &lt;strong&gt;$600-1800&lt;/strong&gt;/year (post-tax) or &lt;strong&gt;$1000-3000&lt;/strong&gt;/year (pre-tax).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Insurance premiums&lt;/strong&gt; for you and your family. This is &lt;strong&gt;$200-300&lt;/strong&gt; per month = &lt;strong&gt;$2400-3600&lt;/strong&gt;/year. However, almost every company takes care of that for you, so it’s unlikely to be a decision factor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;other-concerns&quot;&gt;Other concerns&lt;/h1&gt;

&lt;p&gt;If you’re choosing between different states, take into account:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;state taxes&lt;/strong&gt; (varies &lt;a href=&quot;https://en.wikipedia.org/wiki/State_income_tax&quot;&gt;from &lt;strong&gt;0%&lt;/strong&gt; to &lt;strong&gt;13%&lt;/strong&gt;&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;cost of living&lt;/strong&gt; (especially rent - expect to spend $2000-$3000/month (post-tax) = &lt;strong&gt;$40k-60k&lt;/strong&gt; (pre-tax) in Bay Area)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;sales tax&lt;/strong&gt; (varies &lt;a href=&quot;https://taxfoundation.org/state-and-local-sales-tax-rates-in-2017/&quot;&gt;within &lt;strong&gt;6-9%&lt;/strong&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should help you to put a price tag on many perks and benefits that the companies do or don’t provide. Remember that it may be short-sighted to compare companies from the financial side only, and also to evaluate offers based on the current stock price or current startup valuation. Who knows what will happen in a couple years? Good luck!&lt;/p&gt;</content><author><name></name></author><category term="compensation," /><category term="money," /><category term="offers" /><summary type="html">Disclaimer: This post is about software engineer compensation in United States. I’m using 40% total tax rate in calculations (can be lower depending on your family situation). I am also not a lawyer or tax advisor.</summary></entry><entry><title type="html">How to call Scala UDFs from PySpark</title><link href="https://sergey.party/2017/02/17/how-to-call-scala-udf-from-pyspark.html" rel="alternate" type="text/html" title="How to call Scala UDFs from PySpark" /><published>2017-02-17T00:00:00+00:00</published><updated>2017-02-17T00:00:00+00:00</updated><id>https://sergey.party/2017/02/17/how-to-call-scala-udf-from-pyspark</id><content type="html" xml:base="https://sergey.party/2017/02/17/how-to-call-scala-udf-from-pyspark.html">&lt;h1 id=&quot;motivation&quot;&gt;Motivation&lt;/h1&gt;

&lt;p&gt;Suppose we decided to speed up our PySpark job. One possible way to this is to write a Scala UDF.
If we implement it in Scala instead of Python, the Spark workers will execute the computation themselves rather than ask Python code to do it, and won’t need to serialize/deserialize the data to the Python component. Double win!&lt;/p&gt;

&lt;p&gt;So, we can write a simple Scala object with a single function in it. Then we wrap the function in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;udf&lt;/code&gt;. I sincerely apologize for the imperative style.&lt;/p&gt;

&lt;div class=&quot;language-scala highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.spark.udfs&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.apache.spark.sql.expressions.UserDefinedFunction&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.apache.spark.sql.functions.udf&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Primes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isPrime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Boolean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nf&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isPrimeUDF&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefinedFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;udf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;isPrime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/com/example/spark/udfs/Primes.scala&lt;/code&gt; or under any other convenient name.&lt;/p&gt;

&lt;h1 id=&quot;compiling-scala&quot;&gt;Compiling Scala&lt;/h1&gt;

&lt;p&gt;We don’t need heavyweight IDEs or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sbt&lt;/code&gt; to compile such a simple function. Let’s do it from the command line! We would need to use Scala 2.11 when compiling (as of Spark 2.0.1/2.1.0). &lt;a href=&quot;http://www.scala-lang.org/download/2.11.8.html&quot;&gt;Download Scala 2.11&lt;/a&gt; and unpack it somewhere (I unpacked it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/Downloads/Distribs/scala-2.11.8&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Setup the environment variables:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;SCALA_HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/Downloads/Distribs/scala-2.11.8
&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$SCALA_HOME&lt;/span&gt;/bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure we also have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$SPARK_HOME&lt;/code&gt; set:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SPARK_HOME&lt;/span&gt;
/Users/sserebryakov/spark-2.0.1-bin-hadoop2.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check that we can invoke &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scalac&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scalac &lt;span class=&quot;nt&quot;&gt;-version&lt;/span&gt;
Scala compiler version 2.11.8 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; Copyright 2002-2016, LAMP/EPFL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go to our project folder and build a JAR. Note that the wildcard is just a star, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.jar&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;scalac &lt;span class=&quot;nt&quot;&gt;-classpath&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SPARK_HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/jars/*&quot;&lt;/span&gt; src/com/example/spark/udfs/Primes.scala &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; primes_udf.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;running-pyspark&quot;&gt;Running PySpark&lt;/h1&gt;

&lt;p&gt;Now we can provide the JAR in the classpath for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyspark&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;pyspark &lt;span class=&quot;nt&quot;&gt;--jars&lt;/span&gt; primes_udf.jar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we can use the Scala function like this. The prerequisite is that the data should already be in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DataFrame&lt;/code&gt; format. Be careful with the syntax, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py4j&lt;/code&gt; exceptions are rarely helpful.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyspark.sql&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Row&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pyspark.sql.column&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_to_java_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_to_seq&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parallelize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;toDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;scala_udf_is_prime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_jvm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;udfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Primes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isPrimeUDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;is_prime_column&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scala_udf_is_prime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_to_seq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_to_java_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;withColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'is_prime'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_prime_column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;df&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'number'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will print:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;+------+--------+&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_prime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+------+--------+&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+------+--------+&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tested with Spark 2.0.1, but should work for 2.1.0 (the latest as of this writing) as well.&lt;/p&gt;

&lt;h1 id=&quot;currying&quot;&gt;Currying&lt;/h1&gt;

&lt;p&gt;If we want to initialize the Scala class with some arguments once, and call the function repeatedly, we can use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case class&lt;/code&gt;. Example:&lt;/p&gt;

&lt;div class=&quot;language-scala highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;com.example.spark.udfs&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.apache.spark.sql.expressions.UserDefinedFunction&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;org.apache.spark.sql.functions.udf&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DistanceComputer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;originX&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;distanceUDF&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;UserDefinedFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;udf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;Double&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originX&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;math&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;pow&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;originY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Registering with PySpark would look like:&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;scala_udf_distance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_jvm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;udfs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DistanceComputer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;distanceUDF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure to conform exactly to the argument types when calling the constructor. A constructor with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Double&lt;/code&gt; argument should be called with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;</content><author><name></name></author><category term="scala," /><category term="spark," /><category term="pyspark" /><summary type="html">Motivation</summary></entry></feed>