diff options
| -rw-r--r-- | internal/contentenc/content.go | 36 | 
1 files changed, 33 insertions, 3 deletions
| diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 3643f56..3f578e8 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -7,6 +7,8 @@ import (  	"encoding/hex"  	"errors"  	"log" +	"runtime" +	"sync"  	"github.com/rfjakob/gocryptfs/internal/cryptocore"  	"github.com/rfjakob/gocryptfs/internal/stupidgcm" @@ -149,14 +151,42 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b  	return plaintext, nil  } +// At some point, splitting the ciphertext into more groups will not improve +// performance, as spawning goroutines comes at a cost. +// 2 seems to work ok for now. +const encryptMaxSplit = 2 +  // EncryptBlocks is like EncryptBlock but takes multiple plaintext blocks.  func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint64, fileID []byte) []byte { -	// Encrypt piecewise. This allows easy parallization in the future.  	ciphertextBlocks := make([][]byte, len(plaintextBlocks)) -	be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID) +	// For large writes, we parallelize encryption. +	if len(plaintextBlocks) >= 32 { +		ncpu := runtime.NumCPU() +		if ncpu > encryptMaxSplit { +			ncpu = encryptMaxSplit +		} +		groupSize := len(plaintextBlocks) / ncpu +		var wg sync.WaitGroup +		for i := 0; i < ncpu; i++ { +			wg.Add(1) +			go func(i int) { +				low := i * groupSize +				high := (i + 1) * groupSize +				if i == ncpu-1 { +					// Last group, pick up any left-over blocks +					high = len(plaintextBlocks) +				} +				be.doEncryptBlocks(plaintextBlocks[low:high], ciphertextBlocks[low:high], firstBlockNo+uint64(low), fileID) +				wg.Done() +			}(i) +		} +		wg.Wait() +	} else { +		be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID) +	}  	// Concatenate ciphertext into a single byte array.  	// Size the output buffer for the maximum possible size (all blocks complete) -	// to allocations in out.Write() +	// to prevent further allocations in out.Write()  	tmp := make([]byte, len(plaintextBlocks)*int(be.CipherBS()))  	out := bytes.NewBuffer(tmp[:0])  	for _, v := range ciphertextBlocks { | 
