diff --git a/src/lua/buffer.lua b/src/lua/buffer.lua
index 503f753c264179d3fe7cbd03c2e2f0fb3f4804e9..ef0118c7c44a4bb2269a5e4ff3b604e7ae72fef4 100644
--- a/src/lua/buffer.lua
+++ b/src/lua/buffer.lua
@@ -36,6 +36,34 @@ ibuf_reserve_slow(struct ibuf *ibuf, size_t size);
 
 void *
 lua_static_aligned_alloc(size_t size, size_t alignment);
+
+/**
+ * Register is a buffer to use with FFI functions, which usually
+ * operate with pointers to scalar values like int, char, size_t,
+ * void *. To avoid doing 'ffi.new(<type>[1])' on each such FFI
+ * function invocation, a module can use one of attributes of the
+ * register union.
+ *
+ * Naming policy of the attributes is easy to remember:
+ * 'a' for array type + type name first letters + 'p' for pointer.
+ *
+ * For example:
+ * - int[1] - <a>rray of <i>nt - ai;
+ * - const unsigned char *[1] -
+ *       <a>rray of <c>onst <u>nsigned <c>har <p> pointer - acucp.
+ */
+union c_register {
+    size_t as[1];
+    void *ap[1];
+    int ai[1];
+    char ac[1];
+    const unsigned char *acucp[1];
+    unsigned long aul[1];
+    uint16_t u16;
+    uint32_t u32;
+    uint64_t u64;
+    int64_t i64;
+};
 ]]
 
 local builtin = ffi.C
@@ -205,9 +233,23 @@ local function static_alloc(type, size)
     return ffi.new(type..'[?]', size)
 end
 
+--
+-- Sometimes it is wanted to use several temporary registers at
+-- once. For example, when a C function takes 2 C pointers. Then
+-- one register is not enough - its attributes share memory. Note,
+-- registers are not allocated with separate ffi.new calls
+-- deliberately. With a single allocation they fit into 1 cache
+-- line, and reduce the heap fragmentation.
+--
+local reg_array = ffi.new('union c_register[?]', 2)
+
 return {
     ibuf = ibuf_new;
     IBUF_SHARED = ffi.C.tarantool_lua_ibuf;
     READAHEAD = READAHEAD;
     static_alloc = static_alloc,
+    -- Keep reference.
+    reg_array = reg_array,
+    reg1 = reg_array[0],
+    reg2 = reg_array[1]
 }
diff --git a/src/lua/crypto.lua b/src/lua/crypto.lua
index cb37a73fbe9c202cce622b1f1efbd58ae131b0c7..d23ba8e3a7b9d32d8ea9b4bbd848aae45360402f 100644
--- a/src/lua/crypto.lua
+++ b/src/lua/crypto.lua
@@ -2,6 +2,7 @@
 
 local ffi = require('ffi')
 local buffer = require('buffer')
+local reg = buffer.reg1
 
 ffi.cdef[[
     /* from openssl/err.h */
@@ -102,7 +103,6 @@ local function digest_new(digest)
         digest = digest,
         buf = buffer.ibuf(64),
         initialized = false,
-        outl = ffi.new('int[1]')
     }, digest_mt)
     self:init()
     return self
@@ -132,10 +132,10 @@ local function digest_final(self)
         return error('Digest not initialized')
     end
     self.initialized = false
-    if ffi.C.EVP_DigestFinal_ex(self.ctx, self.buf.wpos, self.outl) ~= 1 then
+    if ffi.C.EVP_DigestFinal_ex(self.ctx, self.buf.wpos, reg.ai) ~= 1 then
         return error('Can\'t finalize digest: ' .. openssl_err_str())
     end
-    return ffi.string(self.buf.wpos, self.outl[0])
+    return ffi.string(self.buf.wpos, reg.ai[0])
 end
 
 local function digest_free(self)
@@ -174,9 +174,7 @@ local function hmac_new(digest, key)
     local self = setmetatable({
         ctx = ctx,
         digest = digest,
-        buf = buffer.ibuf(64),
         initialized = false,
-        outl = ffi.new('int[1]')
     }, hmac_mt)
     self:init(key)
     return self
