summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2017-06-11 13:34:46 +0200
committerJakob Unterwurzacher2017-06-11 21:56:16 +0200
commite52594dae67a5f2532a2e65abaafc8c993fb2db4 (patch)
tree5568ab07d80f0c3b153c05c129da22b4c9e063a0
parent24a7b1b7b8108a1f74b0bf34a8d6c2bdd1ab8dcd (diff)
contentenc: parallelize encryption for 128kiB writes
128kiB = 32 x 4kiB pages is the maximum we get from the kernel. Splitting up smaller writes is probably not worth it. Parallelism is limited to two for now.
-rw-r--r--internal/contentenc/content.go36
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 {