@@ -206,10 +204,11 @@ local function hmac_final(self)
         return error('HMAC not initialized')
     end
     self.initialized = false
-    if ffi.C.HMAC_Final(self.ctx, self.buf.wpos, self.outl) ~= 1 then
+    local buf = buffer.static_alloc('char', 64)
+    if ffi.C.HMAC_Final(self.ctx, buf, reg.ai) ~= 1 then
         return error('Can\'t finalize HMAC: ' .. openssl_err_str())
     end
-    return ffi.string(self.buf.wpos, self.outl[0])
+    return ffi.string(buf, reg.ai[0])
 end
 
 local function hmac_free(self)
diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua
index 609334cffeafd67b77f3c08b469dd7c9b1ac4f18..bfeedbc4b8676acef345f120d96d6ff91f260a10 100644
--- a/src/lua/msgpackffi.lua
+++ b/src/lua/msgpackffi.lua
@@ -21,19 +21,10 @@ float
 mp_decode_float(const char **data);
 double
 mp_decode_double(const char **data);
-union tmpint {
-    uint16_t u16;
-    uint32_t u32;
-    uint64_t u64;
-};
 ]])
 
 local strict_alignment = (jit.arch == 'arm')
-
-local tmpint
-if strict_alignment then
-   tmpint = ffi.new('union tmpint[1]')
-end
+local reg = buffer.reg1
 
 local function bswap_u16(num)
     return bit.rshift(bit.bswap(tonumber(num)), 16)
@@ -70,10 +61,10 @@ end
 local encode_u16
 if strict_alignment then
     encode_u16 = function(buf, code, num)
-        tmpint[0].u16 = bswap_u16(num)
+        reg.u16 = bswap_u16(num)
         local p = buf:alloc(3)
         p[0] = code
-        ffi.copy(p + 1, tmpint, 2)
+        ffi.copy(p + 1, reg, 2)
     end
 else
     encode_u16 = function(buf, code, num)
@@ -86,11 +77,10 @@ end
 local encode_u32
 if strict_alignment then
     encode_u32 = function(buf, code, num)
-        tmpint[0].u32 =
-            ffi.cast('uint32_t', bit.bswap(tonumber(num)))
+        reg.u32 = ffi.cast('uint32_t', bit.bswap(tonumber(num)))
         local p = buf:alloc(5)
         p[0] = code
-        ffi.copy(p + 1, tmpint, 4)
+        ffi.copy(p + 1, reg, 4)
     end
 else
     encode_u32 = function(buf, code, num)
@@ -104,10 +94,10 @@ end
 local encode_u64
 if strict_alignment then
     encode_u64 = function(buf, code, num)
-        tmpint[0].u64 = bit.bswap(ffi.cast('uint64_t', num))
+        reg.u64 = bit.bswap(ffi.cast('uint64_t', num))
         local p = buf:alloc(9)
         p[0] = code
-        ffi.copy(p + 1, tmpint, 8)
+        ffi.copy(p + 1, reg, 8)
     end
 else
     encode_u64 = function(buf, code, num)
@@ -324,9 +314,9 @@ end
 local decode_u16
 if strict_alignment then
     decode_u16 = function(data)
-        ffi.copy(tmpint, data[0], 2)
+        ffi.copy(reg, data[0], 2)
         data[0] = data[0] + 2
-        return tonumber(bswap_u16(tmpint[0].u16))
+        return tonumber(bswap_u16(reg.u16))
     end
 else
     decode_u16 = function(data)
@@ -339,10 +329,10 @@ end
 local decode_u32
 if strict_alignment then
     decode_u32 = function(data)
-        ffi.copy(tmpint, data[0], 4)
+        ffi.copy(reg, data[0], 4)
         data[0] = data[0] + 4
         return tonumber(
-            ffi.cast('uint32_t', bit.bswap(tonumber(tmpint[0].u32))))
+            ffi.cast('uint32_t', bit.bswap(tonumber(reg.u32))))
     end
 else
     decode_u32 = function(data)
@@ -356,9 +346,9 @@ end
 local decode_u64
 if strict_alignment then
     decode_u64 = function(data)
-        ffi.copy(tmpint, data[0], 8);
+        ffi.copy(reg, data[0], 8);
         data[0] = data[0] + 8
-        local num = bit.bswap(tmpint[0].u64)
+        local num = bit.bswap(reg.u64)
         if num <= DBL_INT_MAX then
             return tonumber(num) -- return as 'number'
         end
@@ -385,8 +375,8 @@ end
 local decode_i16
 if strict_alignment then
     decode_i16 = function(data)
-        ffi.copy(tmpint, data[0], 2)
-        local num = bswap_u16(tmpint[0].u16)
+        ffi.copy(reg, data[0], 2)
+        local num = bswap_u16(reg.u16)
         data[0] = data[0] + 2
         -- note: this double cast is actually necessary
         return tonumber(ffi.cast('int16_t', ffi.cast('uint16_t', num)))
@@ -403,8 +393,8 @@ end
 local decode_i32
 if strict_alignment then
     decode_i32 = function(data)
-        ffi.copy(tmpint, data[0], 4)
-        local num = bit.bswap(tonumber(tmpint[0].u32))
+        ffi.copy(reg, data[0], 4)
+        local num = bit.bswap(tonumber(reg.u32))
         data[0] = data[0] + 4
         return num
     end
@@ -419,9 +409,9 @@ end
 local decode_i64
 if strict_alignment then
     decode_i64 = function(data)
-        ffi.copy(tmpint, data[0], 8)
+        ffi.copy(reg, data[0], 8)
         data[0] = data[0] + 8
-        local num = bit.bswap(ffi.cast('int64_t', tmpint[0].u64))
+        local num = bit.bswap(reg.i64)
         if num >= -DBL_INT_MAX and num <= DBL_INT_MAX then
             return tonumber(num) -- return as 'number'
         end
@@ -552,7 +542,7 @@ end
 -- element. It is significally faster on LuaJIT to use double pointer than
 -- return result, newpos.
 --
-local bufp = ffi.new('const unsigned char *[1]');
+local bufp = reg.acucp;
 
 local function check_offset(offset, len)
     if offset == nil then
diff --git a/src/lua/socket.lua b/src/lua/socket.lua
index 2bb5712ebf34addfa69af7dfbdfdd25071e881e8..2dba0a8d2af557f4365d909857d50bc05af98062 100644
--- a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -10,6 +10,8 @@ local fiber = require('fiber')
 local fio = require('fio')
 local log = require('log')
 local buffer = require('buffer')
+local reg1 = buffer.reg1
+local reg2 = buffer.reg2
 local static_alloc = buffer.static_alloc
 
 local format = string.format
@@ -473,9 +475,9 @@ local function socket_setsockopt(self, level, name, value)
     end
 
     if info.type == 1 then
-        local value = ffi.new("int[1]", value)
+        reg1.ai[0] = value
         local res = ffi.C.setsockopt(fd,
-            level, info.iname, value, ffi.sizeof('int'))
+            level, info.iname, reg1.ai, ffi.sizeof('int'))
 
         if res < 0 then
             self._errno = boxerrno()
@@ -517,8 +519,10 @@ local function socket_getsockopt(self, level, name)
     self._errno = nil
 
     if info.type == 1 then
-        local value = ffi.new("int[1]", 0)
-        local len = ffi.new("size_t[1]", ffi.sizeof('int'))
+        local value = reg1.ai
+        value[0] = 0
+        local len = reg2.as
+        len[0] = ffi.sizeof('int')
         local res = ffi.C.getsockopt(fd, level, info.iname, value, len)
 
         if res < 0 then
@@ -533,8 +537,9 @@ local function socket_getsockopt(self, level, name)
     end
 
     if info.type == 2 then
-        local value = ffi.new("char[256]", { 0 })
-        local len = ffi.new("size_t[1]", 256)
+        local value = static_alloc('char', 256)
+        local len = reg1.as
+        len[0] = 256
         local res = ffi.C.getsockopt(fd, level, info.iname, value, len)
         if res < 0 then
             self._errno = boxerrno()
@@ -556,8 +561,9 @@ local function socket_linger(self, active, timeout)
     local info = internal.SO_OPT[level].SO_LINGER
     self._errno = nil
     if active == nil then
-        local value = ffi.new("linger_t[1]")
-        local len = ffi.new("size_t[1]", 2 * ffi.sizeof('int'))
+        local value = static_alloc('linger_t')
+        local len = reg1.as
+        len[0] = ffi.sizeof('linger_t')
         local res = ffi.C.getsockopt(fd, level, info.iname, value, len)
         if res < 0 then
             self._errno = boxerrno()
@@ -799,8 +805,7 @@ local function get_recv_size(self, size)
             -- them using message peek.
             local iflags = get_iflags(internal.SEND_FLAGS, {'MSG_PEEK'})
             assert(iflags ~= nil)
-            local buf = ffi.new('char[?]', 1)
-            size = tonumber(ffi.C.recv(fd, buf, 1, iflags))
+            size = tonumber(ffi.C.recv(fd, reg1.ac, 1, iflags))
             -- Prevent race condition: proceed with the case when
             -- a datagram of length > 0 has been arrived after the
             -- getsockopt call above.
diff --git a/src/lua/string.lua b/src/lua/string.lua
index bb4adfc784b6da9d28fd33bdb00b59b7a308b261..6e12c59ae782c635b76c148283c340898c10449c 100644
--- a/src/lua/string.lua
+++ b/src/lua/string.lua
@@ -15,8 +15,8 @@ ffi.cdef[[
 ]]
 
 local c_char_ptr     = ffi.typeof('const char *')
-local strip_newstart = ffi.new("unsigned long[1]")
-local strip_newlen   = ffi.new("unsigned long[1]")
+local strip_newstart = buffer.reg1.aul
+local strip_newlen   = buffer.reg2.aul
 
 local memcmp  = ffi.C.memcmp
 local memmem  = ffi.C.memmem
diff --git a/test/app/buffer.result b/test/app/buffer.result
index e0aad9bc7a42259e809a4d359ab3734242a64535..beccc5a877ad677fd3f9aa0e55db482d8557e5ca 100644
--- a/test/app/buffer.result
+++ b/test/app/buffer.result
@@ -7,6 +7,33 @@ buffer = require('buffer')
 ffi = require('ffi')
 ---
 ...
+-- Registers.
+reg1 = buffer.reg1
+---
+...
+reg1.u16 = 100
+---
+...
+u16 = ffi.new('uint16_t[1]')
+---
+...
+ffi.copy(u16, reg1, 2)
+---
+...
+u16[0]
+---
+- 100
+...
+u16[0] = 200
+---
+...
+ffi.copy(reg1, u16, 2)
+---
+...
+reg1.u16
+---
+- 200
+...
 -- Alignment.
 _ = buffer.static_alloc('char') -- This makes buffer pos unaligned.
 ---
diff --git a/test/app/buffer.test.lua b/test/app/buffer.test.lua
index ba7299f33fa4dd840cb5a0b281b1998cdc8820ac..a1c3806807a06b18c9ae0d1cca6e80706dae0576 100644
--- a/test/app/buffer.test.lua
+++ b/test/app/buffer.test.lua
@@ -2,6 +2,17 @@ test_run = require('test_run').new()
 buffer = require('buffer')
 ffi = require('ffi')
 
+-- Registers.
+reg1 = buffer.reg1
+reg1.u16 = 100
+u16 = ffi.new('uint16_t[1]')
+ffi.copy(u16, reg1, 2)
+u16[0]
+
+u16[0] = 200
+ffi.copy(reg1, u16, 2)
+reg1.u16
+
 -- Alignment.
 _ = buffer.static_alloc('char') -- This makes buffer pos unaligned.
 p = buffer.static_alloc('int')